1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
name: CI
on:
push:
branches:
pull_request:
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
# Disable incremental compilation in CI — costs more than it saves with cold caches.
CARGO_INCREMENTAL: "0"
# Surface backtraces for any panics so failures are diagnosable from logs alone.
RUST_BACKTRACE: "1"
jobs:
# ---------------------------------------------------------------------------
# Test matrix: stable on Linux + macOS.
# ---------------------------------------------------------------------------
test:
name: test (${{ matrix.os }}, ${{ matrix.rust }})
strategy:
fail-fast: false
matrix:
os:
rust:
include:
# Pin one beta job to catch upcoming-rustc breakage early.
- os: ubuntu-latest
rust: beta
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- uses: Swatinem/rust-cache@v2
- name: cargo build
run: cargo build --all-targets --locked
- name: cargo test (debug)
run: cargo test --all-targets --locked
# Perf budgets are wall-clock based — run them in release mode where
# the budgets were measured. They also pass in debug, but headroom is
# tighter and CI noise can flake. Restrict the release-mode run to
# Linux because macOS GitHub runners have meaningfully different
# absolute timings and would need their own per-OS budgets.
- name: cargo test --release (perf budgets, Linux only)
if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
run: cargo test --release --test perf_budgets -- --nocapture
# ---------------------------------------------------------------------------
# MSRV: keep the crate building on the version we advertise.
# ---------------------------------------------------------------------------
msrv:
name: MSRV (1.86)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.86"
- uses: Swatinem/rust-cache@v2
# We only check `cargo build`, not `cargo test`, because dev-deps may
# legitimately need a newer toolchain (e.g. tokio/insta versions). The
# public crate API and its runtime deps must stay buildable on MSRV.
- run: cargo build --locked
# ---------------------------------------------------------------------------
# Lints
# ---------------------------------------------------------------------------
clippy:
name: clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy --all-targets --locked -- -D warnings
fmt:
name: rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all --check
# ---------------------------------------------------------------------------
# Docs: build with -D warnings so broken intra-doc links fail CI.
# ---------------------------------------------------------------------------
docs:
name: docs
runs-on: ubuntu-latest
env:
RUSTDOCFLAGS: "-D warnings"
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo doc --no-deps --locked --all-features
# ---------------------------------------------------------------------------
# Publish dry-run: catches "this won't actually publish to crates.io"
# problems before they hit the release workflow.
# ---------------------------------------------------------------------------
publish-dry-run:
name: cargo publish --dry-run
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo publish --dry-run --locked
# ---------------------------------------------------------------------------
# Gate: everything above must pass.
# ---------------------------------------------------------------------------
all-checks:
name: all checks
if: always()
needs:
runs-on: ubuntu-latest
steps:
- name: Verify all checks passed
run: |
if [ "${{ needs.test.result }}" != "success" ] \
|| [ "${{ needs.msrv.result }}" != "success" ] \
|| [ "${{ needs.clippy.result }}" != "success" ] \
|| [ "${{ needs.fmt.result }}" != "success" ] \
|| [ "${{ needs.docs.result }}" != "success" ] \
|| [ "${{ needs.publish-dry-run.result }}" != "success" ]; then
echo "One or more checks failed."
exit 1
fi
echo "All checks passed."