name: release
on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
dry_run:
description: "Build + package npm without publishing (npm publish --dry-run)"
type: boolean
default: true
notes_tag:
description: "Only (re)generate the GitHub Release notes for this tag, then stop (e.g. v0.1.1)"
type: string
default: ""
permissions:
contents: write
jobs:
build:
name: ${{ matrix.target }}
if: github.event_name == 'push' || inputs.notes_tag == ''
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu, ext: "" }
- { os: ubuntu-latest, target: aarch64-unknown-linux-gnu, ext: "" }
- { os: macos-latest, target: x86_64-apple-darwin, ext: "" }
- { os: macos-latest, target: aarch64-apple-darwin, ext: "" }
- { os: windows-latest, target: x86_64-pc-windows-msvc, ext: ".exe" }
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Install cross-linker (aarch64 linux)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
- name: Build
run: cargo build --release --target ${{ matrix.target }}
- name: Upload binary artifact
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.target }}
path: target/${{ matrix.target }}/release/spm${{ matrix.ext }}
if-no-files-found: error
- name: Package
shell: bash
run: |
bin="target/${{ matrix.target }}/release/spm${{ matrix.ext }}"
out="spm-${{ matrix.target }}${{ matrix.ext }}"
cp "$bin" "$out"
echo "ASSET=$out" >> "$GITHUB_ENV"
- name: Attach to release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: ${{ env.ASSET }}
npm:
name: npm publish
needs: build
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
id-token: write env:
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Update npm for OIDC Trusted Publishing
run: npm install -g npm@11.12.1
- name: Download binary artifacts
uses: actions/download-artifact@v5
with:
path: artifacts
- name: Verify tag matches crate version
if: startsWith(github.ref, 'refs/tags/v')
run: |
set -euo pipefail
# Read the version only from the [package] section so unrelated
# `version =` keys elsewhere in Cargo.toml can't be picked up.
crate_version="$(awk '/^\[package\]/{p=1;next} /^\[/{p=0} p && /^version[[:space:]]*=/{gsub(/.*=[[:space:]]*"|".*/, ""); print; exit}' Cargo.toml)"
tag_version="${GITHUB_REF_NAME#v}"
echo "tag=$tag_version crate=$crate_version"
if [ "$tag_version" != "$crate_version" ]; then
echo "::error::Tag $GITHUB_REF_NAME does not match crate version $crate_version"
exit 1
fi
- name: Generate npm packages
run: node npm/build.mjs --bin-dir artifacts
- name: Publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
publish() {
if [ "${DRY_RUN}" = "true" ]; then
npm publish "$1" --access public --dry-run
else
npm publish "$1" --access public
fi
}
# Platform packages first, then the root that depends on them.
for dir in npm/dist/@camunda8/spm-*; do
echo "::group::publish $dir"; publish "$dir"; echo "::endgroup::"
done
echo "::group::publish npm/dist/@camunda8/spm"
publish "npm/dist/@camunda8/spm"
echo "::endgroup::"
release-notes:
name: release notes
needs: [build]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write if: |
always() &&
((github.event_name == 'push' && needs.build.result == 'success') ||
(github.event_name == 'workflow_dispatch' && inputs.notes_tag != ''))
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 - name: Update release notes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ (github.event_name == 'push' && github.ref_name) || inputs.notes_tag }}
run: scripts/update-release-notes.sh "$TAG" --repo "$GITHUB_REPOSITORY" --include-commits