refactor(transparency): resolve method families via own algorithm_reg… #1343
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
| 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 |