From 0c2e08c9cbba8faf5071d5f0bd2752f7efef7060 Mon Sep 17 00:00:00 2001 From: Leynos Date: Mon, 22 Sep 2025 00:57:54 +0100 Subject: [PATCH] Address review comments --- .../scripts/validate_toml_versions.py | 4 +- .../tests/test_action_python_version.py | 2 +- .../tests/test_check_github_release.py | 22 ++++++++++ .../tests/test_validate_toml_versions.py | 41 ++++++++++++++++++ .../tests/test_target_install.py | 43 +++++++++++++++++++ .github/workflows/ci.yml | 7 ++- conftest.py | 10 +++++ 7 files changed, 125 insertions(+), 4 deletions(-) diff --git a/.github/actions/release-to-pypi-uv/scripts/validate_toml_versions.py b/.github/actions/release-to-pypi-uv/scripts/validate_toml_versions.py index ddb8aabe..90b81cdb 100644 --- a/.github/actions/release-to-pypi-uv/scripts/validate_toml_versions.py +++ b/.github/actions/release-to-pypi-uv/scripts/validate_toml_versions.py @@ -35,7 +35,9 @@ def _iter_files(pattern: str) -> typ.Iterable[Path]: root = Path() - for path in root.glob(pattern): + for path in sorted( + root.glob(pattern), key=lambda candidate: tuple(candidate.parts) + ): if not path.is_file(): continue parts = set(path.parts) diff --git a/.github/actions/release-to-pypi-uv/tests/test_action_python_version.py b/.github/actions/release-to-pypi-uv/tests/test_action_python_version.py index af6cfc2d..de3d79e4 100644 --- a/.github/actions/release-to-pypi-uv/tests/test_action_python_version.py +++ b/.github/actions/release-to-pypi-uv/tests/test_action_python_version.py @@ -34,4 +34,4 @@ def test_install_step_uses_python_version_input() -> None: data = _load_action() steps = data["runs"]["steps"] install_step = next(step for step in steps if step["name"] == "Install Python") - assert install_step["run"] == 'uv python install "${{ inputs.python-version }}"' + assert 'uv python install "${{ inputs.python-version }}"' in install_step["run"] diff --git a/.github/actions/release-to-pypi-uv/tests/test_check_github_release.py b/.github/actions/release-to-pypi-uv/tests/test_check_github_release.py index 1ce150dc..ead65c14 100644 --- a/.github/actions/release-to-pypi-uv/tests/test_check_github_release.py +++ b/.github/actions/release-to-pypi-uv/tests/test_check_github_release.py @@ -189,3 +189,25 @@ def fake_urlopen(request: typ.Any, timeout: float = 30) -> DummyResponse: # noq assert len(attempts) == 3 captured = capsys.readouterr() assert "GitHub Release 'ok' is published." in captured.out + + +def test_retries_then_fail( + monkeypatch: pytest.MonkeyPatch, + module: ModuleType, + capsys: pytest.CaptureFixture[str], + fake_token: str, +) -> None: + """Abort after exhausting retries when transient errors persist.""" + + def failing_urlopen(request: typ.Any, timeout: float = 30) -> typ.Any: # noqa: ANN401 + _ = request, timeout + raise module.urllib.error.URLError("temporary") + + monkeypatch.setattr(module.urllib.request, "urlopen", failing_urlopen) + monkeypatch.setattr(module.time, "sleep", lambda _: None) + + with pytest.raises(module.typer.Exit): + module.main(tag="v1.0.0", token=fake_token, repo="owner/repo") + + captured = capsys.readouterr() + assert "temporary" in captured.err or "fetch" in captured.err diff --git a/.github/actions/release-to-pypi-uv/tests/test_validate_toml_versions.py b/.github/actions/release-to-pypi-uv/tests/test_validate_toml_versions.py index 20ea9869..f5a781b2 100644 --- a/.github/actions/release-to-pypi-uv/tests/test_validate_toml_versions.py +++ b/.github/actions/release-to-pypi-uv/tests/test_validate_toml_versions.py @@ -256,6 +256,47 @@ def test_multiple_toml_files_mixed_validity( assert "!= tag version" in captured.err +def test_iter_files_discovers_paths_in_deterministic_order( + project_root: Path, + module: ModuleType, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Ensure TOML discovery yields paths in a stable, sorted order.""" + _write_pyproject( + project_root / "pkg_b", + """ +[project] +name = "pkg-b" +version = "1.0.0" +""", + ) + _write_pyproject( + project_root / "pkg_a", + """ +[project] +name = "pkg-a" +version = "1.0.0" +""", + ) + + first = project_root / "pkg_a" / "pyproject.toml" + second = project_root / "pkg_b" / "pyproject.toml" + + def fake_glob( + self: Path, + pattern: str, + ) -> typ.Iterator[Path]: + _ = self + assert pattern == "**/pyproject.toml" + return iter((second, first)) + + monkeypatch.setattr(module.Path, "glob", fake_glob, raising=False) + + discovered = list(module._iter_files("**/pyproject.toml")) + + assert discovered == [first, second] + + @pytest.mark.parametrize("value", ["true", "TRUE", "Yes", "1", "on"]) def test_parse_bool_truthy_values(module: ModuleType, value: str) -> None: """Treat recognised truthy values as ``True`` for configuration flags.""" diff --git a/.github/actions/rust-build-release/tests/test_target_install.py b/.github/actions/rust-build-release/tests/test_target_install.py index e73f494c..89967ef1 100644 --- a/.github/actions/rust-build-release/tests/test_target_install.py +++ b/.github/actions/rust-build-release/tests/test_target_install.py @@ -11,6 +11,7 @@ CMD_MOX_UNSUPPORTED, _register_cross_version_stub, _register_docker_info_stub, + _register_podman_info_stub, _register_rustup_toolchain_stub, ) @@ -144,6 +145,48 @@ def fake_which(name: str) -> str | None: assert build_cmd[1] == f"+{default_toolchain}-x86_64-unknown-linux-gnu" +@CMD_MOX_UNSUPPORTED +def test_falls_back_to_cargo_when_podman_unusable( + main_module: ModuleType, + cross_module: ModuleType, + runtime_module: ModuleType, + module_harness: HarnessFactory, + cmd_mox: CmdMox, +) -> None: + """Fallback to cargo when podman runtime detection fails quickly (issue #97).""" + + cross_env = module_harness(cross_module) + runtime_env = module_harness(runtime_module) + app_env = module_harness(main_module) + + default_toolchain = main_module.DEFAULT_TOOLCHAIN + rustup_stdout = f"{default_toolchain}-x86_64-unknown-linux-gnu\n" + cross_path = _register_cross_version_stub(cmd_mox) + rustup_path = _register_rustup_toolchain_stub(cmd_mox, rustup_stdout) + podman_path = _register_podman_info_stub(cmd_mox, exit_code=1) + + def fake_which(name: str) -> str | None: + if name == "podman": + return podman_path + if name == "cross": + return cross_path + return rustup_path if name == "rustup" else None + + cross_env.patch_shutil_which(fake_which) + runtime_env.patch_shutil_which(fake_which) + app_env.patch_shutil_which(fake_which) + + app_env.patch_attr("ensure_cross", lambda required: (cross_path, required)) + app_env.patch_attr("runtime_available", runtime_module.runtime_available) + + cmd_mox.replay() + main_module.main("x86_64-unknown-linux-gnu", default_toolchain) + cmd_mox.verify() + + assert any(cmd[0] == "cargo" for cmd in app_env.calls) + assert all(cmd[0] != "cross" for cmd in app_env.calls) + + @pytest.mark.parametrize( "target", [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b62ac8a1..63de18cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,9 +39,12 @@ jobs: if: matrix.os == 'ubuntu-latest' # v2.0.2 uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 - - name: Install action-validator + - name: Validate GitHub Actions metadata if: matrix.os == 'ubuntu-latest' - run: bun install -g @action-validator/core @action-validator/cli + run: | + set -euxo pipefail + find . -type f \( -name 'action.yml' -o -name 'action.yaml' \) -print0 \ + | xargs -0 -n1 bunx -y @action-validator/cli validate shell: bash - name: Check formatting if: matrix.os == 'ubuntu-latest' diff --git a/conftest.py b/conftest.py index 142137f9..3ade95aa 100644 --- a/conftest.py +++ b/conftest.py @@ -124,6 +124,16 @@ def _register_docker_info_stub( return _shim_path(cmd_mox, "docker") +def _register_podman_info_stub( + cmd_mox: CmdMox, + *, + exit_code: int = 0, +) -> str: # pragma: no cover - helper + """Register a stub for ``podman info`` and return the shim path.""" + cmd_mox.stub("podman").with_args("info").returns(exit_code=exit_code) + return _shim_path(cmd_mox, "podman") + + if sys.platform != "win32": # pragma: win32 no cover - windows lacks cmd-mox pytest_plugins = ("cmd_mox.pytest_plugin",) else: