name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Tag to publish (e.g. v0.1.2). Must already exist."
required: true
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0"
jobs:
verify:
name: Verify pre-publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{ inputs.tag || github.ref }}
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- name: cargo fmt --check
run: cargo fmt --all -- --check
- name: cargo clippy
run: cargo clippy --release --all-targets -- -D warnings
- name: cargo test (release)
run: cargo test --release
- name: cargo build (release)
run: cargo build --release
publish:
name: cargo publish
needs: verify
runs-on: ubuntu-latest
environment: crates-io
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{ inputs.tag || github.ref }}
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Confirm tag matches Cargo.toml version
shell: bash
run: |
# Drop the leading `v` from the tag (refs/tags/v0.1.2 → 0.1.2).
ref="${{ inputs.tag || github.ref_name }}"
tag="${ref#v}"
ver=$(awk -F'"' '/^version *= *"/ { print $2; exit }' Cargo.toml)
if [ "$tag" != "$ver" ]; then
echo "::error::tag '$tag' does not match Cargo.toml version '$ver'"
exit 1
fi
echo "Publishing ktav $ver"
- name: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish
github-release:
name: GitHub Release
needs: publish
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v5
with:
ref: ${{ inputs.tag || github.ref }}
- name: Extract release notes from CHANGELOG.md
id: notes
shell: bash
run: |
ref="${{ inputs.tag || github.ref_name }}"
ver="${ref#v}"
# Pull the section between `## [<ver>]` and the next `## [`,
# stripping the leading marker and the trailing blank line.
notes=$(awk -v v="$ver" '
$0 ~ "^## \\[" v "\\]" { in_section = 1; next }
in_section && /^## \[/ { exit }
in_section { print }
' CHANGELOG.md)
# GitHub Actions multiline output via heredoc.
{
echo "body<<__END_NOTES__"
echo "$notes"
echo "__END_NOTES__"
} >> "$GITHUB_OUTPUT"
- name: Create / update GitHub Release
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ inputs.tag || github.ref_name }}
name: ${{ inputs.tag || github.ref_name }}
body: ${{ steps.notes.outputs.body }}
fail_on_unmatched_files: false