name: build-release
on:
workflow_dispatch:
inputs:
tag:
description: 'Release tag to build (vX.Y.Z); must match the ref this run is on'
type: string
required: true
concurrency:
group: build-release
cancel-in-progress: false
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
check:
name: check tag
runs-on: ubuntu-22.04
steps:
- env:
TAG: ${{ inputs.tag }}
REF: ${{ github.ref }}
run: |
set -euo pipefail
if [ "$REF" != "refs/tags/$TAG" ]; then
echo "this run is on $REF but was asked to build $TAG; dispatch with --ref $TAG" >&2
exit 1
fi
build:
name: build (${{ matrix.target }})
needs: [check]
runs-on: ${{ matrix.runner }}
permissions:
contents: read
id-token: write
attestations: write
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
runner: macos-14
- target: x86_64-unknown-linux-gnu
runner: ubuntu-22.04
- target: aarch64-unknown-linux-gnu
runner: ubuntu-22.04-arm
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with:
ref: ${{ inputs.tag }}
- name: Read pinned toolchain
id: toolchain
run: echo "channel=$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)" >> "$GITHUB_OUTPUT"
- uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de with:
toolchain: ${{ steps.toolchain.outputs.channel }}
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 with:
key: release-${{ matrix.target }}
- name: Install mold linker
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y mold
- name: Configure mold as the Linux linker
if: runner.os == 'Linux'
run: echo "RUSTFLAGS=-C link-arg=-fuse-ld=mold" >> "$GITHUB_ENV"
- name: Build
env:
TARGET: ${{ matrix.target }}
run: cargo build --profile release-ci --locked --target "$TARGET"
- name: Strip binary
env:
TARGET: ${{ matrix.target }}
run: strip "target/$TARGET/release-ci/gbd"
- name: Package tarball + sha256
env:
TAG: ${{ inputs.tag }}
TARGET: ${{ matrix.target }}
run: |
TARBALL="gbd-${TAG}-${TARGET}.tar.gz"
tar czf "${TARBALL}" -C "target/${TARGET}/release-ci" gbd
shasum -a 256 "${TARBALL}" > "${TARBALL}.sha256"
- name: Attest build provenance
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 with:
subject-path: gbd-${{ inputs.tag }}-${{ matrix.target }}.tar.gz
- name: Upload release artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with:
name: release-${{ inputs.tag }}-${{ matrix.target }}
path: |
gbd-${{ inputs.tag }}-${{ matrix.target }}.tar.gz
gbd-${{ inputs.tag }}-${{ matrix.target }}.tar.gz.sha256
if-no-files-found: error
retention-days: 7
smoke:
name: smoke (ubuntu)
needs: [build]
runs-on: ubuntu-22.04
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with:
pattern: release-${{ inputs.tag }}-x86_64-unknown-linux-gnu
path: dist/
merge-multiple: true
- name: Smoke-test the Linux x86_64 binary
env:
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
TARBALL="dist/gbd-${TAG}-x86_64-unknown-linux-gnu.tar.gz"
tar -xzf "$TARBALL" -C /tmp
BIN=/tmp/gbd
chmod +x "$BIN"
"$BIN" --version
"$BIN" ping
release:
name: release
needs: [build, smoke]
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with:
ref: ${{ inputs.tag }}
fetch-depth: 0
- name: Download all release artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with:
path: dist
pattern: release-*
merge-multiple: true
- name: Aggregate SHA256SUMS
working-directory: dist
run: |
cat gbd-*.tar.gz.sha256 > SHA256SUMS
cat SHA256SUMS
- name: Sign release assets (minisign)
env:
MINISIGN_KEY: ${{ secrets.MINISIGN_KEY }}
run: |
set -euo pipefail
if [ -z "$MINISIGN_KEY" ]; then
echo "MINISIGN_KEY is not set; Cargo.toml declares a pubkey, so an unsigned release would be uninstallable with cargo binstall" >&2
exit 1
fi
sudo apt-get update -qq
sudo apt-get install -y -qq minisign >/dev/null \
|| { echo "minisign is not installable on this runner image; the release job needs ubuntu-24.04 or newer" >&2; exit 1; }
minisign -v
KEY="$RUNNER_TEMP/minisign.key"
umask 077
printf '%s\n' "$MINISIGN_KEY" > "$KEY"
PUBKEY=$(sed -n 's/^pubkey = "\(.*\)"/\1/p' Cargo.toml)
for f in dist/gbd-*.tar.gz dist/SHA256SUMS; do
case "$f" in
*.tar.gz) sig="$f.sig" ;;
*) sig="$f.minisig" ;;
esac
minisign -S -W -s "$KEY" -x "$sig" -m "$f"
minisign -V -q -P "$PUBKEY" -x "$sig" -m "$f"
echo "signed and verified: $f"
done
rm -f "$KEY"
- name: Install git-cliff
uses: taiki-e/install-action@7623a79cdfecb99d681017af368ca353d9f49bb5 with:
tool: git-cliff
- name: Generate release notes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
# Notes must span since the last PUBLISHED release, not the last
# tag: if a build leg failed after `version` pushed a tag, that
# tag exists but has no release.
PREV=$(gh release list --limit 1 --json tagName --jq '.[0].tagName // empty' || true)
if [ -n "$PREV" ]; then
git cliff "${PREV}..${TAG}" --tag "$TAG" -o RELEASE_NOTES.md
else
git cliff --latest --tag "$TAG" -o RELEASE_NOTES.md
fi
- name: Create or update GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
if gh release view "$TAG" >/dev/null 2>&1; then
gh release upload "$TAG" \
dist/gbd-*.tar.gz \
dist/gbd-*.tar.gz.sig \
dist/SHA256SUMS \
dist/SHA256SUMS.minisig \
--clobber
else
gh release create "$TAG" \
dist/gbd-*.tar.gz \
dist/gbd-*.tar.gz.sig \
dist/SHA256SUMS \
dist/SHA256SUMS.minisig \
--title "$TAG" \
--notes-file RELEASE_NOTES.md
fi
publish:
name: crates.io
needs: [release]
runs-on: ubuntu-22.04
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 if: env.CARGO_REGISTRY_TOKEN != ''
with:
ref: ${{ inputs.tag }}
- name: Read pinned toolchain
if: env.CARGO_REGISTRY_TOKEN != ''
id: toolchain
run: echo "channel=$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)" >> "$GITHUB_OUTPUT"
- uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de if: env.CARGO_REGISTRY_TOKEN != ''
with:
toolchain: ${{ steps.toolchain.outputs.channel }}
- name: Publish
if: env.CARGO_REGISTRY_TOKEN != ''
run: cargo publish --locked
- name: Skipped
if: env.CARGO_REGISTRY_TOKEN == ''
run: echo "CARGO_REGISTRY_TOKEN is not set; not publishing to crates.io"