name: Release
on:
workflow_dispatch:
inputs:
version:
description: Release version without the leading v (e.g. 0.2.0)
required: true
type: string
permissions:
contents: write
concurrency:
group: release
cancel-in-progress: false
env:
CARGO_TERM_COLOR: always
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Normalize version
id: meta
shell: bash
env:
INPUT_VERSION: ${{ github.event.inputs.version }}
run: |
version="${INPUT_VERSION#v}"
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Invalid version '$version' (expected semver like 1.2.3)"
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=v$version" >> "$GITHUB_OUTPUT"
- name: Fail if tag already exists
shell: bash
env:
TAG: ${{ steps.meta.outputs.tag }}
run: |
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG already exists"
exit 1
fi
- name: Set crate version
shell: bash
env:
VERSION: ${{ steps.meta.outputs.version }}
run: |
# Replace only the package version (the first `version = "..."` line,
# in the [package] table) — dependency versions are left untouched.
awk -v v="$VERSION" '
!done && /^version = "/ { sub(/"[^"]*"/, "\"" v "\""); done = 1 }
{ print }
' Cargo.toml > Cargo.toml.tmp && mv Cargo.toml.tmp Cargo.toml
grep -m1 '^version = ' Cargo.toml
- name: Refresh lockfile
run: cargo check
- name: Run tests
run: cargo test
- name: Verify the package is publishable
run: cargo publish --dry-run --allow-dirty
- name: Commit, tag, and push
shell: bash
env:
VERSION: ${{ steps.meta.outputs.version }}
TAG: ${{ steps.meta.outputs.tag }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Cargo.toml Cargo.lock
git commit -m "release: $TAG"
git tag -a "$TAG" -m "Release $TAG"
git push origin HEAD
git push origin "$TAG"
- name: Publish to crates.io
run: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.meta.outputs.tag }}
name: ${{ steps.meta.outputs.tag }}
generate_release_notes: true