From d08f46a2e5814074a8500a1faa583df27ba6bbc4 Mon Sep 17 00:00:00 2001 From: Derek Wan Date: Wed, 31 Dec 2025 16:32:03 +0900 Subject: [PATCH 1/5] 2025-12-31 16:32:03 (Wed) > DW-Swift > derek From 1514f0a64e593b2fbeb311812d6d8ebbf66e3b14 Mon Sep 17 00:00:00 2001 From: Derek Wan Date: Wed, 31 Dec 2025 19:19:22 +0900 Subject: [PATCH 2/5] 2025-12-31 19:19:22 (Wed) > DW-Mac > derekwan --- pyproject.toml | 4 ++-- src/utilities/__init__.py | 2 +- src/utilities/docker.py | 1 + src/utilities/subprocess.py | 38 +++++++++++++++++++++++++++++++++++++ uv.lock | 2 +- 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 225ec7e2a0..ac7416475f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,7 +101,7 @@ name = "dycw-utilities" readme = "README.md" requires-python = ">= 3.12" - version = "0.174.17" + version = "0.174.18" [project.entry-points.pytest11] pytest-randomly = "utilities.pytest_plugins.pytest_randomly" @@ -135,7 +135,7 @@ # bump-my-version [tool.bumpversion] allow_dirty = true - current_version = "0.174.17" + current_version = "0.174.18" [[tool.bumpversion.files]] filename = "src/utilities/__init__.py" diff --git a/src/utilities/__init__.py b/src/utilities/__init__.py index 88301dd685..ee4f5e6a7a 100644 --- a/src/utilities/__init__.py +++ b/src/utilities/__init__.py @@ -1,3 +1,3 @@ from __future__ import annotations -__version__ = "0.174.17" +__version__ = "0.174.18" diff --git a/src/utilities/docker.py b/src/utilities/docker.py index 549075e930..6d153138b6 100644 --- a/src/utilities/docker.py +++ b/src/utilities/docker.py @@ -269,6 +269,7 @@ def yield_docker_temp_dir( logger: LoggerLike | None = None, keep: bool = False, ) -> Iterator[Path]: + """Yield a temporary directory in a Docker container.""" path = Path( # skipif-ci docker_exec( container, diff --git a/src/utilities/subprocess.py b/src/utilities/subprocess.py index b2864aff71..51291fed7c 100644 --- a/src/utilities/subprocess.py +++ b/src/utilities/subprocess.py @@ -221,6 +221,16 @@ def expand_path( ## +def git_clone( + url: str, path: PathLike, /, *, sudo: bool = False, branch: str | None = None +) -> None: + """Clone a repository.""" + rm(path, sudo=sudo) + run(*maybe_sudo_cmd(*git_clone_cmd(url, path), sudo=sudo)) + if branch is not None: + run(*maybe_sudo_cmd(*git_hard_reset_cmd(branch=branch), sudo=sudo), cwd=path) + + def git_clone_cmd(url: str, path: PathLike, /) -> list[str]: """Command to use 'git clone' to clone a repository.""" return ["git", "clone", "--recurse-submodules", url, str(path)] @@ -1089,14 +1099,26 @@ def tee_cmd(path: PathLike, /, *, append: bool = False) -> list[str]: ## +def touch(path: PathLike, /, *, sudo: bool = False) -> None: + """Change file access and modification times.""" + run(*maybe_sudo_cmd(*touch_cmd(path), sudo=sudo)) + + def touch_cmd(path: PathLike, /) -> list[str]: + """Command to use 'touch' to change file access and modification times.""" return ["touch", str(path)] ## +def uv_run(module: str, /, *args: str) -> None: + """Run a command or script.""" + run(*uv_run_cmd(module, *args)) + + def uv_run_cmd(module: str, /, *args: str) -> list[str]: + """Command to use 'uv' to run a command or script.""" return [ "uv", "run", @@ -1114,6 +1136,17 @@ def uv_run_cmd(module: str, /, *args: str) -> list[str]: ## +@contextmanager +def yield_git_repo(url: str, /, *, branch: str | None = None) -> Iterator[Path]: + """Yield a temporary git repository.""" + with TemporaryDirectory() as temp_dir: + git_clone(url, temp_dir, branch=branch) + yield temp_dir + + +## + + @contextmanager def yield_ssh_temp_dir( user: str, @@ -1124,6 +1157,7 @@ def yield_ssh_temp_dir( logger: LoggerLike | None = None, keep: bool = False, ) -> Iterator[Path]: + """Yield a temporary directory on a remote machine.""" path = Path( # skipif-ci ssh(user, hostname, *MKTEMP_DIR_CMD, return_=True, retry=retry, logger=logger) ) @@ -1160,6 +1194,7 @@ def yield_ssh_temp_dir( "cp_cmd", "echo_cmd", "expand_path", + "git_clone", "git_clone_cmd", "git_hard_reset_cmd", "maybe_parent", @@ -1183,7 +1218,10 @@ def yield_ssh_temp_dir( "symlink", "symlink_cmd", "tee_cmd", + "touch", "touch_cmd", + "uv_run", "uv_run_cmd", + "yield_git_repo", "yield_ssh_temp_dir", ] diff --git a/uv.lock b/uv.lock index 87f8606f03..c0f758a86d 100644 --- a/uv.lock +++ b/uv.lock @@ -634,7 +634,7 @@ wheels = [ [[package]] name = "dycw-utilities" -version = "0.174.17" +version = "0.174.18" source = { editable = "." } dependencies = [ { name = "atomicwrites" }, From 8c2773174d0723b52fcf097b03e5949cf0adbea8 Mon Sep 17 00:00:00 2001 From: Derek Wan Date: Wed, 31 Dec 2025 19:22:09 +0900 Subject: [PATCH 3/5] 2025-12-31 19:22:09 (Wed) > DW-Mac > derekwan --- src/tests/test_subprocess.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/tests/test_subprocess.py b/src/tests/test_subprocess.py index 1b52890194..e804470d10 100644 --- a/src/tests/test_subprocess.py +++ b/src/tests/test_subprocess.py @@ -33,6 +33,7 @@ cp_cmd, echo_cmd, expand_path, + git_clone, git_clone_cmd, git_hard_reset_cmd, maybe_parent, @@ -60,6 +61,7 @@ tee_cmd, touch_cmd, uv_run_cmd, + yield_git_repo, yield_ssh_temp_dir, ) from utilities.tempfile import TemporaryDirectory, TemporaryFile @@ -233,14 +235,21 @@ def test_subs(self) -> None: assert result == expected +class TestGitClone: + @throttle(delta=5 * MINUTE) + def test_main(self, *, tmp_path: Path) -> None: + git_clone("https://github.com/dycw/template-generic", tmp_path) + assert (tmp_path / "pyproject.toml").is_file() + + class TestGitCloneCmd: def test_main(self) -> None: - result = git_clone_cmd("https://github.com/foo/bar", "path") + result = git_clone_cmd("https://github.com/dycw/template-generic", "path") expected = [ "git", "clone", "--recurse-submodules", - "https://github.com/foo/bar", + "https://github.com/dycw/template-generic", "path", ] assert result == expected @@ -1187,6 +1196,13 @@ def test_args(self) -> None: assert result == expected +class TestYieldGitRepo: + @throttle(delta=5 * MINUTE) + def test_main(self) -> None: + with yield_git_repo("https://github.com/dycw/template-generic") as temp: + assert (temp / "pyproject.toml").is_file() + + class TestYieldSSHTempDir: @skipif_ci @throttle(delta=5 * MINUTE) From 3c28acc3883164d1d1f6fd9b371ac84363219f69 Mon Sep 17 00:00:00 2001 From: Derek Wan Date: Wed, 31 Dec 2025 19:23:02 +0900 Subject: [PATCH 4/5] 2025-12-31 19:23:02 (Wed) > DW-Mac > derekwan --- src/utilities/subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utilities/subprocess.py b/src/utilities/subprocess.py index 51291fed7c..91538051f1 100644 --- a/src/utilities/subprocess.py +++ b/src/utilities/subprocess.py @@ -1114,7 +1114,7 @@ def touch_cmd(path: PathLike, /) -> list[str]: def uv_run(module: str, /, *args: str) -> None: """Run a command or script.""" - run(*uv_run_cmd(module, *args)) + run(*uv_run_cmd(module, *args)) # pragma: no cover def uv_run_cmd(module: str, /, *args: str) -> list[str]: From 45c6defef5c1912c528ea8f58b9f1a66dab86fd7 Mon Sep 17 00:00:00 2001 From: Derek Wan Date: Wed, 31 Dec 2025 19:28:21 +0900 Subject: [PATCH 5/5] 2025-12-31 19:28:21 (Wed) > DW-Mac > derekwan --- src/tests/test_subprocess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/test_subprocess.py b/src/tests/test_subprocess.py index e804470d10..a9db25c059 100644 --- a/src/tests/test_subprocess.py +++ b/src/tests/test_subprocess.py @@ -239,7 +239,7 @@ class TestGitClone: @throttle(delta=5 * MINUTE) def test_main(self, *, tmp_path: Path) -> None: git_clone("https://github.com/dycw/template-generic", tmp_path) - assert (tmp_path / "pyproject.toml").is_file() + assert (tmp_path / ".bumpversion.toml").is_file() class TestGitCloneCmd: @@ -1200,7 +1200,7 @@ class TestYieldGitRepo: @throttle(delta=5 * MINUTE) def test_main(self) -> None: with yield_git_repo("https://github.com/dycw/template-generic") as temp: - assert (temp / "pyproject.toml").is_file() + assert (temp / ".bumpversion.toml").is_file() class TestYieldSSHTempDir: