Skip to content

feat: migrate to stable Rust toolchain #16

feat: migrate to stable Rust toolchain

feat: migrate to stable Rust toolchain #16

Workflow file for this run

name: CI/CD Pipeline
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
inputs:
bump_type:
description: 'Version bump type'
required: true
type: choice
options:
- patch
- minor
- major
description:
description: 'Release description (optional)'
required: false
type: string
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
jobs:
# REQUIRED CI CHECKS - All must pass before release
# These jobs ensure code quality and tests pass before any release
# Linting and formatting
lint:
name: Lint and Format Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run Clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Check file size limit
run: python3 scripts/check_file_size.py
# Test on multiple OS
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run tests
run: cargo test --all-features --verbose
- name: Run doc tests
run: cargo test --doc --verbose
# Test coverage - ensures we maintain high test coverage
coverage:
name: Test Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
# Install tarpaulin using pre-built binary (avoids compilation issues)
- name: Install cargo-tarpaulin
run: |
curl -sL https://github.com/xd009642/tarpaulin/releases/download/0.31.5/cargo-tarpaulin-x86_64-unknown-linux-gnu.tar.gz | tar xz
chmod +x cargo-tarpaulin
sudo mv cargo-tarpaulin /usr/local/bin/
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-coverage-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-coverage-
- name: Run coverage
run: cargo-tarpaulin --out Xml --output-dir coverage
- name: Check coverage threshold
run: |
# Extract coverage percentage from output
COVERAGE=$(cargo-tarpaulin --out Stdout 2>&1 | grep -oP '\d+\.\d+(?=% coverage)' | tail -1)
echo "Coverage: $COVERAGE%"
# Check if coverage meets threshold (90%)
if (( $(echo "$COVERAGE < 90" | bc -l) )); then
echo "::error::Coverage ($COVERAGE%) is below 90% threshold"
exit 1
fi
echo "Coverage check passed: $COVERAGE% >= 90%"
# Build package - only runs if lint, test, and coverage pass
build:
name: Build Package
runs-on: ubuntu-latest
needs: [lint, test, coverage]
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-build-
- name: Build release
run: cargo build --release --verbose
- name: Check package
run: cargo package --list
# Check for changelog fragments in PRs
changelog:
name: Changelog Fragment Check
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for changelog fragments
run: |
# Get list of fragment files (excluding README and template)
FRAGMENTS=$(find changelog.d -name "*.md" ! -name "README.md" 2>/dev/null | wc -l)
# Get changed files in PR
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
# Check if any source files changed (excluding docs and config)
SOURCE_CHANGED=$(echo "$CHANGED_FILES" | grep -E "^(src/|tests/|scripts/|examples/)" | wc -l)
if [ "$SOURCE_CHANGED" -gt 0 ] && [ "$FRAGMENTS" -eq 0 ]; then
echo "::warning::No changelog fragment found. Please add a changelog entry in changelog.d/"
echo ""
echo "To create a changelog fragment:"
echo " Create a new .md file in changelog.d/ with your changes"
echo ""
echo "See changelog.d/README.md for more information."
# Note: This is a warning, not a failure, to allow flexibility
# Change 'exit 0' to 'exit 1' to make it required
exit 0
fi
echo "Changelog check passed"
# Automatic release on push to main (if version changed)
auto-release:
name: Auto Release
needs: [lint, test, build]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Check if version changed
id: version_check
run: |
# Get current version from Cargo.toml
CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' Cargo.toml)
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
# Check if tag exists
if git rev-parse "v$CURRENT_VERSION" >/dev/null 2>&1; then
echo "Tag v$CURRENT_VERSION already exists, skipping release"
echo "should_release=false" >> $GITHUB_OUTPUT
else
echo "New version detected: $CURRENT_VERSION"
echo "should_release=true" >> $GITHUB_OUTPUT
fi
- name: Build release
if: steps.version_check.outputs.should_release == 'true'
run: cargo build --release
- name: Create GitHub Release
if: steps.version_check.outputs.should_release == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 scripts/create_github_release.py \
--version "${{ steps.version_check.outputs.current_version }}" \
--repository "${{ github.repository }}"
# Manual release via workflow_dispatch - only after CI passes
manual-release:
name: Manual Release
needs: [lint, test, build]
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Collect changelog fragments
run: |
# Check if there are any fragments to collect
FRAGMENTS=$(find changelog.d -name "*.md" ! -name "README.md" 2>/dev/null | wc -l)
if [ "$FRAGMENTS" -gt 0 ]; then
echo "Found $FRAGMENTS changelog fragment(s), collecting..."
python3 scripts/collect_changelog.py
else
echo "No changelog fragments found, skipping collection"
fi
- name: Version and commit
id: version
run: |
python3 scripts/version_and_commit.py \
--bump-type "${{ github.event.inputs.bump_type }}" \
--description "${{ github.event.inputs.description }}"
- name: Build release
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
run: cargo build --release
- name: Create GitHub Release
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 scripts/create_github_release.py \
--version "${{ steps.version.outputs.new_version }}" \
--repository "${{ github.repository }}"