name: Release
on:
push:
tags:
- "v*.*.*"
permissions:
contents: write
jobs:
verify:
name: Verify release build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@v2
- name: Check tag matches Cargo.toml version
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
CRATE_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n 1)"
if [ "$TAG_VERSION" != "$CRATE_VERSION" ]; then
echo "Tag version ($TAG_VERSION) does not match Cargo.toml version ($CRATE_VERSION)."
exit 1
fi
- name: Check formatting
run: cargo fmt --all --check
- name: Run clippy (deny warnings)
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run unit tests
run: cargo test --test unit
- name: Validate package contents
run: cargo package --locked
publish-crates-io:
name: Publish crate to crates.io
needs: verify
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@v2
- name: Publish crate (or skip when token missing)
run: |
if [ -z "${CARGO_REGISTRY_TOKEN}" ]; then
echo "CARGO_REGISTRY_TOKEN secret is not configured; skipping crates.io publish."
exit 0
fi
cargo publish --locked --token "${CARGO_REGISTRY_TOKEN}"
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
github-release:
name: Create GitHub release
needs: verify
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Create release with generated notes
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh release view "${GITHUB_REF_NAME}" > /dev/null 2>&1; then
echo "Release ${GITHUB_REF_NAME} already exists; skipping create."
exit 0
fi
gh release create "${GITHUB_REF_NAME}" \
--generate-notes \
--verify-tag