Skip to content

Commit

Permalink
Merge pull request #1525 from rmartin16/cookiecutter-origin-url
Browse files Browse the repository at this point in the history
Update the origin remote URL for a cached template before using it
  • Loading branch information
freakboy3742 authored Nov 6, 2023
2 parents 812361b + afad331 commit 696ced3
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 8 deletions.
1 change: 1 addition & 0 deletions changes/1158.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When a custom Briefcase template from a git repository is used to create an app, Briefcase now ensures that git repository is always used.
4 changes: 4 additions & 0 deletions src/briefcase/commands/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,10 @@ def update_cookiecutter_cache(self, template: str, branch="master"):
repo = self.tools.git.Repo(cached_template)
# Raises ValueError if "origin" isn't a valid remote
remote = repo.remote(name="origin")
# Ensure the existing repo's origin URL points to the location
# being requested. A difference can occur, for instance, if a
# fork of the template is used.
remote.set_url(new_url=template, old_url=remote.url)
try:
# Attempt to update the repository
remote.fetch()
Expand Down
79 changes: 71 additions & 8 deletions tests/commands/base/test_update_cookiecutter_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def test_explicit_cached_repo_template(base_command, mock_git):
base_command.tools.git.Repo.return_value = mock_repo
mock_repo.remote.return_value = mock_remote
mock_remote.refs.__getitem__.return_value = mock_remote_head
mock_remote.url = "https://example.com/magic/special-template.git"

cached_path = cookiecutter_cache_path(
"https://example.com/magic/special-template.git"
Expand All @@ -98,8 +99,59 @@ def test_explicit_cached_repo_template(base_command, mock_git):
# The cookiecutter cache location will be interrogated.
base_command.tools.git.Repo.assert_called_once_with(cached_path)

# The origin of the repo was fetched
# The origin of the repo was updated and fetched
mock_repo.remote.assert_called_once_with(name="origin")
mock_remote.set_url.assert_called_once_with(
new_url="https://example.com/magic/special-template.git",
old_url="https://example.com/magic/special-template.git",
)
mock_remote.fetch.assert_called_once_with()

# The right branch was accessed
mock_remote.refs.__getitem__.assert_called_once_with("special")

# The remote head was checked out.
mock_remote_head.checkout.assert_called_once_with()

# The template that will be used is the original URL
assert cached_template == cached_path


def test_explicit_cached_repo_template_with_diff_url(base_command, mock_git):
"""If a previously known URL template is specified but uses a different remote URL,
the repo's origin URL is updated and is used."""
base_command.tools.git = mock_git

mock_repo = mock.MagicMock()
mock_remote = mock.MagicMock()
mock_remote_head = mock.MagicMock()

# Git returns a Repo, that repo can return a remote, and it has
# heads that can be accessed.
base_command.tools.git.Repo.return_value = mock_repo
mock_repo.remote.return_value = mock_remote
mock_remote.refs.__getitem__.return_value = mock_remote_head
mock_remote.url = "https://example.com/existing/special-template.git"

cached_path = cookiecutter_cache_path(
"https://example.com/magic/special-template.git"
)

# Update the cache
cached_template = base_command.update_cookiecutter_cache(
template="https://example.com/magic/special-template.git",
branch="special",
)

# The cookiecutter cache location will be interrogated.
base_command.tools.git.Repo.assert_called_once_with(cached_path)

# The origin of the repo was updated and fetched
mock_repo.remote.assert_called_once_with(name="origin")
mock_remote.set_url.assert_called_once_with(
new_url="https://example.com/magic/special-template.git",
old_url="https://example.com/existing/special-template.git",
)
mock_remote.fetch.assert_called_once_with()

# The right branch was accessed
Expand All @@ -126,6 +178,7 @@ def test_offline_repo_template(base_command, mock_git):
# will cause a git error (error code 128).
base_command.tools.git.Repo.return_value = mock_repo
mock_repo.remote.return_value = mock_remote
mock_remote.url = "https://example.com/magic/special-template.git"
mock_remote.refs.__getitem__.return_value = mock_remote_head
mock_remote.fetch.side_effect = git_exceptions.GitCommandError("git", 128)

Expand All @@ -141,8 +194,12 @@ def test_offline_repo_template(base_command, mock_git):
# The cookiecutter cache location will be interrogated.
base_command.tools.git.Repo.assert_called_once_with(cached_path)

# The origin of the repo was fetched
# The origin of the repo was updated and fetched
mock_repo.remote.assert_called_once_with(name="origin")
mock_remote.set_url.assert_called_once_with(
new_url="https://example.com/magic/special-template.git",
old_url="https://example.com/magic/special-template.git",
)
mock_remote.fetch.assert_called_once_with()

# The right branch was accessed
Expand All @@ -167,6 +224,7 @@ def test_cached_missing_branch_template(base_command, mock_git):
# raises an IndexError
base_command.tools.git.Repo.return_value = mock_repo
mock_repo.remote.return_value = mock_remote
mock_remote.url = "https://example.com/magic/special-template.git"
mock_remote.refs.__getitem__.side_effect = IndexError

cached_path = cookiecutter_cache_path(
Expand All @@ -183,23 +241,27 @@ def test_cached_missing_branch_template(base_command, mock_git):
# The cookiecutter cache location will be interrogated.
base_command.tools.git.Repo.assert_called_once_with(cached_path)

# The origin of the repo was fetched
# The origin of the repo was updated and fetched
mock_repo.remote.assert_called_once_with(name="origin")
mock_remote.set_url.assert_called_once_with(
new_url="https://example.com/magic/special-template.git",
old_url="https://example.com/magic/special-template.git",
)
mock_remote.fetch.assert_called_once_with()

# An attempt to access the branch was made
mock_remote.refs.__getitem__.assert_called_once_with("invalid")


def test_value_error(base_command, mock_git):
"""If the git clone fails a ValueError is raised."""
def test_git_repo_with_missing_origin_remote(base_command, mock_git):
"""If the local git repo doesn't have an origin remote, a ValueError is raised."""
base_command.tools.git = mock_git

mock_repo = mock.MagicMock()
mock_remote = mock.MagicMock()

# Git returns a Repo, that repo can return a remote, and it has
# heads that can be accessed. However, getting the remote will fail if git clone is not complete.
# Git returns a Repo, that repo can return a remote, and it has heads that can be
# accessed. However, getting the remote will fail if git clone is not complete.
base_command.tools.git.Repo.return_value = mock_repo
mock_repo.remote.side_effect = ValueError("Remote named origin did not exist")

Expand All @@ -216,6 +278,7 @@ def test_value_error(base_command, mock_git):
# The cookiecutter cache location will be interrogated.
base_command.tools.git.Repo.assert_called_once_with(cached_path)

# The origin of the repo was fetched
# The origin of the repo was not updated or fetched
mock_repo.remote.assert_called_once_with(name="origin")
mock_remote.set_url.assert_not_called()
mock_remote.fetch.assert_not_called()

0 comments on commit 696ced3

Please sign in to comment.