---
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
merge_group:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
CARGO_PROFILE_DEV_DEBUG: 0
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changed-files:
name: Detect changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
rust_changed: ${{ steps.changes.outputs.rust_changed }}
deps_changed: ${{ steps.changes.outputs.deps_changed }}
ci_changed: ${{ steps.changes.outputs.ci_changed }}
any_changed: ${{ steps.changes.outputs.any_changed }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v45
with:
files_yaml: |
rust:
- 'src/**/*.rs'
- 'tests/**'
deps:
- 'Cargo.toml'
- 'Cargo.lock'
ci:
- '.github/workflows/**'
- name: Determine changes
id: changes
env:
RUST_CHANGED: ${{ steps.changed-files.outputs.rust_any_changed }}
DEPS_CHANGED: ${{ steps.changed-files.outputs.deps_any_changed }}
CI_CHANGED: ${{ steps.changed-files.outputs.ci_any_changed }}
run: |
# On initial commits, tj-actions/changed-files produces empty
# outputs because there is no previous commit to diff against.
# Treat empty values as "true" so all jobs run.
[ -z "$RUST_CHANGED" ] && RUST_CHANGED="true"
[ -z "$DEPS_CHANGED" ] && DEPS_CHANGED="true"
[ -z "$CI_CHANGED" ] && CI_CHANGED="true"
echo "Rust code changed: $RUST_CHANGED"
echo "Dependencies changed: $DEPS_CHANGED"
echo "CI changed: $CI_CHANGED"
{
echo "rust_changed=$RUST_CHANGED"
echo "deps_changed=$DEPS_CHANGED"
echo "ci_changed=$CI_CHANGED"
if [ "$RUST_CHANGED" == "true" ] || [ "$DEPS_CHANGED" == "true" ] || [ "$CI_CHANGED" == "true" ]; then
echo "any_changed=true"
else
echo "any_changed=false"
fi
} >> "$GITHUB_OUTPUT"
quick-checks:
name: Quick Checks
needs: changed-files
if: needs.changed-files.outputs.any_changed == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: 1.92.0
components: rustfmt, clippy
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
shared-key: "quick-checks-${{ hashFiles('**/Cargo.lock') }}"
cache-on-failure: true
cache-all-crates: true
- name: Check formatting
run: cargo fmt -- --check
- name: Check compilation
run: cargo check --all-targets
- name: Clippy
run: cargo clippy --all-targets
test:
name: Test (${{ matrix.rust }})
needs: [quick-checks, changed-files]
if: needs.changed-files.outputs.any_changed == 'true'
strategy:
fail-fast: false
matrix:
include:
- rust: 1.92.0
- rust: stable
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain (${{ matrix.rust }})
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ matrix.rust }}
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
shared-key: "tests-${{ matrix.rust }}-${{ hashFiles('**/Cargo.lock') }}"
cache-on-failure: true
cache-all-crates: true
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install cargo-nextest
uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- name: Run tests
run: cargo nextest run --all-targets
docs:
name: Documentation
needs: changed-files
if: needs.changed-files.outputs.rust_changed == 'true' || needs.changed-files.outputs.deps_changed == 'true' || needs.changed-files.outputs.ci_changed == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: 1.92.0
- name: Cache documentation dependencies
uses: Swatinem/rust-cache@v2
with:
shared-key: "docs-${{ hashFiles('**/Cargo.lock') }}"
cache-on-failure: true
cache-all-crates: true
- name: Build documentation
run: cargo doc --no-deps
env:
RUSTDOCFLAGS: -D warnings
ci-success:
name: CI Success
if: always()
needs: [changed-files, quick-checks, test, docs]
runs-on: ubuntu-latest
steps:
- name: Check all jobs
env:
RUST_CHANGED: ${{ needs.changed-files.outputs.rust_changed }}
DEPS_CHANGED: ${{ needs.changed-files.outputs.deps_changed }}
CI_CHANGED: ${{ needs.changed-files.outputs.ci_changed }}
ANY_CHANGED: ${{ needs.changed-files.outputs.any_changed }}
QUICK_CHECKS_RESULT: ${{ needs.quick-checks.result }}
TEST_RESULT: ${{ needs.test.result }}
DOCS_RESULT: ${{ needs.docs.result }}
run: |
echo "Change detection:"
echo " Rust changed: $RUST_CHANGED"
echo " Deps changed: $DEPS_CHANGED"
echo " CI changed: $CI_CHANGED"
echo " Any changed: $ANY_CHANGED"
echo ""
echo "Job results:"
echo " quick-checks: $QUICK_CHECKS_RESULT"
echo " test: $TEST_RESULT"
echo " docs: $DOCS_RESULT"
# Check for failures (skipped jobs are OK)
failed=false
if [[ "$QUICK_CHECKS_RESULT" == "failure" ]]; then
echo "quick-checks failed"
failed=true
fi
if [[ "$TEST_RESULT" == "failure" ]]; then
echo "test failed"
failed=true
fi
if [[ "$DOCS_RESULT" == "failure" ]]; then
echo "docs failed"
failed=true
fi
if [[ "$failed" == "true" ]]; then
echo "One or more jobs failed"
exit 1
fi
echo "All required jobs succeeded (or were skipped)"