name: Release
on:
workflow_dispatch:
inputs:
version:
description: "Version to release (e.g. 0.2.0 — must already be set in Cargo.toml)"
required: true
type: string
permissions:
contents: write
env:
RUSTFLAGS: -Dwarnings
jobs:
preflight:
name: preflight
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- run: cargo fmt --all -- --check
- run: cargo clippy --features full,serde -- -Dclippy::all
- run: cargo test --features full,serde
verify-version:
name: verify version
runs-on: ubuntu-latest
needs: preflight
steps:
- uses: actions/checkout@v4
- name: Check Cargo.toml version matches input
run: |
CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d['packages'][0]['version'])")
INPUT_VERSION="${{ inputs.version }}"
echo "Cargo.toml: $CARGO_VERSION"
echo "Input: $INPUT_VERSION"
if [ "$CARGO_VERSION" != "$INPUT_VERSION" ]; then
echo "::error::Version mismatch — bump Cargo.toml to $INPUT_VERSION in a PR first."
exit 1
fi
tag:
name: tag
runs-on: ubuntu-latest
needs: verify-version
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Create and push tag
id: tag
run: |
TAG="v${{ inputs.version }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists, skipping."
else
git tag "$TAG"
git push origin "$TAG"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
publish:
name: publish
runs-on: ubuntu-latest
needs: tag
environment: crates-io
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.tag.outputs.tag }}
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: cargo publish
run: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
github-release:
name: github-release
runs-on: ubuntu-latest
needs: [tag, publish]
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.tag.outputs.tag }}
fetch-depth: 0
- uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.tag.outputs.tag }}
generate_release_notes: true
make_latest: true