publish: release 0.15.0 #995
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # End-to-end transparency test suite — resource-intensive, runs for active non-draft PRs. | |
| # | |
| # Trigger strategy | |
| # ---------------- | |
| # • Automatic: fires when a PR is opened, re-opened, marked ready for review, | |
| # or receives new commits. | |
| # • Draft PRs: the workflow may still be triggered by the PR event, but the job | |
| # itself is skipped until the PR is no longer a draft. | |
| # • Manual: use the "Run workflow" button in the Actions UI (workflow_dispatch) | |
| # whenever you need to re-run on demand. | |
| name: Transparency E2E | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| workflow_dispatch: | |
| inputs: | |
| reason: | |
| description: "Why are you running the E2E suite manually?" | |
| required: false | |
| default: "manual re-run after new commits" | |
| concurrency: | |
| # One run per PR at a time; do NOT cancel in-progress runs so a partial | |
| # result is never silently discarded. | |
| group: "e2e-${{ github.head_ref || github.ref_name }}" | |
| cancel-in-progress: false | |
| jobs: | |
| transparency-e2e-linux: | |
| name: "E2E / Transparency" | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| # pinned: pytest-mpl baselines are generated on this exact image (FreeType/Agg rendering is version-sensitive) | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 30 | |
| env: | |
| MPLBACKEND: Agg | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Set up environment | |
| uses: ./.github/actions/setup-raitap-env | |
| with: | |
| python-version: "3.13" | |
| # torchattacks + foolbox added so the Parity step can exercise the | |
| # robustness adversarial-attack parity tests in this 3.13 lane. | |
| sync-command: uv sync --group dev --extra torch-cpu --extra transparency --extra mlflow --extra metrics --extra onnx-cpu --extra torchattacks --extra foolbox | |
| # Split by marker so each category renders as its own collapsible section | |
| # in the job log. ``if: always()`` keeps later categories running after an | |
| # earlier failure; any failing step still fails the job. | |
| - name: Visual regression | |
| run: uv run pytest -m "e2e and visual" -v --tb=long --mpl | |
| - name: Parity | |
| if: always() | |
| # Captum/torchattacks/foolbox parity run here; marabou parity collects | |
| # but skips (maraboupy is absent in this 3.13 lane) — it runs in the | |
| # E2E / Robustness lane below. | |
| run: uv run pytest -m "e2e and parity" -v --tb=long | |
| - name: Transparency E2E (remaining) | |
| if: always() | |
| run: uv run pytest -m "e2e and not visual and not parity and not robustness" -v --tb=long | |
| detection-e2e-linux: | |
| # Drives the bundled ``contributor-configs/fasterrcnn-udacity/`` config | |
| # end-to-end: torchvision ``fasterrcnn_resnet50_fpn_v2`` on four Udacity | |
| # dashcam frames, per-box Integrated Gradients with the ``detection_image`` | |
| # visualiser, HTMLReporter output. Catches regressions in the detection | |
| # task family (TorchBackend autodetect, per-box explain phase, detection | |
| # visualiser, DetectionMetrics) at the integrated pipeline level. | |
| # | |
| # CPU runs of fasterrcnn + IG OOM the GitHub-hosted ubuntu-latest runner | |
| # (~7 GB RAM). Gated behind ``ENABLE_HOSTED_GPU_CI`` so the job lights up | |
| # only when an org-level GPU lane exists. Until then, contributors run | |
| # the config locally (``contributor-configs/fasterrcnn-udacity/run.sh``). | |
| name: "Detection E2E (Linux)" | |
| if: ${{ vars.ENABLE_HOSTED_GPU_CI == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| env: | |
| MPLBACKEND: Agg | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Set up environment | |
| uses: ./.github/actions/setup-raitap-env | |
| with: | |
| python-version: "3.13" | |
| # Mirror ``contributor-configs/fasterrcnn-udacity/run.sh`` so this | |
| # job exercises the same extras set a contributor gets locally. | |
| sync-command: uv sync --group dev --extra torch-cpu --extra captum --extra metrics --extra reporting | |
| - name: Cache torch hub weights + raitap sample data | |
| # fasterrcnn_resnet50_fpn_v2 weights (~170MB) + UdacitySelfDriving | |
| # samples (small) — both downloaded on first pipeline run, | |
| # deterministic per cfg. Key on pyproject.toml + the assessment.yaml | |
| # so a torchvision bump or cfg change cleanly invalidates. | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| ~/.cache/torch | |
| ~/.cache/raitap | |
| key: detection-e2e-assets-v1-${{ hashFiles('pyproject.toml', 'contributor-configs/fasterrcnn-udacity/assessment.yaml') }} | |
| restore-keys: | | |
| detection-e2e-assets-v1- | |
| - name: Pre-fetch Udacity sample images | |
| # Warm ~/.cache/raitap before the pipeline runs so a cache miss on the | |
| # samples download surfaces as its own failing step rather than | |
| # masquerading as a pipeline error. | |
| run: uv run python -c "from raitap.data.samples import _load_sample; _load_sample('UdacitySelfDriving')" | |
| - name: Run detection E2E pipeline | |
| # --acknowledge-preprocessing-off: torchvision detection models do | |
| # their own internal resize/normalise; a RAITAP-level preprocessing | |
| # step would break box-coord alignment. Mirrors run.sh. | |
| run: | | |
| set -o pipefail | |
| uv run raitap \ | |
| --config-dir contributor-configs/fasterrcnn-udacity \ | |
| --config-name assessment \ | |
| --acknowledge-preprocessing-off 2>&1 | tee detection-run.log | |
| - name: Assert DetectionMetrics resolved (in metrics metadata.json) | |
| # Pipeline persists ``resolved_target`` to ``metrics/metadata.json``; | |
| # checking the file is more robust than scraping log output. | |
| run: | | |
| find outputs -path "*/metrics/metadata.json" -print | head -1 | xargs grep -F "raitap.metrics.DetectionMetrics" | |
| - name: Assert outputs directory exists | |
| run: test -d outputs | |
| - name: Assert detection-image PNG produced | |
| run: find outputs -type f -name "*.png" | head | grep -q . | |
| - name: Assert HTMLReporter output exists | |
| # ``reporting.filename`` in the contributor config is | |
| # ``fasterrcnn_udacity_report``; HTMLReporter appends ``.html``. | |
| run: find outputs -type f -name "fasterrcnn_udacity_report.html" | grep -q . | |
| - name: Upload outputs on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: detection-e2e-outputs | |
| path: outputs | |
| retention-days: 7 | |
| marabou-e2e-linux: | |
| name: "E2E / Robustness" | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| env: | |
| MPLBACKEND: Agg | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Set up environment | |
| uses: ./.github/actions/setup-raitap-env | |
| with: | |
| # maraboupy 2.0.0 publishes cp311 wheels only — pin Python to 3.11. | |
| python-version: "3.11" | |
| sync-command: uv sync --group dev --extra torch-cpu --extra marabou | |
| - name: Install onnxruntime for ONNX inference in tests | |
| # The marabou extra pulls onnx (graph format) but not onnxruntime | |
| # (inference engine). The e2e tests run the original ACAS Xu | |
| # forward pass via onnxruntime to pick the target class, so install | |
| # it explicitly in this lane. | |
| run: uv pip install "onnxruntime>=1.17" | |
| - name: Run Marabou E2E tests | |
| # Scope collection to the marabou file so we don't import unrelated | |
| # test modules whose deps (torchmetrics, etc.) aren't installed in | |
| # this minimal lane. | |
| run: uv run pytest src/raitap/robustness/tests/test_e2e_marabou_acas_xu.py -m e2e -v --tb=long | |
| - name: Marabou parity | |
| if: always() | |
| # Verdict parity vs a direct maraboupy solve. Scoped to the parity file | |
| # for the same minimal-deps reason as the e2e step above. | |
| run: uv run pytest src/raitap/robustness/tests/test_marabou_parity.py -m parity -v --tb=long | |
| example-consumer-e2e-linux: | |
| # Drives `example/` as a standalone consumer end-to-end: `uv sync` against | |
| # the consumer pyproject, then full pipeline via the `raitap` | |
| # console-script. Covers the bits that only show up at the | |
| # consumer-project boundary: console-script entry, `RaitapSearchPathPlugin` | |
| # discovering bundled groups from outside the raitap source tree, | |
| # `tool.uv.package = false` + `tool.uv.sources` pattern, lazy_import | |
| # contract under a real install, and the live auto-deps bootstrap | |
| # (`uv add` + `os.execv` re-exec) with extras already pinned. | |
| # | |
| # The bootstrap-from-zero path (bare ``pip install raitap`` then | |
| # ``raitap --demo``) is covered by ``bare-bootstrap-from-zero-linux`` | |
| # below. | |
| name: "example/ consumer E2E (Linux)" | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| env: | |
| MPLBACKEND: Agg | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Install uv (with wheel cache) | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: "example/uv.lock" | |
| - name: Cache torch hub weights + raitap sample data | |
| # vit_b_32 weights (~350MB) + imagenet_samples (small) — both | |
| # downloaded on first pipeline run, deterministic per cfg. | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| ~/.cache/torch | |
| ~/.cache/raitap | |
| key: example-e2e-assets-v1-${{ hashFiles('example/assessment.yaml') }} | |
| restore-keys: | | |
| example-e2e-assets-v1- | |
| - name: Swap torch backend for CI host (runner-only) | |
| # Committed `example/pyproject.toml` pins `torch-intel` (the Intel-laptop | |
| # default for this project). GH ubuntu runners have no XPU; pulling | |
| # IPEX would waste ~200MB and never be used under `hardware=cpu`. | |
| # Swap to `torch-cpu` in the workspace copy only — never committed. | |
| working-directory: example | |
| run: | | |
| sed -i 's/torch-intel/torch-cpu/g' pyproject.toml | |
| rm -f uv.lock | |
| cat pyproject.toml | |
| - name: Sync consumer venv | |
| working-directory: example | |
| run: uv sync | |
| - name: Run example via console-script (bootstrap + re-exec live) | |
| # `--allow-project-edit` consents to the inferred `uv add` call. | |
| # Bootstrap always re-asserts the full inferred extras set (it | |
| # doesn't diff against the currently-pinned set), so even with | |
| # everything already pinned the call goes through — `uv add` is | |
| # idempotent at the resolver level, but `os.execv` + the sentinel | |
| # short-circuit + the post-relaunch pipeline import all execute. | |
| # `hardware=cpu` Hydra override pins the CPU backend regardless of | |
| # host probe. | |
| working-directory: example | |
| run: | | |
| set -o pipefail | |
| uv run raitap --config-name assessment hardware=cpu --allow-project-edit 2>&1 | tee run.log | |
| - name: Assert bootstrap fired and pipeline produced outputs | |
| working-directory: example | |
| run: | | |
| grep -F "uv add raitap[" run.log | |
| test -d outputs | |
| find outputs -type f -name 'report*' | grep -q . | |
| bare-bootstrap-from-zero-linux: | |
| # Proves the headline UX promise from ``docs/using-raitap/get-it-running.md``: | |
| # ``pip install raitap`` (no extras), ``raitap --demo``, RAITAP figures out | |
| # the rest. This exercises the bootstrap-from-zero path end-to-end: | |
| # - compose Hydra cfg on a venv with NO torch / torchvision / onnxruntime | |
| # - infer extras (host probe picks ``torch-cpu`` here) | |
| # - install via ``uv add raitap[<extras>]`` | |
| # - ``os.execv`` re-exec under the sentinel | |
| # - second-pass compose with the freshly installed backend | |
| # - run the demo pipeline + reports | |
| # | |
| # Bootstrap composes the cfg by importing every adapter family | |
| # ``__init__.py`` — the ``lazy_import`` contract in ``raitap.utils.lazy`` | |
| # is what lets that happen torch-free. A regression here means a | |
| # contributor added a top-level ``import torch`` / ``import torchvision`` | |
| # somewhere in the registration chain. The per-family | |
| # ``test_partial_extras_safe.py`` tests pinpoint the offender; | |
| # ``deps/tests/test_bootstrap_from_zero.py`` covers the bootstrap call | |
| # path itself. This job is the integrated proof. | |
| name: "Bare bootstrap-from-zero (Linux)" | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| env: | |
| MPLBACKEND: Agg | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Install uv (with wheel cache) | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: "uv.lock" | |
| - name: Cache torch hub weights + raitap sample data | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| ~/.cache/torch | |
| ~/.cache/raitap | |
| key: bare-bootstrap-assets-v1-${{ hashFiles('src/raitap/configs/demo.yaml') }} | |
| restore-keys: | | |
| bare-bootstrap-assets-v1- | |
| - name: Create bare consumer project (outside the raitap repo) | |
| # ``uv add`` needs a project pyproject.toml in cwd to operate. Build a | |
| # throwaway one in /tmp that depends on bare ``raitap`` from the | |
| # checkout — no extras pinned. This is the exact "first-time user" | |
| # state the get-it-running docs imply. | |
| run: | | |
| mkdir -p /tmp/bare-consumer | |
| cd /tmp/bare-consumer | |
| cat > pyproject.toml <<EOF | |
| [project] | |
| name = "bare-consumer" | |
| version = "0.0.0" | |
| requires-python = ">=3.11,<3.14" | |
| dependencies = ["raitap"] | |
| [tool.uv] | |
| package = false | |
| [tool.uv.sources] | |
| raitap = { path = "${GITHUB_WORKSPACE}", editable = false } | |
| EOF | |
| uv sync | |
| - name: Run demo end-to-end via bootstrap-from-zero | |
| # ``-y`` consents to ``uv add`` mutating the throwaway pyproject. The | |
| # bootstrap probes the GH ubuntu host (no GPU) → infers ``torch-cpu`` | |
| # + adapter extras from the demo cfg → installs → re-execs → runs. | |
| working-directory: /tmp/bare-consumer | |
| run: | | |
| set -o pipefail | |
| uv run raitap --demo -y 2>&1 | tee run.log | |
| - name: Assert bootstrap fired with torch-cpu + pipeline produced outputs | |
| working-directory: /tmp/bare-consumer | |
| run: | | |
| grep -F "torch-cpu" run.log | |
| grep -F "uv add raitap[" run.log | |
| test -d outputs | |
| find outputs -type f | head -20 |