This project uses a validate-on-PR, release-on-tag CI/CD pipeline strategy, which is the industry best practice for Rust CLI projects. This approach ensures high standards of artifact provenance, reproducibility, traceability, and rollback safety.
Triggers: Pull requests and pushes to main
Purpose: Validate code quality and correctness
Jobs:
-
Check - Formatting and compilation checks
cargo fmt --check- Verify code formattingcargo check- Verify code compiles
-
Test - Run test suite with cargo-nextest
cargo nextest run --profile ci --all-features --workspace --lib- Run unit tests- Generates JUnit XML reports for test result visualization
- Publishes test results to PR comments with timing data
- Uploads test artifacts for 90-day retention
-
Clippy - Linting and code quality
cargo clippy -- -D warnings- Treat warnings as errors
-
Build - Cross-platform build verification
- Build on Ubuntu and macOS
- Upload artifacts for manual testing
Triggers: Version tags matching v* (e.g., v1.2.3)
Purpose: Build release artifacts and publish GitHub releases
Jobs:
-
Verify CI (quality gate)
- Wait for Check, Test, Clippy, and Build jobs to pass
- Ensures all CI checks complete before proceeding
-
Pre-Release Validation (quality gate)
- Run full lifecycle deployment test
- Test creates, updates, and deletes a deployment (~20-30 minutes)
- Validates SDK deployment workflow works before releasing
- Requires secrets:
LANGSMITH_API_KEY,LANGSMITH_WORKSPACE_ID - Timeout: 45 minutes
- If this job fails, the release is blocked
-
Create Release
- Generate changelog using
git-cliff - Create GitHub Release with changelog
- Determine if pre-release based on version suffix
- Only runs if CI verification and pre-release validation pass
- Generate changelog using
-
Build Release
- Build binaries for multiple platforms:
x86_64-unknown-linux-musl(static Linux)x86_64-unknown-linux-gnu(dynamic Linux)x86_64-apple-darwin(macOS Intel)aarch64-apple-darwin(macOS Apple Silicon)x86_64-pc-windows-msvc(Windows)
- Strip binaries (Unix only)
- Create archives (.tar.gz for Unix, .zip for Windows)
- Generate SHA256 checksums
- Upload to GitHub Release
- Build binaries for multiple platforms:
-
Tools (pre-installed in devcontainer):
cargo-release- Version management and release automationgit-cliff- Changelog generation from conventional commits
Note: If not using the devcontainer, install manually:
cargo install cargo-release git-cliff
-
Ensure clean state:
- All changes committed
- On
mainbranch - Working directory clean
- All tests passing
# Dry-run to preview changes
cargo release --dry-run
# Create a patch release (0.1.0 → 0.1.1)
cargo release patch --execute
# Create a minor release (0.1.0 → 0.2.0)
cargo release minor --execute
# Create a major release (0.1.0 → 1.0.0)
cargo release major --executeWhat cargo-release does:
- Updates version in
Cargo.toml - Runs git-cliff to generate changelog
- Creates a git commit with version bump
- Creates an annotated git tag (e.g.,
v1.2.3) - Pushes commit and tag to GitHub
- GitHub Actions automatically triggers release workflow
# 1. Update version in Cargo.toml manually
vim Cargo.toml # Change version field
# 2. Generate changelog
git-cliff --tag v1.2.3 --output CHANGELOG.md
# 3. Commit changes
git add Cargo.toml Cargo.lock CHANGELOG.md
git commit -m "🔖 release: bump version to v1.2.3
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>"
# 4. Create annotated tag
git tag -a v1.2.3 -m "Release v1.2.3"
# 5. Push commit and tag
git push && git push origin v1.2.3What happens next:
- GitHub Actions detects the tag
- Release workflow builds binaries for all platforms
- GitHub Release is created with changelog and binaries
For alpha, beta, or release candidate versions:
# Using cargo-release
cargo release --pre-release alpha --execute # Creates v1.0.0-alpha.1
cargo release --pre-release beta --execute # Creates v1.0.0-beta.1
cargo release --pre-release rc --execute # Creates v1.0.0-rc.1
# Manual
git tag -a v1.0.0-alpha.1 -m "Release v1.0.0-alpha.1"
git push origin v1.0.0-alpha.1Pre-releases are automatically detected and marked appropriately on GitHub.
Configures git-cliff to generate changelogs from Conventional Emoji Commits.
Key features:
- Parses commits with emojis and conventional format
- Groups commits by type (Features, Bug Fixes, etc.)
- Links to GitHub PRs automatically
- Prioritizes breaking changes at the top
Testing locally:
# Preview changelog for next release
git-cliff --unreleased
# Preview changelog for specific tag
git-cliff --tag v1.2.3
# Generate changelog and update CHANGELOG.md
git-cliff --tag v1.2.3 --output CHANGELOG.mdConfigures cargo-release for automated version bumping.
Key settings:
- Disables crates.io publishing (GitHub releases only)
- Enables git-cliff integration for changelog
- Configures tag format (
v{{version}}) - Allows release from
mainbranch - Pushes tags automatically
Testing locally:
# Dry-run to see what would happen
cargo release patch --dry-run
# See verbose output
cargo release patch --dry-run --verboseVersion bumps are determined by commit types following Conventional Emoji Commits:
| Commit Type | Version Bump | Example |
|---|---|---|
🚨 BREAKING CHANGE or BREAKING CHANGE: in body |
MAJOR (1.0.0 → 2.0.0) | Breaking API changes |
! before : in subject |
MAJOR (1.0.0 → 2.0.0) | feat!: redesign API |
✨ feat |
MINOR (1.0.0 → 1.1.0) | New features |
🩹 fix, ⚡️ perf |
PATCH (1.0.0 → 1.0.1) | Bug fixes, performance |
| Other types (docs, style, refactor, test, build, ci, chore) | No bump | Non-releasable changes |
Priority: When multiple commits exist, use the highest priority: MAJOR > MINOR > PATCH
Examples:
# PATCH release (bug fix)
git commit -m "🩹 fix: resolve memory leak in client"
cargo release patch --execute
# MINOR release (new feature)
git commit -m "✨ feat: add deployment assistant support"
cargo release minor --execute
# MAJOR release (breaking change)
git commit -m "🚨 BREAKING CHANGE: remove deprecated API
Old endpoints /api/v1/* are no longer supported.
Migrate to /api/v2/* endpoints."
cargo release major --executeCheck:
- Does the code compile locally?
cargo build --release - Are all tests passing?
cargo test --workspace - Check the Actions logs on GitHub for specific errors
Fix:
- Fix the issue locally
- Create a new patch version
- Push the new tag
Check:
- Are commits following Conventional Emoji Commits format?
- Test locally:
git-cliff --unreleased
Fix:
- Verify
cliff.tomlconfiguration - Check commit messages:
git log --oneline - Ensure commits have proper emoji/type prefixes
Check:
git statusFix:
# Commit or stash uncommitted changes
git add .
git commit -m "🔧 build: prepare for release"
# Or stash
git stashCheck:
- Was it an annotated tag?
git tag -v v1.2.3 - Does the tag match
v*pattern? - Check Actions tab on GitHub
Fix:
- Delete and recreate as annotated tag:
git tag -d v1.2.3 git push origin :refs/tags/v1.2.3 git tag -a v1.2.3 -m "Release v1.2.3" git push origin v1.2.3
Before pushing (tag is local only):
git tag -d v1.2.3
git reset --hard HEAD~1 # If you made a commitAfter pushing (tag is on GitHub):
# 1. Delete GitHub Release
gh release delete v1.2.3 --yes
# 2. Delete remote tag
git push origin :refs/tags/v1.2.3
# 3. Delete local tag
git tag -d v1.2.3
# 4. Revert release commit (if needed)
git revert HEAD
git push
# 5. Fix issues and create new release-
Run full checks locally:
cargo fmt && \ cargo check --workspace --all-features && \ cargo clippy --workspace --all-features -- -D warnings && \ cargo test --workspace --all-features
-
Review commits since last release:
git log $(git describe --tags --abbrev=0)..HEAD --oneline -
Preview changelog:
git-cliff --unreleased
-
Dry-run release:
cargo release patch --dry-run
- Follow Conventional Emoji Commits format strictly
- Use descriptive commit messages
- Include scope when helpful:
feat(cli):vsfeat: - Document breaking changes thoroughly
- Reference issue numbers:
Fixes #42
- Patch releases: Bug fixes, performance improvements (as needed)
- Minor releases: New features, non-breaking changes (monthly or as needed)
- Major releases: Breaking changes (carefully planned, with migration guides)
- Never commit secrets to the repository
- GitHub Actions uses
GITHUB_TOKENwith minimal permissions - Release artifacts include SHA256 checksums for verification
- Consider GPG signing tags for critical releases
- PR Status: Check the PR page for green checkmarks
- Actions Tab: https://github.com/codekiln/langstar/actions
- Release Page: https://github.com/codekiln/langstar/releases
After creating a release:
-
Check GitHub Release page for new release
-
Download and verify artifacts:
# Download release wget https://github.com/codekiln/langstar/releases/download/v1.2.3/langstar-1.2.3-x86_64-linux-musl.tar.gz wget https://github.com/codekiln/langstar/releases/download/v1.2.3/langstar-1.2.3-x86_64-linux-musl.tar.gz.sha256 # Verify checksum sha256sum -c langstar-1.2.3-x86_64-linux-musl.tar.gz.sha256
-
Test the binary:
tar xzf langstar-1.2.3-x86_64-linux-musl.tar.gz ./langstar --version ./langstar --help
This project uses cargo-nextest for test execution, providing:
- Faster execution: Up to 3x faster than
cargo testthrough better parallelization - JUnit XML output: Native test result reporting with per-test timing data
- Better CI integration: Test retries, flaky test detection, and partitioning support
- Process-level isolation: Each test runs in its own process (not just thread)
# Install cargo-nextest (pre-installed in devcontainer)
cargo install cargo-nextest
# Run unit tests (fast, parallel)
cargo nextest run --all-features --workspace --lib
# Run unit tests with CI profile (generates JUnit XML)
cargo nextest run --profile ci --all-features --workspace --lib
# Run integration tests
cargo nextest run --profile integration -p langstar --features integration-tests
# List tests without running
cargo nextest list --all-features --workspaceConfigured in .config/nextest.toml:
| Profile | Use Case | Timeout | Output |
|---|---|---|---|
default |
Local development | 60s | Failed tests only |
ci |
CI unit tests | 60s | JUnit XML at target/nextest/ci/junit-ci.xml |
integration |
Integration tests | 180s | JUnit XML at target/nextest/integration/junit-integration.xml |
In pull requests:
- PR Comments: Test results with execution times appear as comments
- GitHub Checks: Dedicated check runs for unit and integration test results
- Artifacts: JUnit XML files stored for 90 days for historical analysis
After CI runs, check the PR comment for "Test Results" which shows:
- Per-test execution time
- Tests sorted by duration
- Trend comparison across runs