def test_must_fail_when_repo_not_found(self): location = str(Path("my", "folder")) with patch.object(repository, "unzip") as unzip_mock: unzip_mock.side_effect = RepositoryNotFound("repo") with self.assertRaises(ArbitraryProjectDownloadFailed): generate_non_cookiecutter_project(location, self.output_dir)
def determine_repo_dir(template, abbreviations, clone_to_dir, checkout, no_input, password=None): """ Locate the repository directory from a template reference. Applies repository abbreviations to the template reference. If the template refers to a repository URL, clone it. If the template is a path to a local repository, use it. :param template: A directory containing a project template directory, or a URL to a git repository. :param abbreviations: A dictionary of repository abbreviation definitions. :param clone_to_dir: The directory to clone the repository into. :param checkout: The branch, tag or commit ID to checkout after clone. :param no_input: Prompt the user at command line for manual configuration? :param password: The password to use when extracting the repository. :return: A tuple containing the cookiecutter template directory, and a boolean descriving whether that directory should be cleaned up after the template has been instantiated. :raises: `RepositoryNotFound` if a repository directory could not be found. """ template = expand_abbreviations(template, abbreviations) if is_zip_file(template): unzipped_dir = unzip(zip_uri=template, is_url=is_repo_url(template), clone_to_dir=clone_to_dir, no_input=no_input, password=password) repository_candidates = [unzipped_dir] cleanup = True elif is_repo_url(template): cloned_repo = clone( repo_url=template, checkout=checkout, clone_to_dir=clone_to_dir, no_input=no_input, ) repository_candidates = [cloned_repo] cleanup = False else: repository_candidates = [ template, os.path.join(clone_to_dir, template) ] cleanup = False for repo_candidate in repository_candidates: if repository_has_cookiecutter_config(repo_candidate): return repo_candidate, cleanup raise RepositoryNotFound( 'A valid repository for "{}" could not be found in the following ' 'locations:\n{}'.format(template, '\n'.join(repository_candidates)))
def test_init_arbitrary_project_with_location_is_not_cookiecutter( self, generate_non_cookiecutter_project_mock, cookiecutter_mock): cookiecutter_mock.side_effect = RepositoryNotFound("msg") generate_project(location=self.location, output_dir=self.output_dir) generate_non_cookiecutter_project_mock.assert_called_with( location=self.location, output_dir=self.output_dir)
def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): """ Clone a repo to the current directory. :param repo_url: Repo URL of unknown type. :param checkout: The branch, tag or commit ID to checkout after clone. :param clone_to_dir: The directory to clone to. Defaults to the current directory. :param no_input: Suppress all user prompts when calling via API. :returns: str with path to the new directory of the repository. """ # Ensure that clone_to_dir exists clone_to_dir = os.path.expanduser(clone_to_dir) make_sure_path_exists(clone_to_dir) # identify the repo_type vcs, repo_url = identify_repo(repo_url) # check that the appropriate VCS for the repo_type is installed if not vcs.is_installed(): msg = "'{0}' is not installed.".format(vcs.cmd) raise VCSNotInstalled(msg) repo_url = repo_url.rstrip('/') repo_name = os.path.split(repo_url)[1] repo_dir = vcs.get_repo_dir(repo_name, clone_to_dir) logger.debug('repo_dir is {0}'.format(repo_dir)) if os.path.isdir(repo_dir): clone = prompt_and_delete(repo_dir, no_input=no_input) else: clone = True if clone: try: vcs.clone(repo_url, checkout, clone_to_dir, repo_dir) except subprocess.CalledProcessError as clone_error: output = clone_error.output.decode('utf-8') # In case of error, print VCS output click.echo( 'Cloning of {} repository {} returned an error:\n{}'.format( vcs.cmd, repo_url, output)) if any(error in output.lower() for error in vcs.not_found_errors): raise RepositoryNotFound( 'The repository {} could not be found, ' 'have you made a typo?'.format(repo_url)) if any(error in output.lower() for error in vcs.branch_errors): raise RepositoryCloneFailed( 'The {} branch of repository {} could not found, ' 'have you made a typo?'.format(checkout, repo_url)) # Raise base subprocess error if SVN error can't be identified raise return repo_dir
def test_init_arbitrary_project_with_named_folder(self, generate_non_cookiecutter_project_mock, cookiecutter_mock): cookiecutter_mock.side_effect = RepositoryNotFound("msg") generate_project(location=self.location, output_dir=self.output_dir, name=self.name) expected_output_dir = str(Path(self.output_dir, self.name)) generate_non_cookiecutter_project_mock.assert_called_with( location=self.location, output_dir=expected_output_dir )
def test_generate_project_cookiecutter_exceptions( self, mock_cookiecutter, mock_generate_non_cookiecutter_project): t = Template(location=self._ANY_LOCATION) with self.assertRaises(InvalidLocationError): mock_cookiecutter.side_effect = UnknownRepoType() t.generate_project({}) mock_cookiecutter.reset_mock() with self.assertRaises(GenerateProjectFailedError): mock_cookiecutter.side_effect = Exception("something went wrong") t.generate_project({}) mock_cookiecutter.reset_mock() # if the provided template is not a cookiecutter template, we generate a non cookiecutter template mock_cookiecutter.side_effect = RepositoryNotFound() t.generate_project({}) mock_generate_non_cookiecutter_project.assert_called_once()
def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): """Clone a repo to the current directory. :param repo_url: Repo URL of unknown type. :param checkout: The branch, tag or commit ID to checkout after clone. :param clone_to_dir: The directory to clone to. Defaults to the current directory. :param no_input: Suppress all user prompts when calling via API. :returns: str with path to the new directory of the repository. """ # Ensure that clone_to_dir exists clone_to_dir = os.path.expanduser(clone_to_dir) make_sure_path_exists(clone_to_dir) # identify the repo_type repo_type, repo_url = identify_repo(repo_url) # check that the appropriate VCS for the repo_type is installed if not is_vcs_installed(repo_type): msg = "'{0}' is not installed.".format(repo_type) raise VCSNotInstalled(msg) repo_url = repo_url.rstrip('/') repo_name = os.path.split(repo_url)[1] if repo_type == 'git': repo_name = repo_name.split(':')[-1].rsplit('.git')[0] repo_dir = os.path.normpath(os.path.join(clone_to_dir, repo_name)) elif repo_type == 'hg': repo_dir = os.path.normpath(os.path.join(clone_to_dir, repo_name)) logger.debug('repo_dir is {0}'.format(repo_dir)) if os.path.isdir(repo_dir): clone = prompt_and_delete(repo_dir, no_input=no_input) else: clone = True if clone: try: subprocess.check_output( # nosec [repo_type, 'clone', repo_url], cwd=clone_to_dir, stderr=subprocess.STDOUT, ) if checkout is not None: subprocess.check_output( # nosec [repo_type, 'checkout', checkout], cwd=repo_dir, stderr=subprocess.STDOUT, ) except subprocess.CalledProcessError as clone_error: output = clone_error.output.decode('utf-8') if 'not found' in output.lower(): raise RepositoryNotFound( 'The repository {} could not be found, ' 'have you made a typo?'.format(repo_url)) if any(error in output for error in BRANCH_ERRORS): raise RepositoryCloneFailed( 'The {} branch of repository {} could not found, ' 'have you made a typo?'.format(checkout, repo_url)) logger.error('git clone failed with error: %s', output) raise return repo_dir
def clone(repo_url, checkout=None, clone_to_dir=".", no_input=False): """Clone a repo to the current directory. :param repo_url: Repo URL of unknown type. :param checkout: The branch, tag or commit ID to checkout after clone. :param clone_to_dir: The directory to clone to. Defaults to the current directory. :param no_input: Suppress all user prompts when calling via API. """ # Ensure that clone_to_dir exists clone_to_dir = os.path.expanduser(clone_to_dir) make_sure_path_exists(clone_to_dir) # identify the repo_type repo_type, repo_url = identify_repo(repo_url) # check that the appropriate VCS for the repo_type is installed if not is_vcs_installed(repo_type): msg = "'{0}' is not installed.".format(repo_type) raise VCSNotInstalled(msg) repo_url = repo_url.rstrip("/") tail = os.path.split(repo_url)[1] if repo_type == "git": repo_dir = os.path.normpath( os.path.join(clone_to_dir, tail.rsplit(".git")[0])) elif repo_type == "hg": repo_dir = os.path.normpath(os.path.join(clone_to_dir, tail)) logger.debug("repo_dir is {0}".format(repo_dir)) if os.path.isdir(repo_dir): clone = prompt_and_delete(repo_dir, no_input=no_input) else: clone = True if clone: try: subprocess.check_output( [repo_type, "clone", repo_url], cwd=clone_to_dir, stderr=subprocess.STDOUT, ) if checkout is not None: subprocess.check_output( [repo_type, "checkout", checkout], cwd=repo_dir, stderr=subprocess.STDOUT, ) except subprocess.CalledProcessError as clone_error: output = clone_error.output.decode("utf-8") if "not found" in output.lower(): raise RepositoryNotFound( "The repository {} could not be found, " "have you made a typo?".format(repo_url)) if any(error in output for error in BRANCH_ERRORS): raise RepositoryCloneFailed( "The {} branch of repository {} could not found, " "have you made a typo?".format(checkout, repo_url)) raise return repo_dir