Skip to content

refactor(transparency): resolve method families via own algorithm_reg… #1343

refactor(transparency): resolve method families via own algorithm_reg…

refactor(transparency): resolve method families via own algorithm_reg… #1343

Workflow file for this run

name: Quality Gate
on:
workflow_dispatch:
push:
# No path filter here: branch rulesets expect a status for every push. Doc-only
# changes skip heavy jobs via job-level conditions (skipped still satisfies required checks).
permissions:
contents: read
pull-requests: read
jobs:
changes:
name: Detect relevant changes
runs-on: ubuntu-latest
outputs:
python: ${{ steps.result.outputs.python }}
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Paths filter
id: filter
if: github.event_name != 'workflow_dispatch'
uses: dorny/paths-filter@v4
with:
filters: |
python:
- "**.py"
- "pyproject.toml"
- "uv.lock"
- ".github/workflows/**"
- ".github/actions/**"
- name: Decide whether Python CI should run
id: result
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "python=true" >> "$GITHUB_OUTPUT"
elif [ "${{ steps.filter.outputs.python }}" = "true" ]; then
echo "python=true" >> "$GITHUB_OUTPUT"
else
echo "python=false" >> "$GITHUB_OUTPUT"
fi
detect_pr_context:
name: Detect PR Context
needs: changes
if: needs.changes.outputs.python == 'true'
runs-on: ubuntu-latest
outputs:
has_open_pr: ${{ steps.detect.outputs.has_open_pr }}
is_draft_pr: ${{ steps.detect.outputs.is_draft_pr }}
steps:
- name: Detect open PR for branch
id: detect
uses: actions/github-script@v9
with:
script: |
const branch = context.ref.replace("refs/heads/", "");
const { owner, repo } = context.repo;
const { data: pulls } = await github.rest.pulls.list({
owner,
repo,
state: "open",
head: `${owner}:${branch}`,
});
if (pulls.length === 0) {
core.setOutput("has_open_pr", "false");
core.setOutput("is_draft_pr", "false");
return;
}
core.setOutput("has_open_pr", "true");
core.setOutput("is_draft_pr", pulls[0].draft ? "true" : "false");
quality-gate-linux-cpu:
# Single job runs both supported Python versions sequentially. 3.13 runs
# the full quality gate (lint, format, pyright, coverage) first so cheap
# checks fail fast; 3.11 only exercises the test suite (floor compatibility)
# and runs last — its result is moot if 3.13 already failed. Collapsing into
# one GitHub check trades parallelism for a quieter PR status list.
name: Quality Gate
needs: changes
if: needs.changes.outputs.python == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Python 3.13 environment
uses: ./.github/actions/setup-raitap-env
with:
python-version: "3.13"
sync-command: uv sync --group dev --extra torch-cpu --extra mlflow --extra shap --extra captum --extra metrics --extra onnx-cpu --extra reporting --extra torchattacks --extra foolbox --extra xgboost --extra text
- name: Ruff
run: uv run ruff check .
- name: Ruff Format
run: uv run ruff format --check .
- name: Guard against defensive getattr on typed config/backend
run: |
if grep -rnE 'getattr\((config|cfg|self\.config|self\.cfg|backend|self\.backend|self\._backend)\s*,\s*"[a-zA-Z_][a-zA-Z0-9_]*"\s*,' src/raitap --include='*.py' | grep -v '/tests/'; then
echo "::error::Defensive getattr on typed config/backend. AppConfig fields and ModelBackend attrs are declared — read them directly (config.x / backend.x) so a missing field fails loud. See docs/contributor/adr.md."
exit 1
fi
- name: Pyright
run: uv run pyright
- name: Pytest on 3.13 (with coverage)
run: uv run pytest -m "not e2e and not cuda" -v --cov=src/raitap --cov-report=term-missing --cov-report=html
- name: Upload coverage report
uses: actions/upload-artifact@v7
with:
name: coverage-report
path: htmlcov/
retention-days: 30
- name: Set up Python 3.11 environment
uses: ./.github/actions/setup-raitap-env
with:
python-version: "3.11"
sync-command: uv sync --group dev --extra torch-cpu --extra mlflow --extra shap --extra captum --extra metrics --extra onnx-cpu --extra reporting --extra torchattacks --extra foolbox --extra xgboost --extra text
- name: Pytest on 3.11 (floor compatibility, no coverage)
run: uv run pytest -m "not e2e and not cuda" -v
quality-gate-linux-cuda:
name: Quality Gate (Linux CUDA)
# Hosted GPU runners are not enabled for every repository by default.
# Keep the job defined, but only run it when the repo explicitly opts in.
needs: [changes, detect_pr_context]
if: ${{ needs.changes.outputs.python == 'true' && vars.ENABLE_HOSTED_GPU_CI == 'true' && needs.detect_pr_context.outputs.has_open_pr == 'true' && needs.detect_pr_context.outputs.is_draft_pr == 'false' }}
runs-on: ubuntu-latest-gpu
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up environment
uses: ./.github/actions/setup-raitap-env
with:
python-version: "3.13"
sync-command: uv sync --group dev --extra torch-cuda --extra mlflow --extra shap --extra captum --extra metrics --extra onnx-cuda --extra reporting --extra torchattacks --extra foolbox --extra text
- name: CUDA smoke check
run: |
uv run python - <<'PY'
import onnxruntime as ort
import torch
if not torch.cuda.is_available():
raise RuntimeError("CUDA is not available on this runner.")
tensor = torch.zeros(1, device="cuda")
if tensor.device.type != "cuda":
raise RuntimeError(f"Expected CUDA tensor, got {tensor.device}.")
providers = ort.get_available_providers()
if "CUDAExecutionProvider" not in providers:
raise RuntimeError(
f"CUDAExecutionProvider missing from ONNX Runtime providers: {providers}"
)
PY
- name: Pytest
run: uv run pytest -m "cuda" -v