name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUSTFLAGS: ""
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy, llvm-tools-preview
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install cargo-llvm-cov
run: |
if ! command -v cargo-llvm-cov &> /dev/null; then
cargo install cargo-llvm-cov
fi
- name: Format check
run: cargo fmt --all -- --check
- name: Clippy
run: |
cargo clippy --all-targets --all-features -- \
-D warnings \
-A clippy::module_name_repetitions \
-A clippy::must_use_candidate \
-A clippy::missing_errors_doc \
-A clippy::missing_const_for_fn \
-A clippy::return_self_not_must_use \
-A clippy::missing_fields_in_debug \
-A clippy::uninlined_format_args \
-A clippy::if_not_else \
-A clippy::result_large_err \
-A clippy::multiple_crate_versions \
-A clippy::implicit_hasher \
-A clippy::unused_async \
-A clippy::cast_lossless \
-A clippy::redundant_clone \
-A clippy::redundant_closure_for_method_calls \
-A clippy::significant_drop_tightening \
-A clippy::missing_panics_doc \
-A clippy::cast_possible_truncation \
-A clippy::cast_precision_loss \
-A clippy::option_if_let_else \
-A clippy::derive_partial_eq_without_eq \
-A clippy::redundant_else \
-A clippy::match_same_arms
- name: Build
run: cargo build --all-features --verbose
- name: Run tests
run: cargo test --all-features --verbose -- --test-threads=1
- name: Run doctests
run: cargo test --doc --all-features --verbose
- name: Check examples
run: |
# Build every registered example with its declared required-features.
# Reading required-features from cargo metadata keeps this in sync with
# Cargo.toml automatically, so future renames or feature changes don't
# require workflow edits.
cargo metadata --no-deps --format-version 1 \
| python3 -c '
import json, sys
meta = json.load(sys.stdin)
pkg = next(p for p in meta["packages"] if p["name"] == "pmcp")
for t in pkg["targets"]:
if "example" in t["kind"]:
feats = ",".join(t.get("required-features") or [])
print(f"{t[\"name\"]}|{feats}")
' | while IFS='|' read -r name feats; do
if [[ -n "$feats" ]]; then
echo "Checking example: $name (features: $feats)"
cargo check --example "$name" --features "$feats"
else
echo "Checking example: $name (no required features)"
cargo check --example "$name" --features http
fi
done
- name: Clean up before coverage
run: |
echo "Disk usage before cleanup:"
df -h
echo "Cleaning cargo build artifacts..."
cargo clean
echo "Disk usage after cleanup:"
df -h
- name: Run coverage
run: cargo llvm-cov --all-features --lcov --output-path lcov.info
feature-flags:
name: Feature Flag Verification
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-feature-flags-${{ hashFiles('**/Cargo.lock') }}
- name: Verify feature flag combinations
run: make test-feature-flags
quality-gate:
name: Quality Gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-cargo-quality-${{ hashFiles('**/Cargo.lock') }}
- name: Install quality tools
run: |
if ! command -v cargo-llvm-cov &> /dev/null; then
cargo install cargo-llvm-cov
fi
if ! command -v cargo-nextest &> /dev/null; then
cargo install cargo-nextest
fi
# Force install latest cargo-audit to support CVSS 4.0
cargo install cargo-audit --force
- name: Check disk space before quality gate
run: df -h
- name: Check rustdoc zero-warnings
run: make doc-check
- name: Run quality gate
run: make quality-gate
- name: Check disk space after quality gate
run: df -h
benchmarks:
name: Benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run benchmarks
run: cargo bench --all-features --no-run
msrv:
name: Minimum Supported Rust Version (1.82)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust 1.82
uses: dtolnay/rust-toolchain@1.82
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-msrv-${{ hashFiles('**/Cargo.lock') }}
- name: Check MSRV
run: cargo check --all-features
gate:
runs-on: ubuntu-latest
needs: [test, quality-gate]
if: always()
steps:
- name: Evaluate required checks
env:
TEST_RESULT: ${{ needs.test.result }}
QG_RESULT: ${{ needs.quality-gate.result }}
run: |
if [[ "$TEST_RESULT" != "success" ]] || \
[[ "$QG_RESULT" != "success" ]]; then
echo "Required checks failed: test=$TEST_RESULT, quality-gate=$QG_RESULT"
exit 1
fi
echo "All required checks passed."