Repository navigation
chore: release v1.3.2 #5413
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
| # This file is part of the jebel-quant/rhiza repository | |
| # (https://github.com/jebel-quant/rhiza). | |
| # | |
| # Workflow: Continuous Integration | |
| # | |
| # Purpose: Run tests on multiple Python versions, check dependencies, run | |
| # pre-commit hooks, verify documentation coverage, validate the | |
| # project, run security scans, and check license compliance. | |
| # | |
| # Python version matrix source of truth: | |
| # - Generated by hynek/build-and-inspect-python-package | |
| # - Reads `Programming Language :: Python :: 3.x` classifiers from package metadata | |
| # - Adding/removing classifiers updates CI Python coverage automatically | |
| # | |
| # Trigger: On push and pull_request. | |
| name: "(RHIZA) CI" | |
| # Cancel superseded in-progress runs of this workflow for the same ref. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| actions: read | |
| on: | |
| push: | |
| pull_request: | |
| workflow_call: | |
| secrets: | |
| GH_PAT: | |
| required: false | |
| UV_EXTRA_INDEX_URL: | |
| required: false | |
| jobs: | |
| generate-matrix: | |
| # CI budget: ≤5 min (matrix generation/setup). Last measured: 2026-05-28. | |
| timeout-minutes: 5 | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.versions-output.outputs.list }} | |
| os_matrix: ${{ steps.os.outputs.list }} | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| lfs: true | |
| - id: versions | |
| name: Build package metadata and extract supported Python classifiers | |
| uses: hynek/build-and-inspect-python-package@d44ca7d91762de7a7d5436ddae667c6da6d1c3df # v2.18.0 | |
| - id: versions-output | |
| run: | | |
| echo "list=${{ steps.versions.outputs.supported_python_classifiers_json_array }}" >> "$GITHUB_OUTPUT" | |
| - name: Debug matrix | |
| run: | | |
| echo "Python versions: ${{ steps.versions-output.outputs.list }}" | |
| - id: os | |
| run: | | |
| OS_MATRIX=$(make -f .rhiza/rhiza.mk -s ci-os-matrix) | |
| echo "list=$OS_MATRIX" >> "$GITHUB_OUTPUT" | |
| - name: Debug OS matrix | |
| run: | | |
| echo "OS versions: ${{ steps.os.outputs.list }}" | |
| test: | |
| # CI budget: ≤20 min (test matrix execution). Last measured: 2026-05-28. | |
| timeout-minutes: 20 | |
| needs: generate-matrix | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| python-version: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} | |
| os: ${{ fromJson(needs.generate-matrix.outputs.os_matrix) }} | |
| fail-fast: false | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| lfs: true | |
| - name: Install uv | |
| id: install-uv | |
| continue-on-error: true | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 | |
| with: | |
| version: "0.11.16" | |
| python-version: ${{ matrix.python-version }} | |
| - name: Retry uv installation | |
| if: steps.install-uv.outcome == 'failure' | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 | |
| with: | |
| version: "0.11.16" | |
| python-version: ${{ matrix.python-version }} | |
| - name: Configure git auth for private packages | |
| uses: jebel-quant/actions/configure-git-auth@6d52725ca371d609489c5b50236965ad70bbe528 # v1 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Cache uv artifacts | |
| uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0 | |
| with: | |
| path: ~/.cache/uv | |
| key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-uv- | |
| - name: Run tests | |
| env: | |
| UV_EXTRA_INDEX_URL: ${{ secrets.UV_EXTRA_INDEX_URL }} | |
| shell: bash | |
| run: | | |
| make test | |
| - name: Verify clean working tree | |
| if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest' | |
| shell: bash | |
| run: | | |
| status="$(git status --porcelain)" | |
| if [[ -n "$status" ]]; then | |
| echo "Working tree is dirty after make test:" | |
| printf '%s\n' "$status" | |
| git diff --exit-code || true | |
| exit 1 | |
| fi | |
| - name: Upload coverage report | |
| if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: coverage-report | |
| path: _tests/coverage.xml | |
| if-no-files-found: ignore | |
| lowest-deps: | |
| name: Lowest-direct dependency compatibility | |
| # CI budget: ≤20 min (dependency compatibility test run). Last measured: 2026-05-28. | |
| timeout-minutes: 20 | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 | |
| with: | |
| version: "0.11.16" | |
| - name: Configure git auth for private packages | |
| uses: jebel-quant/actions/configure-git-auth@6d52725ca371d609489c5b50236965ad70bbe528 # v1 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Install project with lowest-direct resolution | |
| env: | |
| UV_EXTRA_INDEX_URL: ${{ secrets.UV_EXTRA_INDEX_URL }} | |
| run: uv sync --all-extras --all-groups --resolution lowest-direct | |
| - name: Run tests with lowest-direct resolution | |
| env: | |
| UV_EXTRA_INDEX_URL: ${{ secrets.UV_EXTRA_INDEX_URL }} | |
| run: uv run --all-extras --all-groups --resolution lowest-direct pytest tests -q --ignore=tests/stress --ignore=tests/benchmarks | |
| typecheck: | |
| name: Type checking (Python ${{ matrix.python-version }}) | |
| # CI budget: ≤5 min (lint/typecheck quality gate). Last measured: 2026-05-28. | |
| timeout-minutes: 5 | |
| needs: generate-matrix | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| python-version: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} | |
| fail-fast: false | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 | |
| with: | |
| version: "0.11.16" | |
| python-version: ${{ matrix.python-version }} | |
| - name: Configure git auth for private packages | |
| uses: jebel-quant/actions/configure-git-auth@6d52725ca371d609489c5b50236965ad70bbe528 # v1 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Run ty and mypy type checkers (make typecheck) | |
| # Runs `uv run ty check src/` and `uv run mypy --strict src/` | |
| # as defined in .rhiza/make.d/test.mk. Set TYPECHECKER=ty or | |
| # TYPECHECKER=mypy to run only one of the two checkers. | |
| env: | |
| UV_EXTRA_INDEX_URL: ${{ secrets.UV_EXTRA_INDEX_URL }} | |
| run: make typecheck | |
| deptry: | |
| name: Check dependencies with deptry | |
| # CI budget: ≤5 min (lint/dependency hygiene check). Last measured: 2026-05-28. | |
| timeout-minutes: 5 | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 | |
| with: | |
| version: "0.11.16" | |
| - name: Configure git auth for private packages | |
| uses: jebel-quant/actions/configure-git-auth@6d52725ca371d609489c5b50236965ad70bbe528 # v1 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Run deptry | |
| run: make deps | |
| pre-commit: | |
| name: Pre-commit hooks | |
| # CI budget: ≤5 min (lint/format hooks). Last measured: 2026-05-28. | |
| timeout-minutes: 5 | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| - name: Configure git auth for private packages | |
| uses: jebel-quant/actions/configure-git-auth@6d52725ca371d609489c5b50236965ad70bbe528 # v1 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Cache prek environments | |
| uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0 | |
| with: | |
| path: ~/.cache/prek | |
| key: ${{ runner.os }}-prek-${{ hashFiles('.pre-commit-config.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-prek- | |
| - name: Run prek | |
| run: | | |
| make fmt | |
| docs-coverage: | |
| # CI budget: ≤10 min (docs quality check). Last measured: 2026-05-28. | |
| timeout-minutes: 10 | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 | |
| with: | |
| version: "0.11.16" | |
| - name: Configure git auth for private packages | |
| uses: jebel-quant/actions/configure-git-auth@6d52725ca371d609489c5b50236965ad70bbe528 # v1 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Check docs coverage | |
| env: | |
| UV_EXTRA_INDEX_URL: ${{ secrets.UV_EXTRA_INDEX_URL }} | |
| run: | | |
| make docs-coverage | |
| security: | |
| name: Security scanning | |
| # CI budget: ≤10 min (security scanning and suppression audit). Last measured: 2026-05-28. | |
| timeout-minutes: 10 | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 | |
| with: | |
| version: "0.11.16" | |
| - name: Configure git auth for private packages | |
| uses: jebel-quant/actions/configure-git-auth@6d52725ca371d609489c5b50236965ad70bbe528 # v1 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Run security scans | |
| env: | |
| UV_EXTRA_INDEX_URL: ${{ secrets.UV_EXTRA_INDEX_URL }} | |
| run: make security | |
| license: | |
| name: License compliance scan | |
| # CI budget: ≤10 min (license and artifact generation). Last measured: 2026-05-28. | |
| timeout-minutes: 10 | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 | |
| with: | |
| version: "0.11.16" | |
| - name: Configure git auth for private packages | |
| uses: jebel-quant/actions/configure-git-auth@6d52725ca371d609489c5b50236965ad70bbe528 # v1 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Run license check | |
| env: | |
| UV_EXTRA_INDEX_URL: ${{ secrets.UV_EXTRA_INDEX_URL }} | |
| run: make license | |
| - name: Generate LICENSES.md | |
| env: | |
| UV_EXTRA_INDEX_URL: ${{ secrets.UV_EXTRA_INDEX_URL }} | |
| run: | | |
| uv run --with pip-licenses pip-licenses --format markdown --output-file LICENSES.md | |
| - name: Upload LICENSES.md | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: LICENSES.md | |
| path: LICENSES.md | |
| if-no-files-found: ignore | |
| ci-gate: | |
| name: CI gate | |
| # Roll-up gate: a single required status check that aggregates the test, | |
| # typecheck, and lowest-deps jobs. Branch protection requires this context | |
| # instead of every per-leg name, so the matrix can change without editing | |
| # the ruleset. | |
| # CI budget: ≤5 min (result aggregation only). Last measured: 2026-06-27. | |
| timeout-minutes: 5 | |
| if: always() | |
| needs: [test, typecheck, lowest-deps] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 | |
| with: | |
| egress-policy: audit | |
| - name: Verify test, typecheck, and lowest-deps succeeded | |
| run: | | |
| echo "test result: ${{ needs.test.result }}" | |
| echo "typecheck result: ${{ needs.typecheck.result }}" | |
| echo "lowest-deps result: ${{ needs.lowest-deps.result }}" | |
| # Accept "success" or "cancelled" (transient runner cancellation). | |
| # Reject "failure" (real failure) and "skipped" (job never ran). | |
| if [[ "${{ needs.test.result }}" != "success" && "${{ needs.test.result }}" != "cancelled" \ | |
| || "${{ needs.typecheck.result }}" != "success" && "${{ needs.typecheck.result }}" != "cancelled" \ | |
| || "${{ needs.lowest-deps.result }}" != "success" && "${{ needs.lowest-deps.result }}" != "cancelled" ]]; then | |
| echo "::error::test, typecheck, and/or lowest-deps did not succeed; failing the CI gate." | |
| exit 1 | |
| fi | |
| echo "All required CI jobs succeeded." |