From 9edaf8a4c4d8dfed502e07ac38152d52a69c4110 Mon Sep 17 00:00:00 2001 From: owieschon Date: Tue, 21 Jul 2026 18:31:40 -0400 Subject: [PATCH 1/2] ci: preserve caller-owned required check names --- .github/workflows/reusable-sourcebound.yml | 6 ++++++ pyproject.toml | 2 +- tests/test_reusable_gate_workflow.py | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-sourcebound.yml b/.github/workflows/reusable-sourcebound.yml index b81e563..106972d 100644 --- a/.github/workflows/reusable-sourcebound.yml +++ b/.github/workflows/reusable-sourcebound.yml @@ -17,12 +17,18 @@ on: required: false default: "" type: string + check_name: + description: Display name for the caller's required status context. + required: false + default: sourcebound + type: string permissions: contents: read jobs: sourcebound: + name: ${{ inputs.check_name }} runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 diff --git a/pyproject.toml b/pyproject.toml index 5bb686e..8701230 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "sourcebound" -version = "2.0.0rc1" +version = "2.0.0rc2" description = "Bind documentation claims to source evidence and fail CI when they drift." readme = "README.md" requires-python = ">=3.10" diff --git a/tests/test_reusable_gate_workflow.py b/tests/test_reusable_gate_workflow.py index 1b23d73..a74b8ad 100644 --- a/tests/test_reusable_gate_workflow.py +++ b/tests/test_reusable_gate_workflow.py @@ -9,6 +9,8 @@ def test_reusable_gate_installs_and_invokes_the_sourcebound_runtime() -> None: workflow = (ROOT / ".github/workflows/reusable-sourcebound.yml").read_text() + assert "check_name:" in workflow + assert "name: ${{ inputs.check_name }}" in workflow assert '"sourcebound @ git+https://github.com/owieschon/sourcebound.git@${SOURCEBOUND_PACKAGE_REF}"' in workflow assert 'python3 -I -m sourcebound --root "$GITHUB_WORKSPACE" verdict' in workflow assert "from sourcebound.verdict import render_verdict_payload_sarif" in workflow From 20e66ccfd660439b4a6ab4515c7787c051a33e7b Mon Sep 17 00:00:00 2001 From: owieschon Date: Tue, 21 Jul 2026 18:39:31 -0400 Subject: [PATCH 2/2] fix: retry package installer visibility after publish --- pyproject.toml | 2 +- scripts/verify_published_release.py | 115 +++++++++++++++---------- tests/test_verify_published_release.py | 37 +++++++- 3 files changed, 106 insertions(+), 48 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8701230..e95a001 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "sourcebound" -version = "2.0.0rc2" +version = "2.0.0rc3" description = "Bind documentation claims to source evidence and fail CI when they drift." readme = "README.md" requires-python = ">=3.10" diff --git a/scripts/verify_published_release.py b/scripts/verify_published_release.py index b4a57c2..d09d72c 100644 --- a/scripts/verify_published_release.py +++ b/scripts/verify_published_release.py @@ -129,59 +129,76 @@ def _installed_binary(directory: Path) -> Path: return binary -def _smoke_installers(version: str, root: Path, temporary: Path) -> dict[str, str]: +def _smoke_installers( + version: str, + root: Path, + temporary: Path, + *, + attempts: int = 24, + delay: float = 5.0, +) -> dict[str, str]: specification = f"{PYPI_PROJECT}=={version}" if importlib.util.find_spec("pipx") is None: raise RuntimeError("pipx module not found; install the workflow's pinned pipx version") - pipx_home = temporary / "pipx-home" - pipx_bin = temporary / "pipx-bin" - pipx_env = dict(os.environ) - pipx_env.update( - { - "PIPX_BIN_DIR": str(pipx_bin), - "PIPX_DEFAULT_PYTHON": sys.executable, - "PIPX_HOME": str(pipx_home), - "PIPX_SHARED_LIBS": str(temporary / "pipx-shared"), - } - ) - _run( - [ - sys.executable, - "-m", - "pipx", - "install", - specification, - "--pip-args=--no-cache-dir", - ], - env=pipx_env, - ) - pipx_sourcebound = _installed_binary(pipx_bin) - pipx_version = _run([str(pipx_sourcebound), "--version"], env=pipx_env) - _run([str(pipx_sourcebound), "--root", str(root), "doctor"], env=pipx_env) - uv = shutil.which("uv") if uv is None: raise RuntimeError("uv executable not found") - uv_bin = temporary / "uv-bin" - uv_env = dict(os.environ) - uv_env.update( - { - "UV_CACHE_DIR": str(temporary / "uv-cache"), - "UV_TOOL_BIN_DIR": str(uv_bin), - "UV_TOOL_DIR": str(temporary / "uv-tools"), - } - ) - _run([uv, "tool", "install", specification], env=uv_env) - uv_sourcebound = _installed_binary(uv_bin) - uv_version = _run([str(uv_sourcebound), "--version"], env=uv_env) - _run([str(uv_sourcebound), "--root", str(root), "doctor"], env=uv_env) - - if pipx_version != version or uv_version != version: - raise RuntimeError( - f"installer versions do not match {version}: pipx={pipx_version}, uv={uv_version}" + + last_error: RuntimeError | None = None + for attempt in range(attempts): + pipx_home = temporary / "pipx-home" + pipx_bin = temporary / "pipx-bin" + uv_bin = temporary / "uv-bin" + for path in (pipx_home, pipx_bin, temporary / "pipx-shared", temporary / "uv-cache", temporary / "uv-tools", uv_bin): + shutil.rmtree(path, ignore_errors=True) + pipx_env = dict(os.environ) + pipx_env.update( + { + "PIPX_BIN_DIR": str(pipx_bin), + "PIPX_DEFAULT_PYTHON": sys.executable, + "PIPX_HOME": str(pipx_home), + "PIPX_SHARED_LIBS": str(temporary / "pipx-shared"), + } ) - return {"pipx": pipx_version, "uv": uv_version} + uv_env = dict(os.environ) + uv_env.update( + { + "UV_CACHE_DIR": str(temporary / "uv-cache"), + "UV_TOOL_BIN_DIR": str(uv_bin), + "UV_TOOL_DIR": str(temporary / "uv-tools"), + } + ) + try: + _run( + [ + sys.executable, + "-m", + "pipx", + "install", + specification, + "--pip-args=--no-cache-dir", + ], + env=pipx_env, + ) + pipx_sourcebound = _installed_binary(pipx_bin) + pipx_version = _run([str(pipx_sourcebound), "--version"], env=pipx_env) + _run([str(pipx_sourcebound), "--root", str(root), "doctor"], env=pipx_env) + + _run([uv, "tool", "install", specification], env=uv_env) + uv_sourcebound = _installed_binary(uv_bin) + uv_version = _run([str(uv_sourcebound), "--version"], env=uv_env) + _run([str(uv_sourcebound), "--root", str(root), "doctor"], env=uv_env) + if pipx_version != version or uv_version != version: + raise RuntimeError( + f"installer versions do not match {version}: pipx={pipx_version}, uv={uv_version}" + ) + return {"pipx": pipx_version, "uv": uv_version} + except RuntimeError as exc: + last_error = exc + if attempt + 1 < attempts: + time.sleep(delay) + raise RuntimeError(f"installers did not expose {specification} after {attempts} attempts: {last_error}") def verify_published_release( @@ -249,7 +266,13 @@ def verify_published_release( } if set(observed.values()) != {local_digest}: raise RuntimeError(f"published wheel digests differ: {observed}") - installers = _smoke_installers(version, root, temporary) + installers = _smoke_installers( + version, + root, + temporary, + attempts=attempts, + delay=delay, + ) return { "schema": SCHEMA, diff --git a/tests/test_verify_published_release.py b/tests/test_verify_published_release.py index 77d6e3a..5c70f54 100644 --- a/tests/test_verify_published_release.py +++ b/tests/test_verify_published_release.py @@ -62,6 +62,41 @@ def test_smoke_installers_names_the_missing_pipx_prerequisite( publication._smoke_installers("1.2.2", tmp_path, tmp_path / "temporary") +def test_smoke_installers_retries_package_index_visibility( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + attempts = 0 + + def fake_run(args: list[str], **_: object) -> str: + nonlocal attempts + if args[:4] == [sys.executable, "-m", "pipx", "install"]: + attempts += 1 + if attempts == 1: + raise RuntimeError("package index has not caught up") + return "" + if args[:3] == ["uv", "tool", "install"]: + return "" + if args[1:] == ["--version"]: + return "1.2.2" + if args[1:] == ["--root", str(tmp_path), "doctor"]: + return "" + raise AssertionError(f"unexpected command: {args}") + + monkeypatch.setattr(publication, "_run", fake_run) + monkeypatch.setattr(publication.importlib.util, "find_spec", lambda name: object()) + monkeypatch.setattr(publication.shutil, "which", lambda name: "uv") + monkeypatch.setattr(publication, "_installed_binary", lambda directory: Path("/bin/sourcebound")) + sleeps: list[float] = [] + monkeypatch.setattr(publication.time, "sleep", sleeps.append) + + assert publication._smoke_installers( + "1.2.2", tmp_path, tmp_path / "temporary", attempts=2, delay=0.25 + ) == {"pipx": "1.2.2", "uv": "1.2.2"} + assert attempts == 2 + assert sleeps == [0.25] + + def test_verify_published_release_matches_bytes_and_smokes_installers( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -96,7 +131,7 @@ def fake_run(args: list[str], **_: object) -> str: monkeypatch.setattr( publication, "_smoke_installers", - lambda version, root, temporary: {"pipx": version, "uv": version}, + lambda version, root, temporary, **_: {"pipx": version, "uv": version}, ) receipt = publication.verify_published_release(