Files
Marius CiepluchandClaude Fable 5.1 17194b3df9 ci: pytest runs with the hunt and semantic extras kept; sandbox tests skip by cause where no bwrap exists
CI's first full pytest run since 2836b78 showed 65 failures in three classes. uv run re-synced the environment
to the lockfile's default extras before pytest, removing dspy again (UV_NO_SYNC=1 now). The learn slice, pair
corpus and hunt producer tests need tree-sitter (semantic extra installed). Thirty-two tests need bubblewrap
isolation, which CI's container cannot provide: they carry a sandbox marker and skip where the isolation
preflight fails, with the cause's type in the reason; proven locally by hiding bwrap (7 skipped, 23 passed).
The partial-clone extraction test now probes one lazy fetch through git itself first, so git's own stderr
reaches the next CI log instead of git_exit_128.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 07:54:09 +02:00

83 lines
2.8 KiB
Python

from __future__ import annotations
import sys
import anyio
import dagger
# Substrings that mark a transient registry / image-pull failure (e.g. a Docker
# Hub 5xx on the python:3.11-bookworm pull, or a registry.dagger.io timeout) that
# is worth retrying rather than failing the whole pipeline.
_TRANSIENT_MARKERS = (
"pull image",
"failed to authorize",
"failed to fetch oauth",
"TransportQueryError",
"i/o timeout",
"connection reset",
"context deadline exceeded",
" 502",
" 503",
" 520",
" 429",
)
async def _run_checks() -> None:
config = dagger.Config(log_output=sys.stderr)
async with dagger.Connection(config) as client:
source = client.host().directory(
".",
exclude=[
".git",
".mypy_cache",
".pytest_cache",
".ruff_cache",
".venv",
"**/.openultrasast",
"dist",
],
)
checks = (
client.container()
.from_("python:3.11-bookworm")
.with_directory("/src", source)
.with_workdir("/src")
.with_exec(["python", "-m", "pip", "install", "uv"])
# uv run re-syncs the environment to the lockfile's default extras unless told not to, which silently
# removed the extras below before pytest ran.
.with_env_variable("UV_NO_SYNC", "1")
.with_exec(["uv", "sync", "--group", "dev", "--extra", "hunt", "--extra", "semantic"])
.with_exec(["uv", "run", "ruff", "format", "--check", "."])
.with_exec(["uv", "run", "ruff", "check", "."])
.with_exec(["uv", "run", "mypy", "src/openultrasast"])
.with_exec(["uv", "run", "pytest"])
.with_exec(["uv", "run", "python", "-m", "openultrasast.gate"])
.with_exec(["uv", "run", "python", "-m", "openultrasast.map_gate"])
.with_exec(["uv", "run", "python", "-m", "openultrasast.pair_gate"])
.with_exec(["uv", "run", "python", "-m", "compileall", "src", "tests"])
.with_exec(["uv", "build"])
)
await checks.sync()
async def main() -> None:
attempts = 3
for attempt in range(1, attempts + 1):
try:
await _run_checks()
return
except Exception as exc: # noqa: BLE001 — retry only transient registry/pull failures
message = str(exc)
transient = any(marker in message for marker in _TRANSIENT_MARKERS)
if attempt < attempts and transient:
print(f"dagger: transient registry/pull failure on attempt {attempt}; retrying in 20s", file=sys.stderr)
await anyio.sleep(20)
continue
raise
if __name__ == "__main__":
anyio.run(main)