name: Release fallow-cov-protocol
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Existing tag to re-run the release for (e.g. v0.3.0)"
required: true
type: string
permissions: {}
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
CARGO_TERM_COLOR: always
jobs:
publish:
name: Publish to crates.io
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
- name: Extract version from tag
id: version
shell: bash
env:
REF: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
run: |
set -euo pipefail
# Normalise "refs/tags/v0.3.0" and "v0.3.0" to "0.3.0".
TAG="${REF#refs/tags/}"
VERSION="${TAG#v}"
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
echo "::error::tag '${TAG}' does not match v<semver> format"
exit 1
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
- name: Verify tag matches Cargo.toml version
shell: bash
env:
TAG_VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
PKG_VERSION=$(grep -m1 '^version' Cargo.toml | cut -d'"' -f2)
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
echo "::error::tag v${TAG_VERSION} does not match Cargo.toml version ${PKG_VERSION}"
exit 1
fi
echo "Tag and manifest agree on ${PKG_VERSION}"
- name: Verify PROTOCOL_VERSION matches Cargo.toml version
shell: bash
env:
TAG_VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
# The protocol-reviewer agent guards this invariant at PR time;
# the release workflow re-checks so a tag pushed against a stale
# lib.rs cannot reach crates.io.
LIB_VERSION=$(grep -oE 'PROTOCOL_VERSION: &str = "[^"]+"' src/lib.rs | cut -d'"' -f2)
if [ "$LIB_VERSION" != "$TAG_VERSION" ]; then
echo "::error::PROTOCOL_VERSION \"${LIB_VERSION}\" in src/lib.rs does not match tag v${TAG_VERSION}"
exit 1
fi
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 with:
key: release
- name: Run tests
run: cargo test --all-targets
- name: Clippy
run: cargo clippy --all-targets -- -D warnings
- name: Format check
run: cargo fmt --all -- --check
- name: Cargo publish dry-run
run: cargo publish --dry-run
- name: Check if version is already published on crates.io
id: check_published
shell: bash
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
STATUS=$(curl -sS -o /dev/null -w "%{http_code}" \
"https://crates.io/api/v1/crates/fallow-cov-protocol/${VERSION}")
if [ "$STATUS" = "200" ]; then
echo "already=true" >> "$GITHUB_OUTPUT"
echo "::notice::fallow-cov-protocol ${VERSION} is already on crates.io; skipping cargo publish"
else
echo "already=false" >> "$GITHUB_OUTPUT"
fi
- name: Publish to crates.io
if: steps.check_published.outputs.already != 'true'
shell: bash
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish
- name: Extract CHANGELOG slice for this version
id: changelog
shell: bash
env:
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ steps.version.outputs.tag }}
run: |
set -euo pipefail
# CHANGELOG uses `## [X.Y.Z] - YYYY-MM-DD` headers (Keep a
# Changelog). Print lines from the matching section up to (but
# not including) the next `## ` header.
awk -v ver="${VERSION}" '
/^## / {
if (found) exit
if ($0 ~ "^## \\["ver"\\]") { found=1; next }
}
found { print }
' CHANGELOG.md > /tmp/changelog-slice.md
{
echo "title=fallow-cov-protocol ${TAG}"
echo "body<<CHANGELOG_EOF"
cat /tmp/changelog-slice.md
echo ""
echo "---"
echo ""
echo "Install: \`cargo add fallow-cov-protocol@${VERSION}\`"
echo "CHANGELOG_EOF"
} >> "$GITHUB_OUTPUT"
- name: Create GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda with:
name: ${{ steps.changelog.outputs.title }}
tag_name: ${{ steps.version.outputs.tag }}
body: ${{ steps.changelog.outputs.body }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}