diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 314668438..9f79395fa 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -145,16 +145,23 @@ def _install_trusted_uv_url_opener() -> None: def _is_candidate_lock_name(name: str) -> bool: """Return whether a file name is a possible pip requirements lock.""" - return name == "requirements.lock" or ( - fnmatch.fnmatch(name, "requirements*.txt") - and not fnmatch.fnmatch(name, "requirements-*-ci-hashes.txt") + return ( + ( + fnmatch.fnmatch(name, "requirements*.txt") + and not fnmatch.fnmatch(name, "requirements-*-ci-hashes.*") + ) + or ( + fnmatch.fnmatch(name, "requirements*.lock") + and not fnmatch.fnmatch(name, "requirements-*-ci-hashes.*") + ) ) def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: """Return whether one safe tracked path can name a pip requirements lock. - In addition to conventional ``requirements*.txt`` names, repositories often + In addition to conventional ``requirements*.txt`` and ``requirements*.lock`` + names, repositories often keep concrete environment closures as direct children such as ``requirements/ci.txt`` or ``service/requirements/package.txt``. Only direct ``.txt`` children of a directory named ``requirements`` gain this path-based diff --git a/scripts/ci/organization_commercial_readiness_loop.py b/scripts/ci/organization_commercial_readiness_loop.py index c00cfa1e0..a4d7fa983 100644 --- a/scripts/ci/organization_commercial_readiness_loop.py +++ b/scripts/ci/organization_commercial_readiness_loop.py @@ -239,6 +239,7 @@ class GitHubClient: """Use the GitHub CLI as an authenticated, bounded REST transport.""" def __init__(self, token: str, *, timeout_seconds: int = 60) -> None: + """Initialize the client with one bounded GitHub credential.""" if not token: raise GitHubError("GH_TOKEN is required for organization coordination") self._token = token @@ -853,4 +854,4 @@ def main( if __name__ == "__main__": # pragma: no cover - exercised through main() - raise SystemExit(main()) \ No newline at end of file + raise SystemExit(main()) diff --git a/scripts/ci/repair_pr827_coderabbit_comments.py b/scripts/ci/repair_pr827_coderabbit_comments.py index 8b1df14cb..fb8501dac 100644 --- a/scripts/ci/repair_pr827_coderabbit_comments.py +++ b/scripts/ci/repair_pr827_coderabbit_comments.py @@ -266,14 +266,28 @@ def _rewrite_materialized_includes( ' encoding="utf-8",\n' ' )\n', ) -replace_once( - TEST, - ' "requirements-test.txt",\n' - ' "services/account_unification/requirements-dev.txt",\n', - ' "requirements-test.txt",\n' - ' "requirements/ci.txt",\n' - ' "services/account_unification/requirements-dev.txt",\n', -) +test_text = Path(TEST).read_text(encoding="utf-8") +if ' "requirements/ci.txt",\n' not in test_text: + if ' "services/account_unification/requirements-dev.lock",\n' in test_text: + replace_once( + TEST, + ' "requirements-test.txt",\n' + ' "services/account_unification/requirements-dev.lock",\n' + ' "services/account_unification/requirements-dev.txt",\n', + ' "requirements-test.txt",\n' + ' "requirements/ci.txt",\n' + ' "services/account_unification/requirements-dev.lock",\n' + ' "services/account_unification/requirements-dev.txt",\n', + ) + else: + replace_once( + TEST, + ' "requirements-test.txt",\n' + ' "services/account_unification/requirements-dev.txt",\n', + ' "requirements-test.txt",\n' + ' "requirements/ci.txt",\n' + ' "services/account_unification/requirements-dev.txt",\n', + ) insert_before( TEST, ' between_file.write_text("START old", encoding="utf-8")\n', diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 6073dbdc1..900ea511b 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -1,3 +1,5 @@ +"""Verify trusted base lock discovery, materialization, and exporter failures.""" + from __future__ import annotations import ast @@ -124,6 +126,10 @@ def test_materializes_hash_pinned_locks_named_beyond_the_legacy_whitelist( "fastapi==1 --hash=sha256:" + ("a" * 64) + "\n", encoding="utf-8", ) + (service / "requirements-dev.lock").write_text( + "uvicorn==1 --hash=sha256:" + ("c" * 64) + "\n", + encoding="utf-8", + ) (repo / "requirements-test.txt").write_text( "hypothesis==6 --hash=sha256:" + ("b" * 64) + "\n", encoding="utf-8", @@ -148,18 +154,23 @@ def test_materializes_hash_pinned_locks_named_beyond_the_legacy_whitelist( assert [entry["source"] for entry in manifest] == [ "requirements-test.txt", "requirements/ci.txt", + "services/account_unification/requirements-dev.lock", "services/account_unification/requirements-dev.txt", ] def test_lock_name_candidates_are_pip_requirements_files() -> None: - """Requirements files and requirements.lock are candidates; other names are not.""" + """Requirements files and lock files are candidates; other names are not.""" assert materializer._is_candidate_lock_name("requirements.lock") + assert materializer._is_candidate_lock_name("requirements-dev.lock") assert materializer._is_candidate_lock_name("requirements-dev.txt") assert materializer._is_candidate_lock_name("requirements.txt") assert not materializer._is_candidate_lock_name( "requirements-opencode-review-ci-hashes.txt" ) + assert not materializer._is_candidate_lock_name( + "requirements-opencode-review-ci-hashes.lock" + ) assert not materializer._is_candidate_lock_name("uv.lock") assert not materializer._is_candidate_lock_name("pyproject.toml") assert materializer._is_candidate_lock_path( @@ -438,6 +449,7 @@ def test_rejects_malformed_git_tree_entries( """Malformed git output cannot be interpreted as a trusted lock blob.""" def fake_git(_repo_root: Path, *_args: str) -> bytes: + """Return the malformed tree fixture for the parser under test.""" return tree_output monkeypatch.setattr(materializer, "_git", fake_git) @@ -470,6 +482,7 @@ def test_main_reports_each_materialized_lock( def fake_materialize( _repo_root: Path, _base_sha: str, _output_dir: Path ) -> list[dict[str, str]]: + """Return one deterministic manifest for the CLI reporting test.""" return [ { "file": "requirements-000.txt", @@ -533,6 +546,7 @@ def test_main_fails_with_the_materialization_reason( """A materialization exception fails closed and remains diagnosable in CI.""" def fail_materialize(_repo_root: Path, _base_sha: str, _output_dir: Path) -> None: + """Raise the fixture failure that the CLI must report.""" raise OSError("fixture failure") monkeypatch.setattr(materializer, "materialize", fail_materialize) @@ -592,6 +606,7 @@ def test_skips_non_blob_tree_entries( ) def fake_git(_repo_root: Path, *args: str) -> bytes: + """Return one regular blob and one skipped gitlink tree entry.""" if args[0] == "ls-tree": return tree if args[0] == "show": @@ -656,6 +671,7 @@ def test_uv_lock_fails_closed_when_trusted_uv_bootstrap_fails( repo, base_sha = _uv_repo(tmp_path, with_pyproject=True) def fail_install() -> str: + """Raise the bootstrap failure expected for a tracked uv project.""" raise RuntimeError("trusted uv bootstrap failed") monkeypatch.setattr(materializer, "_install_trusted_uv", fail_install) @@ -671,6 +687,7 @@ def test_uv_lock_skipped_when_pyproject_is_absent( repo, base_sha = _uv_repo(tmp_path, with_pyproject=False, lock_dir="service") def unexpected_install() -> str: + """Fail if an orphan uv lock attempts to bootstrap the trusted exporter.""" raise AssertionError("orphan uv.lock must not bootstrap uv") monkeypatch.setattr(materializer, "_install_trusted_uv", unexpected_install) @@ -946,6 +963,7 @@ def test_install_trusted_uv_verifies_version_and_caches_path( calls = 0 def verify(*_args: object, **_kwargs: object) -> subprocess.CompletedProcess[bytes]: + """Return the exact pinned uv version and count verification executions.""" nonlocal calls calls += 1 return subprocess.CompletedProcess( @@ -988,6 +1006,7 @@ def test_install_trusted_uv_rejects_version_process_failures( monkeypatch.setattr(materializer, "_verified_uv_binary", lambda _payload: b"binary") def fail(*_args: object, **_kwargs: object) -> None: + """Raise the parameterized process failure from the fake executable.""" raise failure monkeypatch.setattr(materializer.subprocess, "run", fail) @@ -1044,6 +1063,7 @@ def test_run_uv_export_invokes_uv_with_frozen_offline_flags( captured: dict[str, object] = {} def fake_run(argv: list[str], **kwargs: object) -> subprocess.CompletedProcess[bytes]: + """Capture uv export arguments while returning a successful result.""" captured["argv"] = argv captured["cwd"] = kwargs.get("cwd") captured["timeout"] = kwargs.get("timeout") @@ -1079,6 +1099,7 @@ def test_uv_export_process_failures_fail_closed( monkeypatch.setattr(materializer, "_install_trusted_uv", lambda: "/usr/bin/uv") def fail_export(_work: Path, _uv_path: str) -> None: + """Raise the parameterized exporter failure for the fail-closed test.""" raise export_error monkeypatch.setattr(materializer, "_run_uv_export", fail_export)