name: CI
on:
push:
branches: [ main ]
pull_request:
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Build
run: cargo build --all-targets
- name: Test
run: cargo test --all-targets
- name: Doc tests
run: cargo test --doc
lints:
name: Lints
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- name: rustfmt
run: cargo fmt --all -- --check
- name: clippy
run: cargo clippy --all-targets -- -D warnings
msrv:
name: MSRV (Rust 1.88)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@1.88
- uses: Swatinem/rust-cache@v2
- name: Build (MSRV)
run: cargo build --all-targets
- name: Test (MSRV)
run: cargo test --all-targets
audit:
name: Security audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install cargo-audit
uses: taiki-e/install-action@cargo-audit
- name: Run security audit
run: cargo audit
mutants:
name: Mutation tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-mutants
- name: Run mutation tests
run: cargo mutants --timeout 60
- name: Upload mutants output
if: always()
uses: actions/upload-artifact@v4
with:
name: mutants-out
path: mutants.out/
self_score:
name: Self-score
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov
- uses: Swatinem/rust-cache@v2
- name: Generate coverage
run: cargo llvm-cov --lcov --output-path lcov.info --workspace
- name: Score (threshold 15, exclude fixtures)
run: |
cargo run --release -- \
--lcov lcov.info \
--workspace \
--exclude 'tests/fixtures/**' \
--threshold 15 \
--fail-above \
--format json \
--output crap-current.json
- name: Upload baseline (main only)
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: crap-baseline
path: crap-current.json
retention-days: 90
- name: Download baseline (PRs only)
if: github.event_name == 'pull_request'
uses: actions/download-artifact@v4
with:
name: crap-baseline
continue-on-error: true
- name: Regression check (PRs only)
if: github.event_name == 'pull_request'
run: |
if [ -f crap-current.json ]; then
cargo run --release -- \
--lcov lcov.info \
--workspace \
--exclude 'tests/fixtures/**' \
--baseline crap-current.json \
--fail-regression
else
echo "No baseline available — skipping regression check."
fi