1- name : Rust
1+ name : Rust CI/CD Pipeline
22
33on :
44 push :
55 branches :
6- - ' main'
6+ - main
77 paths :
88 - ' rust/**'
99 - ' .github/workflows/rust.yml'
10+ - ' scripts/**'
11+ - ' changelog.d/**'
1012 pull_request :
11- branches :
12- - ' main'
13+ types : [opened, synchronize, reopened]
1314 paths :
1415 - ' rust/**'
1516 - ' .github/workflows/rust.yml'
17+ - ' scripts/**'
18+ - ' changelog.d/**'
19+ workflow_dispatch :
20+ inputs :
21+ bump_type :
22+ description : ' Version bump type'
23+ required : true
24+ type : choice
25+ options :
26+ - patch
27+ - minor
28+ - major
29+ description :
30+ description : ' Release description (optional)'
31+ required : false
32+ type : string
33+
34+ concurrency :
35+ group : ${{ github.workflow }}-${{ github.ref }}
36+ cancel-in-progress : true
37+
38+ env :
39+ CARGO_TERM_COLOR : always
1640
1741defaults :
1842 run :
1943 working-directory : rust
2044
2145jobs :
22- test :
46+ # REQUIRED CI CHECKS - All must pass before release
47+ # These jobs ensure code quality and tests pass before any release
48+
49+ # Linting and formatting
50+ lint :
51+ name : Lint and Format Check
2352 runs-on : ubuntu-latest
2453 steps :
2554 - uses : actions/checkout@v4
@@ -30,13 +59,62 @@ jobs:
3059 toolchain : nightly-2022-08-22
3160 components : rustfmt
3261
33- - name : Run tests
34- run : cargo test --verbose
62+ - name : Setup Node.js
63+ uses : actions/setup-node@v4
64+ with :
65+ node-version : ' 20.x'
66+
67+ - name : Cache cargo registry
68+ uses : actions/cache@v4
69+ with :
70+ path : |
71+ ~/.cargo/registry
72+ ~/.cargo/git
73+ rust/target
74+ key : ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }}
75+ restore-keys : |
76+ ${{ runner.os }}-cargo-
3577
3678 - name : Check formatting
37- run : cargo fmt -- --check
79+ run : cargo fmt --all -- --check
80+
81+ - name : Check file size limit
82+ working-directory : .
83+ run : node scripts/check-file-size.mjs
84+
85+ # Test on multiple OS
86+ test :
87+ name : Test (${{ matrix.os }})
88+ runs-on : ${{ matrix.os }}
89+ strategy :
90+ fail-fast : false
91+ matrix :
92+ os : [ubuntu-latest, macos-latest, windows-latest]
93+ steps :
94+ - uses : actions/checkout@v4
95+
96+ - name : Install Rust toolchain
97+ uses : dtolnay/rust-toolchain@master
98+ with :
99+ toolchain : nightly-2022-08-22
100+
101+ - name : Cache cargo registry
102+ uses : actions/cache@v4
103+ with :
104+ path : |
105+ ~/.cargo/registry
106+ ~/.cargo/git
107+ rust/target
108+ key : ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }}
109+ restore-keys : |
110+ ${{ runner.os }}-cargo-
38111
112+ - name : Run tests
113+ run : cargo test --all-features --verbose
114+
115+ # Code coverage
39116 coverage :
117+ name : Code Coverage
40118 runs-on : ubuntu-latest
41119 steps :
42120 - uses : actions/checkout@v4
@@ -47,6 +125,17 @@ jobs:
47125 toolchain : nightly-2022-08-22
48126 components : llvm-tools-preview
49127
128+ - name : Cache cargo registry
129+ uses : actions/cache@v4
130+ with :
131+ path : |
132+ ~/.cargo/registry
133+ ~/.cargo/git
134+ rust/target
135+ key : ${{ runner.os }}-cargo-coverage-${{ hashFiles('rust/Cargo.lock') }}
136+ restore-keys : |
137+ ${{ runner.os }}-cargo-coverage-
138+
50139 - name : Install cargo-llvm-cov
51140 uses : taiki-e/install-action@cargo-llvm-cov
52141
@@ -58,3 +147,218 @@ jobs:
58147 with :
59148 files : rust/lcov.info
60149 fail_ci_if_error : false
150+
151+ # Build package - only runs if lint and test pass
152+ build :
153+ name : Build Package
154+ runs-on : ubuntu-latest
155+ needs : [lint, test]
156+ steps :
157+ - uses : actions/checkout@v4
158+
159+ - name : Install Rust toolchain
160+ uses : dtolnay/rust-toolchain@master
161+ with :
162+ toolchain : nightly-2022-08-22
163+
164+ - name : Cache cargo registry
165+ uses : actions/cache@v4
166+ with :
167+ path : |
168+ ~/.cargo/registry
169+ ~/.cargo/git
170+ rust/target
171+ key : ${{ runner.os }}-cargo-build-${{ hashFiles('rust/Cargo.lock') }}
172+ restore-keys : |
173+ ${{ runner.os }}-cargo-build-
174+
175+ - name : Build release
176+ run : cargo build --release --verbose
177+
178+ # Check for changelog fragments in PRs
179+ changelog :
180+ name : Changelog Fragment Check
181+ runs-on : ubuntu-latest
182+ if : github.event_name == 'pull_request'
183+ steps :
184+ - uses : actions/checkout@v4
185+ with :
186+ fetch-depth : 0
187+
188+ - name : Check for changelog fragments
189+ working-directory : .
190+ run : |
191+ # Get list of fragment files (excluding README and template)
192+ FRAGMENTS=$(find changelog.d -name "*.md" ! -name "README.md" 2>/dev/null | wc -l)
193+
194+ # Get changed files in PR
195+ CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
196+
197+ # Check if any source files changed (excluding docs and config)
198+ SOURCE_CHANGED=$(echo "$CHANGED_FILES" | grep -E "^(rust/src/|rust/tests/|scripts/)" | wc -l)
199+
200+ if [ "$SOURCE_CHANGED" -gt 0 ] && [ "$FRAGMENTS" -eq 0 ]; then
201+ echo "::warning::No changelog fragment found. Please add a changelog entry in changelog.d/"
202+ echo ""
203+ echo "To create a changelog fragment:"
204+ echo " Create a new .md file in changelog.d/ with your changes"
205+ echo ""
206+ echo "See changelog.d/README.md for more information."
207+ # Note: This is a warning, not a failure, to allow flexibility
208+ # Change 'exit 0' to 'exit 1' to make it required
209+ exit 0
210+ fi
211+
212+ echo "Changelog check passed"
213+
214+ # Automatic release on push to main using changelog fragments
215+ # This job automatically bumps version based on fragments in changelog.d/
216+ auto-release :
217+ name : Auto Release
218+ needs : [lint, test, build, coverage]
219+ if : github.event_name == 'push' && github.ref == 'refs/heads/main'
220+ runs-on : ubuntu-latest
221+ permissions :
222+ contents : write
223+ steps :
224+ - uses : actions/checkout@v4
225+ with :
226+ fetch-depth : 0
227+ token : ${{ secrets.GITHUB_TOKEN }}
228+
229+ - name : Install Rust toolchain
230+ uses : dtolnay/rust-toolchain@master
231+ with :
232+ toolchain : nightly-2022-08-22
233+
234+ - name : Setup Node.js
235+ uses : actions/setup-node@v4
236+ with :
237+ node-version : ' 20.x'
238+
239+ - name : Configure git
240+ run : |
241+ git config user.name "github-actions[bot]"
242+ git config user.email "github-actions[bot]@users.noreply.github.com"
243+ working-directory : .
244+
245+ - name : Determine bump type from changelog fragments
246+ id : bump_type
247+ working-directory : .
248+ run : node scripts/get-bump-type.mjs
249+
250+ - name : Check if version already released or no fragments
251+ id : check
252+ working-directory : .
253+ run : |
254+ # Check if there are changelog fragments
255+ if [ "${{ steps.bump_type.outputs.has_fragments }}" != "true" ]; then
256+ # No fragments - check if current version tag exists
257+ CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' rust/Cargo.toml)
258+ if git rev-parse "v$CURRENT_VERSION" >/dev/null 2>&1; then
259+ echo "No changelog fragments and v$CURRENT_VERSION already released"
260+ echo "should_release=false" >> $GITHUB_OUTPUT
261+ else
262+ echo "No changelog fragments but v$CURRENT_VERSION not yet released"
263+ echo "should_release=true" >> $GITHUB_OUTPUT
264+ echo "skip_bump=true" >> $GITHUB_OUTPUT
265+ fi
266+ else
267+ echo "Found changelog fragments, proceeding with release"
268+ echo "should_release=true" >> $GITHUB_OUTPUT
269+ echo "skip_bump=false" >> $GITHUB_OUTPUT
270+ fi
271+
272+ - name : Collect changelog and bump version
273+ id : version
274+ if : steps.check.outputs.should_release == 'true' && steps.check.outputs.skip_bump != 'true'
275+ working-directory : .
276+ run : |
277+ node scripts/version-and-commit.mjs \
278+ --bump-type "${{ steps.bump_type.outputs.bump_type }}"
279+
280+ - name : Get current version
281+ id : current_version
282+ if : steps.check.outputs.should_release == 'true'
283+ working-directory : .
284+ run : |
285+ CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' rust/Cargo.toml)
286+ echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
287+
288+ - name : Build release
289+ if : steps.check.outputs.should_release == 'true'
290+ run : cargo build --release
291+
292+ - name : Create GitHub Release
293+ if : steps.check.outputs.should_release == 'true'
294+ env :
295+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
296+ working-directory : .
297+ run : |
298+ node scripts/create-github-release.mjs \
299+ --release-version "${{ steps.current_version.outputs.version }}" \
300+ --repository "${{ github.repository }}"
301+
302+ # Manual release via workflow_dispatch - only after CI passes
303+ manual-release :
304+ name : Manual Release
305+ needs : [lint, test, build, coverage]
306+ if : github.event_name == 'workflow_dispatch'
307+ runs-on : ubuntu-latest
308+ permissions :
309+ contents : write
310+ steps :
311+ - uses : actions/checkout@v4
312+ with :
313+ fetch-depth : 0
314+ token : ${{ secrets.GITHUB_TOKEN }}
315+
316+ - name : Install Rust toolchain
317+ uses : dtolnay/rust-toolchain@master
318+ with :
319+ toolchain : nightly-2022-08-22
320+
321+ - name : Setup Node.js
322+ uses : actions/setup-node@v4
323+ with :
324+ node-version : ' 20.x'
325+
326+ - name : Configure git
327+ run : |
328+ git config user.name "github-actions[bot]"
329+ git config user.email "github-actions[bot]@users.noreply.github.com"
330+ working-directory : .
331+
332+ - name : Collect changelog fragments
333+ working-directory : .
334+ run : |
335+ # Check if there are any fragments to collect
336+ FRAGMENTS=$(find changelog.d -name "*.md" ! -name "README.md" 2>/dev/null | wc -l)
337+ if [ "$FRAGMENTS" -gt 0 ]; then
338+ echo "Found $FRAGMENTS changelog fragment(s), collecting..."
339+ node scripts/collect-changelog.mjs
340+ else
341+ echo "No changelog fragments found, skipping collection"
342+ fi
343+
344+ - name : Version and commit
345+ id : version
346+ working-directory : .
347+ run : |
348+ node scripts/version-and-commit.mjs \
349+ --bump-type "${{ github.event.inputs.bump_type }}" \
350+ --description "${{ github.event.inputs.description }}"
351+
352+ - name : Build release
353+ if : steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
354+ run : cargo build --release
355+
356+ - name : Create GitHub Release
357+ if : steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
358+ env :
359+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
360+ working-directory : .
361+ run : |
362+ node scripts/create-github-release.mjs \
363+ --release-version "${{ steps.version.outputs.new_version }}" \
364+ --repository "${{ github.repository }}"
0 commit comments