name: Release
permissions:
contents: write
id-token: write
on:
push:
tags:
- v[0-9]+.*
workflow_dispatch:
inputs:
version:
description: 'Version to validate (e.g. 0.20.0); publish steps are always skipped in dispatch mode'
required: false
default: ''
env:
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' }}
jobs:
check-version:
name: Preflight (version + changelog consistency)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check tag, Cargo, npm, and CHANGELOG versions agree
run: |
set -eu
CARGO_VERSION="$(grep -m1 '^version' Cargo.toml | sed 's/version = "\(.*\)"/\1/')"
WASM_VERSION="$(grep -m1 '"version"' src/wasm/js/package.json | sed 's/.*"version": "\(.*\)".*/\1/')"
echo "cargo=${CARGO_VERSION} wasm=${WASM_VERSION}"
if [ "${DRY_RUN}" = "true" ]; then
TARGET_VERSION="${{ github.event.inputs.version }}"
if [ -z "${TARGET_VERSION}" ]; then
TARGET_VERSION="${CARGO_VERSION}"
fi
else
TARGET_VERSION="${GITHUB_REF_NAME#v}"
fi
echo "target=${TARGET_VERSION}"
if [ "${CARGO_VERSION}" != "${TARGET_VERSION}" ]; then
echo "::error::Cargo.toml version ${CARGO_VERSION} != tag/dispatch ${TARGET_VERSION}" && exit 1
fi
if [ "${WASM_VERSION}" != "${TARGET_VERSION}" ]; then
echo "::error::src/wasm/js/package.json version ${WASM_VERSION} != ${TARGET_VERSION}" && exit 1
fi
if ! grep -qE "^## v${TARGET_VERSION} - [0-9]{4}-[0-9]{2}-[0-9]{2}$" CHANGELOG.md; then
echo "::error::CHANGELOG.md has no '## v${TARGET_VERSION} - YYYY-MM-DD' section" && exit 1
fi
echo "versions consistent: v${TARGET_VERSION}"
release-test:
name: Build & Test (release gate)
needs: check-version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- name: Run tests
run: cargo test --all-features
release-format-check:
name: Format + README check
needs: check-version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run format check
run: cargo fmt --check
- name: Run cargo-readme check
run: cargo install cargo-readme --locked && cargo readme > TMP_README.md && diff -b TMP_README.md README.md
security-audit:
name: Security audit
needs: check-version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install cargo-audit
run: cargo install cargo-audit --locked
- name: Generate lockfile
run: cargo generate-lockfile
- name: Run cargo audit
run: cargo audit
cargo-publish:
name: Publish to crates.io
needs: [release-test, release-format-check, security-audit]
if: needs.release-test.result == 'success' && needs.release-format-check.result == 'success' && needs.security-audit.result == 'success' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Publish to crates.io
continue-on-error: true
id: crates_publish
run: >
cargo publish
--all-features
--verbose
--token ${{ secrets.CARGO_REGISTRY_TOKEN }}
- name: Fail loudly if publication did not succeed on a tag push
if: steps.crates_publish.outcome == 'failure'
run: |
echo "::error::cargo publish failed"
echo "::error::If this crate version was already published to crates.io in a previous attempt of this tag, re-run this failed job only (Re-run failed jobs). Do NOT re-push the tag: crates.io versions are immutable and re-publishing always fails."
exit 1
npm-publish:
name: Publish @bgpkit/parser to npm
needs: [release-test, release-format-check, security-audit]
if: needs.release-test.result == 'success' && needs.release-format-check.result == 'success' && needs.security-audit.result == 'success' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: https://registry.npmjs.org
scope: '@bgpkit'
- name: Install wasm-pack
run: cargo install wasm-pack --locked
- name: Add wasm target
run: rustup target add wasm32-unknown-unknown
- name: Build WASM npm package
run: bash src/wasm/build.sh
- name: Upgrade npm for trusted publishing
run: npm install -g npm@latest
- name: Publish @bgpkit/parser to npm
continue-on-error: true
id: npm_publish
working-directory: pkg
run: npm publish --access public --provenance
- name: Fail loudly if publication did not succeed on a tag push
if: steps.npm_publish.outcome == 'failure'
run: |
echo "::error::npm publish failed"
echo "::error::If this version was already published to npm in a previous attempt of this tag, re-run this failed job only (Re-run failed jobs). Do NOT re-push the tag: npm versions are immutable."
exit 1
create-release:
name: Create GitHub release
needs: [cargo-publish, npm-publish]
if: always() && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Confirm both registries succeeded
run: |
if [ "${{ needs.cargo-publish.result }}" != "success" ] || [ "${{ needs.npm-publish.result }}" != "success" ]; then
echo "::error::Refusing to create GitHub release: cargo-publish=${{ needs.cargo-publish.result }} npm-publish=${{ needs.npm-publish.result }}"
exit 1
fi
- uses: taiki-e/create-gh-release-action@v1
with:
changelog: CHANGELOG.md
token: ${{ secrets.GITHUB_TOKEN }}
upload-assets:
name: Upload binary assets
needs: create-release
if: github.event_name == 'push'
strategy:
matrix:
include:
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: universal-apple-darwin
os: macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: taiki-e/upload-rust-binary-action@v1
with:
bin: bgpkit-parser
checksum: sha256
features: cli
target: ${{ matrix.target }}
token: ${{ secrets.GITHUB_TOKEN }}