name: CI/CD Pipeline
permissions:
contents: read
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-Dwarnings"
jobs:
quality-checks:
name: Quality Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- name: Format
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --all-targets --all-features
- name: Build
run: cargo build --release
- name: Test
run: cargo test --all-features
- name: Publish dry-run
run: cargo publish --dry-run
semantic-release:
name: Semantic Release
runs-on: ubuntu-latest
needs: [quality-checks]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
permissions:
contents: write
issues: write
pull-requests: write
id-token: write
outputs:
new-release-published: ${{ steps.semantic.outputs.new-release-published }}
new-release-version: ${{ steps.semantic.outputs.new-release-version }}
steps:
- name: Generate release token
uses: actions/create-github-app-token@v2
id: app-token
with:
app-id: ${{ secrets.RELEASER_APP_ID }}
private-key: ${{ secrets.RELEASER_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: ${{ github.event.repository.name }}
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install semantic-release plugins
run: npm install -g semantic-release @semantic-release/commit-analyzer @semantic-release/release-notes-generator @semantic-release/changelog @semantic-release/exec @semantic-release/git @semantic-release/github conventional-changelog-conventionalcommits
- name: Run semantic-release
id: semantic
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
npx semantic-release
if [ -f ".semantic-release-version" ]; then
VERSION=$(cat .semantic-release-version)
echo "new-release-published=true" >> $GITHUB_OUTPUT
echo "new-release-version=$VERSION" >> $GITHUB_OUTPUT
else
echo "new-release-published=false" >> $GITHUB_OUTPUT
fi
crates-publish:
name: Publish to crates.io
runs-on: ubuntu-latest
needs: [semantic-release]
if: needs.semantic-release.outputs.new-release-published == 'true'
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
ref: v${{ needs.semantic-release.outputs.new-release-version }}
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Verify version matches tag
run: |
TAG="${{ needs.semantic-release.outputs.new-release-version }}"
CARGO_VER=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
if [ "$TAG" != "$CARGO_VER" ]; then
echo "::error::Tag v$TAG does not match Cargo.toml version $CARGO_VER"
exit 1
fi
- name: Build
run: cargo build --release
- name: Test
run: cargo test --all-features
- name: Authenticate with crates.io (OIDC)
id: crates-auth
uses: rust-lang/crates-io-auth-action@v1
- name: Publish to crates.io
run: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }}
summary:
name: Pipeline Summary
runs-on: ubuntu-latest
permissions: {}
needs: [quality-checks, semantic-release, crates-publish]
if: always()
steps:
- name: Summary
run: |
echo "## Pipeline Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.quality-checks.result }}" == "success" ]]; then
echo "✅ Quality checks: Passed" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Quality checks: Failed" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.semantic-release.outputs.new-release-published }}" == "true" ]]; then
echo "🚀 New version: v${{ needs.semantic-release.outputs.new-release-version }}" >> $GITHUB_STEP_SUMMARY
else
echo "ℹ️ No new version released" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.crates-publish.result }}" == "success" ]]; then
echo "📦 Published to crates.io: structured-proxy v${{ needs.semantic-release.outputs.new-release-version }}" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.crates-publish.result }}" == "skipped" ]]; then
echo "⏭️ crates.io publish: Skipped (no new release)" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.crates-publish.result }}" == "failure" ]]; then
echo "❌ crates.io publish: Failed" >> $GITHUB_STEP_SUMMARY
fi