| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- # Main quality gate (task 42).
- #
- # Per-PR legs (all must be green, except `coverage` which is report-only):
- # fmt, clippy, audit, deny, sqlx-prepare-check, nextest, doc-tests,
- # coverage (llvm-cov report upload over lib unit tests; non-blocking),
- # fuzz-build (nightly leg inside `fuzz/`, so the pinned stable
- # `rust-toolchain.toml` at the root is never violated).
- # Scheduled legs (not per-PR):
- # mutants (weekly budgeted mutation run over the pure security modules),
- # fuzz-run (bounded run of every fuzz target; the per-PR leg only builds),
- # miri (scoped interpreter run over the sync crypto/JOSE helpers;
- # allow-failure, never gates).
- #
- # Must NOT gate on miri for tokio/sqlx/redis; no lint suppressions anywhere.
- # The `miri` leg therefore runs only sync unit tests (`crypto::{ct, pkce,
- # tokens}` and the sync subset of `jose::jar` — the fetch-decode,
- # request-URI-membership, and alg-rejection tests, which short-circuit before
- # any crypto or I/O), excluding the argon2 KDF
- # (`crypto::secret`, cost prohibitive under the interpreter), the `ring`-backed
- # modules (`crypto::field`, `crypto::totp`, whose C/asm calls the interpreter
- # cannot execute), and every
- # `#[tokio::test]` / redis-backed test (DPoP replay store, async claim
- # paths, including the async `jose::jar::resolve` path itself — only its sync
- # unit tests are selected); the whole leg is `continue-on-error` so it can
- # never gate a PR. If a `#[tokio::test]` or a real-verification test is ever
- # added under `jose::jar`, narrow the filter to the explicit sync test paths.
- name: ci
- on:
- push:
- branches: ["master"]
- pull_request: {}
- schedule:
- # Weekly budgeted mutation run (Sunday 03:00 UTC).
- - cron: "0 3 * * 0"
- workflow_dispatch: {}
- permissions:
- contents: read
- env:
- CARGO_TERM_COLOR: always
- SQLX_OFFLINE: "true"
- # Shared compilation cache: sccache runs in each compiling job below (except
- # `miri`, whose cargo-miri overrides RUSTC_WRAPPER itself). It writes to a
- # local on-disk dir (SCCACHE_DIR) persisted by actions/cache -- NOT the GitHub
- # Actions cache backend, which stores one cache entry per compiled object
- # (thousands per run, per-ref duplicated); SCCACHE_GHA_ENABLED is deliberately
- # left unset. Incremental is useless on ephemeral runners and harms cross-run
- # cache hits.
- SCCACHE_DIR: ${{ github.workspace }}/.sccache
- CARGO_INCREMENTAL: "0"
- jobs:
- fmt:
- name: fmt
- runs-on: ubuntu-24.04
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- with:
- components: rustfmt
- - name: Check formatting
- run: cargo fmt --check
- clippy:
- name: clippy
- runs-on: ubuntu-24.04
- env:
- RUSTC_WRAPPER: sccache
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- with:
- components: clippy
- - name: Cache sccache directory
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ env.SCCACHE_DIR }}
- key: sccache-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
- restore-keys: |
- sccache-${{ runner.os }}-
- - name: Start sccache (shared compilation cache)
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
- with:
- version: "v0.18.0"
- - name: Clippy (all targets, warnings denied)
- run: cargo clippy --all-targets -- -D warnings
- audit:
- name: audit
- runs-on: ubuntu-24.04
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- - name: Install cargo-audit
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: cargo-audit
- - name: Audit advisories
- run: cargo audit
- deny:
- name: deny
- runs-on: ubuntu-24.04
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- - name: Install cargo-deny
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: cargo-deny
- - name: Check supply chain (ring-only TLS enforced in deny.toml)
- run: cargo deny check
- sqlx-prepare-check:
- name: sqlx-prepare-check
- runs-on: ubuntu-24.04
- env:
- RUSTC_WRAPPER: sccache
- services:
- postgres:
- image: postgres:16
- env:
- POSTGRES_USER: postgres
- POSTGRES_PASSWORD: sqlxcheck
- POSTGRES_DB: postgres
- ports:
- - 5432:5432
- options: >-
- --health-cmd "pg_isready -U postgres"
- --health-interval 5s
- --health-timeout 5s
- --health-retries 10
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- - name: Cache sccache directory
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ env.SCCACHE_DIR }}
- key: sccache-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
- restore-keys: |
- sccache-${{ runner.os }}-
- - name: Start sccache (shared compilation cache)
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
- with:
- version: "v0.18.0"
- - name: Install sqlx-cli
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: sqlx-cli@0.9.0
- - name: Apply migrations to the check database
- env:
- DATABASE_URL: postgres://postgres:sqlxcheck@127.0.0.1:5432/postgres
- run: cargo sqlx migrate run
- - name: Fail if the committed .sqlx cache is stale
- env:
- DATABASE_URL: postgres://postgres:sqlxcheck@127.0.0.1:5432/postgres
- run: cargo sqlx prepare --check --workspace
- doc-links:
- name: doc-links
- runs-on: ubuntu-24.04
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- with:
- python-version: "3.x"
- - name: Check documentation links (markdown-link-check equivalent)
- run: python3 scripts/check_doc_links.py
- nextest:
- name: nextest
- runs-on: ubuntu-24.04
- env:
- RUSTC_WRAPPER: sccache
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- - name: Cache sccache directory
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ env.SCCACHE_DIR }}
- key: sccache-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
- restore-keys: |
- sccache-${{ runner.os }}-
- - name: Start sccache (shared compilation cache)
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
- with:
- version: "v0.18.0"
- - name: Install cargo-nextest
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: nextest
- - name: Install redis-server (in-process Sentinel/Cluster fixtures)
- run: sudo apt-get update && sudo apt-get install -y redis-server
- - name: Start shared test services (Postgres + Redis)
- run: docker compose -f compose.test.yaml up -d
- - name: Full test suite (shared services; testcontainers for lifecycle)
- run: cargo nextest run --all-targets
- - name: Stop shared test services
- if: always()
- run: docker compose -f compose.test.yaml down -v
- doc-tests:
- name: doc-tests
- runs-on: ubuntu-24.04
- env:
- RUSTC_WRAPPER: sccache
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- - name: Cache sccache directory
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ env.SCCACHE_DIR }}
- key: sccache-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
- restore-keys: |
- sccache-${{ runner.os }}-
- - name: Start sccache (shared compilation cache)
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
- with:
- version: "v0.18.0"
- - name: Documentation tests
- run: cargo test --doc
- fuzz-build:
- name: fuzz-build
- runs-on: ubuntu-24.04
- env:
- RUSTC_WRAPPER: sccache
- defaults:
- run:
- working-directory: fuzz
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # The `fuzz/rust-toolchain.toml` in the working directory selects
- # nightly via rustup override; the root stable pin is untouched.
- - name: Install cargo-fuzz (nightly, per fuzz/rust-toolchain.toml)
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: cargo-fuzz
- - name: Cache sccache directory
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ env.SCCACHE_DIR }}
- key: sccache-${{ runner.os }}-fuzz-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
- restore-keys: |
- sccache-${{ runner.os }}-fuzz-
- - name: Start sccache (shared compilation cache)
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
- with:
- version: "v0.18.0"
- # Explicit gnu target: the prebuilt cargo-fuzz binary is musl-static,
- # so its default target is musl (ASan-incompatible, and musl std is
- # not installed). Never drop this flag without checking the default.
- - name: Build all fuzz targets
- run: cargo fuzz build --target x86_64-unknown-linux-gnu
- fuzz-run:
- name: fuzz-run
- # Scheduled bounded fuzzing. `fuzz-build` (above) only compiles the
- # targets per PR; this leg actually executes them, so a crash is found
- # without a human starting it. It is bounded and scheduled, not 24/7:
- # continuous coverage is ClusterFuzzLite (`.clusterfuzzlite/`, docs in
- # `docs/CONTRIBUTING.md`); the remaining owner step is enabling it.
- if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
- runs-on: ubuntu-24.04
- env:
- RUSTC_WRAPPER: sccache
- timeout-minutes: 70
- strategy:
- fail-fast: false
- matrix:
- target: [token_form, dcr_json, jar_claims, dpop_proof, jar_verify, redirect_uri, scope_parse]
- defaults:
- run:
- working-directory: fuzz
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- # `fuzz/rust-toolchain.toml` selects nightly; the root stable pin is
- # untouched, exactly as in `fuzz-build`.
- - name: Install cargo-fuzz (nightly)
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: cargo-fuzz
- - name: Cache sccache directory
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ env.SCCACHE_DIR }}
- key: sccache-${{ runner.os }}-fuzz-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
- restore-keys: |
- sccache-${{ runner.os }}-fuzz-
- - name: Start sccache (shared compilation cache)
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
- with:
- version: "v0.18.0"
- # `uses` steps resolve paths from the workspace root, not the job's
- # `working-directory`; keep the fuzz/ prefix explicit.
- - name: Restore corpus for ${{ matrix.target }}
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: fuzz/corpus/${{ matrix.target }}
- key: fuzz-corpus-${{ matrix.target }}-${{ github.run_id }}
- restore-keys: |
- fuzz-corpus-${{ matrix.target }}-
- # Same explicit target as `fuzz-build`: cargo-fuzz defaults to the
- # installer binary's own platform (musl), not the runner's (gnu).
- - name: Fuzz ${{ matrix.target }} (bounded)
- run: cargo fuzz run --target x86_64-unknown-linux-gnu ${{ matrix.target }} -- -max_total_time=600 -rss_limit_mb=2048
- # Save only on success; a per-run key grows the corpus while the prefix
- # `restore-keys` always pick the newest entry.
- - name: Save corpus for ${{ matrix.target }}
- if: success()
- uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: fuzz/corpus/${{ matrix.target }}
- key: fuzz-corpus-${{ matrix.target }}-${{ github.run_id }}
- - name: Upload crash artifacts
- if: failure()
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: fuzz-crash-${{ matrix.target }}
- path: fuzz/artifacts/${{ matrix.target }}
- if-no-files-found: ignore
- mutants:
- name: mutants
- # Scheduled budgeted job only: never blocks a PR.
- if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
- runs-on: ubuntu-24.04
- env:
- RUSTC_WRAPPER: sccache
- timeout-minutes: 60
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- - name: Cache sccache directory
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ env.SCCACHE_DIR }}
- key: sccache-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
- restore-keys: |
- sccache-${{ runner.os }}-
- - name: Start sccache (shared compilation cache)
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
- with:
- version: "v0.18.0"
- - name: Install cargo-mutants
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: cargo-mutants
- # cargo-mutants runs the baseline and every mutant via `cargo nextest run`.
- - name: Install cargo-nextest
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: nextest
- - name: Budgeted mutation run (see .cargo/mutants.toml)
- run: cargo mutants --timeout 1800 --test-tool nextest
- coverage:
- name: coverage
- # Report-only coverage signal (T4-4). Non-blocking by design: the upload
- # is the deliverable; there is no `--fail-under-*` threshold yet.
- # Services mirror `nextest` (shared Postgres/Redis plus redis-server),
- # because several lib unit tests are testcontainers-backed.
- # Follow-up: add a threshold and extend beyond `--lib` once
- # the first reports land.
- runs-on: ubuntu-24.04
- env:
- RUSTC_WRAPPER: sccache
- continue-on-error: true
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
- with:
- components: llvm-tools-preview
- - name: Cache sccache directory
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ env.SCCACHE_DIR }}
- key: sccache-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'fuzz/Cargo.lock') }}
- restore-keys: |
- sccache-${{ runner.os }}-
- - name: Start sccache (shared compilation cache)
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
- with:
- version: "v0.18.0"
- - name: Install cargo-llvm-cov
- uses: taiki-e/install-action@ab6895351a88b908e96a4697860d4ffea5e57dbe # v2
- with:
- tool: cargo-llvm-cov
- - name: Install redis-server (in-process Sentinel/Cluster fixtures)
- run: sudo apt-get update && sudo apt-get install -y redis-server
- - name: Start shared test services (Postgres + Redis)
- run: docker compose -f compose.test.yaml up -d
- - name: Coverage report (lib unit tests only; integration tests stay in `nextest`)
- run: cargo llvm-cov --lib --lcov --output-path lcov.info
- - name: Stop shared test services
- if: always()
- run: docker compose -f compose.test.yaml down -v
- - name: Upload coverage report
- if: always()
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- with:
- name: coverage-lcov
- path: lcov.info
- if-no-files-found: error
- miri:
- name: miri
- # Scoped miri leg (T4-4). Scheduled only: the interpreter is slow and
- # nightly-only, so this never runs per-PR and never gates
- # (`continue-on-error`). Nightly is confined to this job; the root
- # `rust-toolchain.toml` stable pin is untouched.
- # Scope is the sync unit tests of the dependency-free helpers, plus the
- # sync subset of `jose::jar` (fetch-decode, request-URI-membership, and
- # alg-rejection tests only — never the async `resolve` path).
- # Explicitly excluded, per the header constraint: tokio (`#[tokio::test]`), sqlx, and redis
- # (DPoP replay store, async claim paths), plus the argon2 KDF
- # (`crypto::secret`, cost prohibitive under the interpreter) and the
- # `ring`-backed modules (`crypto::field`, `crypto::totp`, whose C/asm
- # calls Miri cannot interpret).
- if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
- runs-on: ubuntu-24.04
- timeout-minutes: 60
- continue-on-error: true
- steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- - uses: dtolnay/rust-toolchain@f0d5300ba8908d942c808f4d31c08471d7798ba3 # nightly
- with:
- components: miri
- - name: Scoped miri run (sync crypto/JOSE helpers only)
- env:
- # Relaxed isolation for the first scheduled runs (wall-clock/env
- # access); tighten in a follow-up once green.
- MIRIFLAGS: -Zmiri-disable-isolation
- # `+nightly` overrides the root `rust-toolchain.toml` stable pin, which
- # would otherwise resolve `cargo miri` to stable (where miri is absent).
- run: >
- cargo +nightly miri test --lib --
- crypto::ct crypto::pkce crypto::tokens
- jose::jar
|