ci: emit the gate status context the org ruleset requires
#52
Workflow file for this run
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| jobs: | |
| test: | |
| name: Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo- | |
| - name: Run tests | |
| run: cargo test --workspace | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-lint-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-lint- | |
| - name: Check formatting | |
| run: cargo fmt --all -- --check | |
| - name: Run clippy | |
| run: cargo clippy --workspace -- -D warnings | |
| coverage: | |
| name: Coverage | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-cov-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-cov- | |
| - name: Install cargo-llvm-cov | |
| run: cargo install cargo-llvm-cov --locked | |
| - name: Generate coverage | |
| run: cargo llvm-cov --workspace --lcov --output-path lcov.info | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: lcov.info | |
| fail_ci_if_error: false | |
| security: | |
| name: Security | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| key: ${{ runner.os }}-cargo-audit-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo-audit- | |
| - name: Install cargo-audit | |
| run: cargo install cargo-audit --locked | |
| - name: Run security audit | |
| run: cargo audit | |
| # Aggregating gate. The paiml org ruleset "Green Main" requires exactly one | |
| # status context — `gate` — so this job's id AND name must stay literally | |
| # `gate`. It measures nothing of its own: it passes only when every real CI | |
| # job above reported `success`, and it names the jobs it checked so a green | |
| # here can never mean "checked nothing". | |
| gate: | |
| name: gate | |
| runs-on: ubuntu-latest | |
| needs: [test, lint, coverage, security] | |
| if: always() | |
| steps: | |
| - name: Aggregate CI job results | |
| env: | |
| NEEDS_JSON: ${{ toJSON(needs) }} | |
| # The denominator. Every job listed in `needs:` must appear here and | |
| # must have succeeded. Keep in sync with `needs:` above — a mismatch | |
| # fails the gate rather than silently guarding fewer jobs. | |
| EXPECTED: 'test lint coverage security' | |
| run: | | |
| set -euo pipefail | |
| echo "$NEEDS_JSON" | jq -r 'to_entries[] | "\(.key)=\(.value.result)"' > results.txt | |
| checked=0 | |
| failed=0 | |
| for job in $EXPECTED; do | |
| result="$(sed -n "s/^${job}=//p" results.txt)" | |
| if [ -z "$result" ]; then | |
| echo "MISSING ${job}: not present in needs — gate is not guarding it" | |
| failed=$((failed + 1)) | |
| continue | |
| fi | |
| checked=$((checked + 1)) | |
| if [ "$result" = "success" ]; then | |
| printf 'ok %s: %s\n' "$job" "$result" | |
| else | |
| printf 'FAIL %s: %s\n' "$job" "$result" | |
| failed=$((failed + 1)) | |
| fi | |
| done | |
| # Anything in needs but not in EXPECTED is unguarded — also a failure. | |
| while IFS='=' read -r job _; do | |
| case " $EXPECTED " in | |
| *" $job "*) ;; | |
| *) | |
| echo "UNGUARDED ${job}: in needs but not in EXPECTED" | |
| failed=$((failed + 1)) | |
| ;; | |
| esac | |
| done < results.txt | |
| echo "checked ${checked} of $(printf '%s\n' $EXPECTED | wc -l) expected CI jobs" | |
| if [ "$checked" -eq 0 ]; then | |
| echo "gate inspected zero jobs — refusing to report a pass" | |
| exit 1 | |
| fi | |
| if [ "$failed" -ne 0 ]; then | |
| echo "gate: ${failed} problem(s) — see above" | |
| exit 1 | |
| fi | |
| echo "gate: all ${checked} CI jobs green" |