From b678499183db2d16f0836b7a16bd88c4bef475e1 Mon Sep 17 00:00:00 2001 From: saschabuehrle Date: Fri, 20 Mar 2026 13:11:02 +0100 Subject: [PATCH] fix: fall back to HEAD when remote tag lookup fails --- copier/_vcs.py | 12 +++++++++++- tests/test_vcs.py | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/copier/_vcs.py b/copier/_vcs.py index 3b2ebf7a8..868a828c1 100644 --- a/copier/_vcs.py +++ b/copier/_vcs.py @@ -140,9 +140,19 @@ def get_latest_tag(url: str, use_prereleases: OptBool = False) -> str: The latest git tag, or `HEAD` if no valid tags are found. """ git = get_git() + try: + tags_output = git("ls-remote", "--tags", "--refs", url) + except ProcessExecutionError: + print( + colors.warn + | "Could not list remote tags from template; using HEAD as ref", + file=sys.stderr, + ) + return "HEAD" + all_tags = ( tag.split("\t", 1)[1].removeprefix("refs/tags/") - for tag in git("ls-remote", "--tags", "--refs", url).splitlines() + for tag in tags_output.splitlines() ) all_tags = (tag for tag in all_tags if valid_version(tag)) if not use_prereleases: diff --git a/tests/test_vcs.py b/tests/test_vcs.py index c6c8668b7..9e6d476b9 100644 --- a/tests/test_vcs.py +++ b/tests/test_vcs.py @@ -12,7 +12,13 @@ from copier import run_copy, run_update from copier._main import Worker from copier._user_data import load_answersfile_data -from copier._vcs import clone, get_git_version, get_latest_tag, get_repo +from copier._vcs import ( + ProcessExecutionError, + clone, + get_git_version, + get_latest_tag, + get_repo, +) from copier.errors import DirtyLocalWarning, ShallowCloneWarning from .helpers import build_file_tree, git, git_save @@ -213,6 +219,15 @@ def test_invalid_version(tmp_path: Path) -> None: assert get_latest_tag(str(tmp_path)) == "v2" +def test_get_latest_tag_falls_back_to_head_on_ls_remote_error() -> None: + mock_git = mock.MagicMock( + side_effect=ProcessExecutionError(["git", "ls-remote"], 128, "", "auth") + ) + + with mock.patch("copier._vcs.get_git", return_value=mock_git): + assert get_latest_tag("https://x-access-token:token@github.com/org/private.git") == "HEAD" + + @pytest.mark.parametrize("sorter", [iter, reversed]) def test_select_latest_version_tag( tmp_path_factory: pytest.TempPathFactory,