88 default : true
99
1010jobs :
11- ci :
11+ # 📋 Code Quality Checks
12+ code-quality :
13+ name : Code Quality
1214 runs-on : ubuntu-latest
1315 env :
14- CARGO_LOCKED : " true" # don't mutate Cargo.lock during CI
1516 CARGO_TERM_COLOR : always
1617 steps :
1718 - uses : actions/checkout@v5
1819 with :
1920 fetch-depth : 0
2021
21- # Read MSRV (rust-version) from Cargo.toml
22+ - name : Install nightly rustfmt
23+ uses : dtolnay/rust-toolchain@v1
24+ with :
25+ toolchain : nightly-2025-08-01
26+ components : rustfmt
27+
28+ - name : Check formatting
29+ run : cargo +nightly-2025-08-01 fmt --all -- --check
30+
31+ - name : REUSE Compliance
32+ uses : fsfe/reuse-action@v5
33+
34+ # 🔍 Linting
35+ clippy :
36+ name : Clippy (MSRV)
37+ runs-on : ubuntu-latest
38+ env :
39+ CARGO_LOCKED : " true"
40+ CARGO_TERM_COLOR : always
41+ steps :
42+ - uses : actions/checkout@v5
43+
2244 - name : Read MSRV from Cargo.toml
2345 id : msrv
2446 shell : bash
@@ -36,26 +58,66 @@ jobs:
3658 echo "msrv=${RV}" >> "$GITHUB_OUTPUT"
3759 echo "Using MSRV: $RV"
3860
39- # Install MSRV for clippy/tests/package
4061 - name : Install Rust (${{ steps.msrv.outputs.msrv }})
4162 uses : dtolnay/rust-toolchain@v1
4263 with :
4364 toolchain : ${{ steps.msrv.outputs.msrv }}
4465 components : clippy
4566
46- # Pin nightly for rustfmt because unstable_features = true in .rustfmt.toml
47- - name : Install nightly rustfmt
67+ - name : Cache cargo
68+ uses : Swatinem/rust-cache@v2
69+ with :
70+ shared-key : " stable"
71+ save-if : ${{ github.ref == 'refs/heads/main' }}
72+
73+ - name : Run clippy
74+ shell : bash
75+ run : |
76+ set -euo pipefail
77+ if [ "${{ inputs.all-features }}" = "true" ]; then
78+ cargo +${{ steps.msrv.outputs.msrv }} clippy --workspace --all-targets --all-features -- -D warnings
79+ else
80+ cargo +${{ steps.msrv.outputs.msrv }} clippy --workspace --all-targets -- -D warnings
81+ fi
82+
83+ # 🧪 Tests
84+ test :
85+ name : Tests (MSRV)
86+ runs-on : ubuntu-latest
87+ env :
88+ CARGO_LOCKED : " true"
89+ CARGO_TERM_COLOR : always
90+ steps :
91+ - uses : actions/checkout@v5
92+
93+ - name : Read MSRV from Cargo.toml
94+ id : msrv
95+ shell : bash
96+ run : |
97+ set -euo pipefail
98+ if ! command -v jq >/dev/null 2>&1; then
99+ sudo apt-get update -y && sudo apt-get install -y jq
100+ fi
101+ RV=$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].rust_version // empty')
102+ if [ -z "$RV" ]; then
103+ echo "rust-version is not set in Cargo.toml"
104+ exit 1
105+ fi
106+ [[ "$RV" =~ ^[0-9]+\.[0-9]+$ ]] && RV="${RV}.0"
107+ echo "msrv=${RV}" >> "$GITHUB_OUTPUT"
108+ echo "Using MSRV: $RV"
109+
110+ - name : Install Rust (${{ steps.msrv.outputs.msrv }})
48111 uses : dtolnay/rust-toolchain@v1
49112 with :
50- toolchain : nightly-2025-08-01
51- components : rustfmt
113+ toolchain : ${{ steps.msrv.outputs.msrv }}
52114
53115 - name : Cache cargo
54116 uses : Swatinem/rust-cache@v2
55117 with :
118+ shared-key : " stable"
56119 save-if : ${{ github.ref == 'refs/heads/main' }}
57120
58- # Ensure Cargo.lock is present when CARGO_LOCKED=1
59121 - name : Verify lockfile is committed
60122 shell : bash
61123 run : |
@@ -65,41 +127,76 @@ jobs:
65127 exit 1
66128 fi
67129
68- - name : Check formatting (nightly rustfmt)
69- run : cargo +nightly-2025-08-01 fmt --all -- --check
130+ - name : Install cargo-nextest
131+ uses : taiki-e/install-action@v2
132+ with :
133+ tool : cargo-nextest
70134
71- - name : Clippy (MSRV)
135+ - name : Run tests
72136 shell : bash
73137 run : |
74138 set -euo pipefail
75139 if [ "${{ inputs.all-features }}" = "true" ]; then
76- cargo +${{ steps.msrv.outputs.msrv }} clippy --workspace --all-targets --all-features -- -D warnings
140+ cargo +${{ steps.msrv.outputs.msrv }} nextest run --workspace --all-features --no-fail-fast --profile ci
77141 else
78- cargo +${{ steps.msrv.outputs.msrv }} clippy --workspace --all-targets -- -D warnings
142+ cargo +${{ steps.msrv.outputs.msrv }} nextest run --workspace --no-fail-fast --profile ci
79143 fi
80144
81- - name : Tests (MSRV)
145+ - name : Upload test results to Codecov
146+ if : always()
147+ uses : codecov/test-results-action@v1
148+ with :
149+ token : ${{ secrets.CODECOV_TOKEN }}
150+
151+ # 📦 Build & Package
152+ build :
153+ name : Build & Package
154+ runs-on : ubuntu-latest
155+ needs : [code-quality, clippy, test]
156+ env :
157+ CARGO_LOCKED : " true"
158+ CARGO_TERM_COLOR : always
159+ steps :
160+ - uses : actions/checkout@v5
161+
162+ - name : Read MSRV from Cargo.toml
163+ id : msrv
82164 shell : bash
83165 run : |
84166 set -euo pipefail
85- if [ "${{ inputs.all-features }}" = "true" ]; then
86- cargo +${{ steps.msrv.outputs.msrv }} test --workspace --all-features --no-fail-fast
87- else
88- cargo +${{ steps.msrv.outputs.msrv }} test --workspace --no-fail-fast
167+ if ! command -v jq >/dev/null 2>&1; then
168+ sudo apt-get update -y && sudo apt-get install -y jq
169+ fi
170+ RV=$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].rust_version // empty')
171+ if [ -z "$RV" ]; then
172+ echo "rust-version is not set in Cargo.toml"
173+ exit 1
89174 fi
175+ [[ "$RV" =~ ^[0-9]+\.[0-9]+$ ]] && RV="${RV}.0"
176+ echo "msrv=${RV}" >> "$GITHUB_OUTPUT"
177+ echo "Using MSRV: $RV"
178+
179+ - name : Install Rust (${{ steps.msrv.outputs.msrv }})
180+ uses : dtolnay/rust-toolchain@v1
181+ with :
182+ toolchain : ${{ steps.msrv.outputs.msrv }}
183+
184+ - name : Cache cargo
185+ uses : Swatinem/rust-cache@v2
186+ with :
187+ shared-key : " stable"
90188
91-
92- - name : Auto-commit changes (any branch)
189+ - name : Auto-commit changes
93190 if : always()
94191 run : |
95192 set -euo pipefail
96- git config user.name "github-actions[bot]"
97- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
98- git config --global --add safe.directory "$GITHUB_WORKSPACE"
99- git add .
100- git commit -m "chore(readme): auto-refresh [skip ci]" || true
193+ git config user.name "github-actions[bot]"
194+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
195+ git config --global --add safe.directory "$GITHUB_WORKSPACE"
196+ git add .
197+ git commit -m "chore(readme): auto-refresh [skip ci]" || true
101198
102- - name : Ensure tree is clean before package
199+ - name : Ensure tree is clean
103200 shell : bash
104201 run : |
105202 set -euo pipefail
@@ -111,3 +208,41 @@ jobs:
111208
112209 - name : Package (dry-run)
113210 run : cargo +${{ steps.msrv.outputs.msrv }} package --locked
211+
212+ # 📊 Coverage
213+ coverage :
214+ name : Coverage
215+ runs-on : ubuntu-latest
216+ steps :
217+ - uses : actions/checkout@v5
218+
219+ - name : Install Rust stable
220+ uses : dtolnay/rust-toolchain@v1
221+ with :
222+ toolchain : stable
223+ components : llvm-tools-preview
224+
225+ - name : Cache cargo
226+ uses : Swatinem/rust-cache@v2
227+ with :
228+ shared-key : " coverage"
229+ save-if : ${{ github.ref == 'refs/heads/main' }}
230+
231+ - name : Install cargo-llvm-cov
232+ uses : taiki-e/install-action@v2
233+ with :
234+ tool : cargo-llvm-cov
235+
236+ - name : Generate coverage
237+ shell : bash
238+ run : |
239+ set -euo pipefail
240+ cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
241+
242+ - name : Upload coverage to Codecov
243+ uses : codecov/codecov-action@v5
244+ with :
245+ token : ${{ secrets.CODECOV_TOKEN }}
246+ files : lcov.info
247+ fail_ci_if_error : true
248+ verbose : true
0 commit comments