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: |
for example in examples/*.rs; do
if [[ -f "$example" ]]; then
example_name=$(basename "$example" .rs)
# Skip examples that require special feature flags
if [[ "$example_name" == "09_authentication" ]]; then
echo "Skipping example: $example_name (requires authentication_example feature)"
continue
fi
if [[ "$example_name" == "10_progress_notifications" ]]; then
echo "Skipping example: $example_name (requires progress_example feature)"
continue
fi
if [[ "$example_name" == "11_request_cancellation" ]]; then
echo "Skipping example: $example_name (requires cancellation_example feature)"
continue
fi
if [[ "$example_name" == "13_websocket_transport" ]] || \
[[ "$example_name" == "27_websocket_server_enhanced" ]]; then
echo "Skipping example: $example_name (requires websocket feature)"
continue
fi
if [[ "$example_name" == "18_resource_watcher" ]]; then
echo "Skipping example: $example_name (requires resource-watcher feature)"
continue
fi
if [[ "$example_name" == "22_streamable_http_server_stateful" ]] || \
[[ "$example_name" == "23_streamable_http_server_stateless" ]] || \
[[ "$example_name" == "24_streamable_http_client" ]] || \
[[ "$example_name" == "55_server_middleware" ]]; then
echo "Skipping example: $example_name (requires streamable-http feature)"
continue
fi
if [[ "$example_name" == "32_typed_tools" ]] || \
[[ "$example_name" == "33_advanced_typed_tools" ]] || \
[[ "$example_name" == "34_serverbuilder_typed" ]] || \
[[ "$example_name" == "35_wasm_typed_tools" ]] || \
[[ "$example_name" == "36_typed_tool_v2_example" ]] || \
[[ "$example_name" == "37_description_variants_example" ]] || \
[[ "$example_name" == "53_typed_tools_workflow_integration" ]] || \
[[ "$example_name" == "conference_venue_map" ]] || \
[[ "$example_name" == "hotel_gallery" ]]; then
echo "Skipping example: $example_name (requires schema-generation feature)"
continue
fi
if [[ "$example_name" == "20_oidc_discovery" ]]; then
echo "Skipping example: $example_name (requires http-client feature)"
continue
fi
if [[ "$example_name" == "28_sse_optimized" ]]; then
echo "Skipping example: $example_name (requires sse feature)"
continue
fi
if [[ "$example_name" == "29_connection_pool" ]] || \
[[ "$example_name" == "30_enhanced_middleware" ]] || \
[[ "$example_name" == "31_advanced_error_recovery" ]] || \
[[ "$example_name" == "49_tool_with_sampling_server" ]] || \
[[ "$example_name" == "63_mcp_tool_macro" ]] || \
[[ "$example_name" == "64_mcp_prompt_macro" ]]; then
echo "Skipping example: $example_name (requires full feature)"
continue
fi
echo "Checking example: $example_name"
# Most examples need at least http feature for tokio
cargo check --example "$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: 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."