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)
Example #2
0
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)))
Example #3
0
    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)
Example #4
0
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
Example #5
0
    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
        )
Example #6
0
 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()
Example #7
0
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
Example #8
0
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