copier/tests/test_vcs.py
2025-04-22 12:20:23 +02:00

212 lines
7.0 KiB
Python

import os
import shutil
from collections.abc import Iterator, Sequence
from pathlib import Path
from typing import Callable
import pytest
from packaging.version import Version
from plumbum import local
from copier import Worker, run_copy, run_update
from copier.errors import ShallowCloneWarning
from copier.user_data import load_answersfile_data
from copier.vcs import checkout_latest_tag, clone, get_git_version, get_repo
from .helpers import git
def test_get_repo() -> None:
get = get_repo
assert get("git@git.myproject.org:MyProject") == "git@git.myproject.org:MyProject"
assert (
get("git://git.myproject.org/MyProject") == "git://git.myproject.org/MyProject"
)
assert (
get("https://github.com/jpscaletti/copier.git")
== "https://github.com/jpscaletti/copier.git"
)
assert (
get("https://github.com/jpscaletti/copier")
== "https://github.com/jpscaletti/copier.git"
)
assert (
get("https://gitlab.com/gitlab-org/gitlab")
== "https://gitlab.com/gitlab-org/gitlab.git"
)
assert (
get("gh:/jpscaletti/copier.git") == "https://github.com/jpscaletti/copier.git"
)
assert get("gh:jpscaletti/copier.git") == "https://github.com/jpscaletti/copier.git"
assert get("gl:jpscaletti/copier.git") == "https://gitlab.com/jpscaletti/copier.git"
assert get("gh:jpscaletti/copier") == "https://github.com/jpscaletti/copier.git"
assert get("gl:jpscaletti/copier") == "https://gitlab.com/jpscaletti/copier.git"
assert (
get("git+https://git.myproject.org/MyProject")
== "https://git.myproject.org/MyProject"
)
assert (
get("git+ssh://git.myproject.org/MyProject")
== "ssh://git.myproject.org/MyProject"
)
assert get("git://git.myproject.org/MyProject.git@master")
assert get("git://git.myproject.org/MyProject.git@v1.0")
assert get("git://git.myproject.org/MyProject.git@da39a3ee5e6b4b0d3255bfef956018")
assert get("http://google.com") is None
assert get("git.myproject.org/MyProject") is None
assert get("https://google.com") is None
assert (
get("tests/demo_updatediff_repo.bundle") == "tests/demo_updatediff_repo.bundle"
)
@pytest.mark.impure
def test_clone() -> None:
tmp = clone("https://github.com/copier-org/copier.git")
assert tmp
assert Path(tmp, "README.md").exists()
shutil.rmtree(tmp, ignore_errors=True)
@pytest.mark.impure
def test_local_clone() -> None:
tmp = clone("https://github.com/copier-org/copier.git")
assert tmp
assert Path(tmp, "README.md").exists()
local_tmp = clone(tmp)
assert local_tmp
assert Path(local_tmp, "README.md").exists()
shutil.rmtree(local_tmp, ignore_errors=True)
@pytest.mark.impure
def test_shallow_clone(tmp_path: Path, recwarn: pytest.WarningsRecorder) -> None:
# This test should always work but should be much slower if `is_git_shallow_repo()` is not
# checked in `clone()`.
src_path = str(tmp_path / "autopretty")
git("clone", "--depth=2", "https://github.com/copier-org/autopretty.git", src_path)
assert Path(src_path, "README.md").exists()
if get_git_version() >= Version("2.27"):
with pytest.warns(ShallowCloneWarning):
local_tmp = clone(str(src_path))
else:
assert len(recwarn) == 0
local_tmp = clone(str(src_path))
assert len(recwarn) == 0
assert local_tmp
assert Path(local_tmp, "README.md").exists()
shutil.rmtree(local_tmp, ignore_errors=True)
@pytest.mark.impure
def test_removes_temporary_clone(tmp_path: Path) -> None:
src_path = "https://github.com/copier-org/autopretty.git"
with Worker(
src_path=src_path, dst_path=tmp_path, defaults=True, unsafe=True
) as worker:
worker.run_copy()
temp_clone = worker.template.local_abspath
assert not temp_clone.exists()
@pytest.mark.impure
def test_dont_remove_local_clone(tmp_path: Path) -> None:
src_path = str(tmp_path / "autopretty")
git("clone", "https://github.com/copier-org/autopretty.git", src_path)
with Worker(
src_path=src_path, dst_path=tmp_path, defaults=True, unsafe=True
) as worker:
worker.run_copy()
assert Path(src_path).exists()
@pytest.mark.impure
def test_update_using_local_source_path_with_tilde(tmp_path: Path) -> None:
# first, get a local repository clone
src_path = str(tmp_path / "autopretty")
git("clone", "https://github.com/copier-org/autopretty.git", src_path)
# then prepare the user path to this clone (starting with ~)
if os.name == "nt":
# in GitHub CI, the user in the temporary path is not the same as the current user:
# ["C:\\", "Users", "RUNNER~X"] vs. runneradmin
user = Path(src_path).parts[2]
user_src_path = str(Path("~", "..", user, *Path(src_path).parts[3:]))
else:
# temporary path is in /tmp, so climb back up from ~ using ../
user_src_path = f"~/{'/'.join(['..'] * len(Path.home().parts))}{src_path}"
# generate project and assert correct path in answers
worker = run_copy(
src_path=user_src_path, dst_path=tmp_path, defaults=True, unsafe=True
)
assert worker.answers.combined["_src_path"] == user_src_path
# assert project update works and correct path again
with local.cwd(tmp_path):
git("init")
git("add", "-A")
git("commit", "-m", "init")
worker = run_update(
dst_path=tmp_path,
defaults=True,
overwrite=True,
answers_file=".copier-answers.autopretty.yml",
unsafe=True,
)
assert worker.answers.combined["_src_path"] == user_src_path
def test_invalid_version(tmp_path: Path) -> None:
sample = tmp_path / "sample.txt"
with local.cwd(tmp_path):
git("init")
sample.write_text("1")
git("add", sample)
git("commit", "-m1")
git("tag", "not-a-version")
sample.write_text("2")
git("commit", "-am2")
git("tag", "v2")
sample.write_text("3")
git("commit", "-am3")
assert git("describe", "--tags").strip() != "v2"
checkout_latest_tag(tmp_path)
assert git("describe", "--tags").strip() == "v2"
@pytest.mark.parametrize("sorter", [iter, reversed])
def test_select_latest_version_tag(
tmp_path_factory: pytest.TempPathFactory,
sorter: Callable[[Sequence[str]], Iterator[str]],
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
filename = "version.txt"
with local.cwd(src):
git("init")
Path("{{ _copier_conf.answers_file }}.jinja").write_text(
"{{ _copier_answers|to_nice_yaml }}"
)
for version in sorter(["v1", "v1.0", "v1.0.0", "v1.0.1"]):
Path(filename).write_text(version)
git("add", ".")
git("commit", "-m", version)
git("tag", version)
run_copy(str(src), dst)
assert (dst / filename).is_file()
assert (dst / filename).read_text() == "v1.0.1"
answers = load_answersfile_data(dst)
assert answers["_commit"] == "v1.0.1"