Skip to content

Commit 0cfa879

Browse files
konardclaude
andcommitted
Add CI/CD pipeline and development tools from template
Based on rust-ai-driven-development-pipeline-template best practices: - Enhanced CI/CD workflow with multi-OS testing (ubuntu, macos, windows) - Code coverage reporting with cargo-llvm-cov - Automatic release workflow with changelog fragment system - Manual release workflow with workflow_dispatch - Cache for faster CI builds Development tools: - scripts/ folder with version bumping and release scripts - changelog.d/ folder with fragment-based changelog system - .pre-commit-config.yaml for local code quality checks - CONTRIBUTING.md with development guidelines - CHANGELOG.md with keepachangelog format Cargo.toml improvements: - Fixed version typo (aplha -> 0.1.0) - Added lib section with correct crate name - Added release profile optimizations - Updated repository URLs .gitignore updates: - Added Rust-specific patterns (target/, *.rs.bk, etc.) - Added coverage and development tool patterns 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 5ed1798 commit 0cfa879

15 files changed

+1812
-11
lines changed

.github/workflows/rust.yml

Lines changed: 312 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,54 @@
1-
name: Rust
1+
name: Rust CI/CD Pipeline
22

33
on:
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

1741
defaults:
1842
run:
1943
working-directory: rust
2044

2145
jobs:
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

Comments
 (0)