Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/reusable-sourcebound.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "sourcebound"
version = "2.0.0rc1"
version = "2.0.0rc3"
description = "Bind documentation claims to source evidence and fail CI when they drift."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
115 changes: 69 additions & 46 deletions scripts/verify_published_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions tests/test_reusable_gate_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 36 additions & 1 deletion tests/test_verify_published_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
Loading