Skip to content

Release Binaries

Release Binaries #3

name: Release Binaries
on:
workflow_call:
inputs:
tag:
description: 'Tag to build and release (e.g. v0.6.0)'
required: true
type: string
workflow_dispatch:
inputs:
tag:
description: 'Tag to build and release (e.g. v0.6.0)'
required: true
type: string
concurrency:
group: release-binaries-${{ inputs.tag }}
cancel-in-progress: false
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
jobs:
build:
name: Build (${{ matrix.target }})
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: linux
runner: ${{ vars.RELEASE_RUNNER_LINUX_AMD64 || 'ubuntu-24.04' }}
- target: aarch64-unknown-linux-gnu
os: linux
runner: ${{ vars.RELEASE_RUNNER_LINUX_ARM64 || 'ubuntu-24.04-arm' }}
- target: aarch64-apple-darwin
os: macos
runner: ${{ vars.RELEASE_RUNNER_MACOS_ARM64 || 'macos-15' }}
steps:
- name: Resolve artifact version
id: version
env:
INPUT_TAG: ${{ inputs.tag }}
run: |
TAG="$INPUT_TAG"
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.+-]+)?$ ]]; then
echo "::error::Invalid tag format: '$TAG' (expected e.g. v1.2.3 or v1.2.3-rc.1)"
exit 1
fi
echo "asset_tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ format('refs/tags/{0}', inputs.tag) }}
submodules: recursive
- name: Install Linux system dependencies
if: matrix.os == 'linux'
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libclang-dev
- name: Install macOS system dependencies
if: matrix.os == 'macos'
run: |
brew install llvm pkg-config
LLVM_PREFIX="$(brew --prefix llvm)"
echo "${LLVM_PREFIX}/bin" >> "$GITHUB_PATH"
echo "LIBCLANG_PATH=${LLVM_PREFIX}/lib" >> "$GITHUB_ENV"
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4
- name: Validate runner target
env:
TARGET: ${{ matrix.target }}
run: |
HOST="$(rustc -vV | awk '/^host:/ {print $2}')"
if [[ "$HOST" != "$TARGET" ]]; then
echo "::error::Runner host '$HOST' does not match release target '$TARGET'"
exit 1
fi
- name: Install sccache
uses: taiki-e/install-action@a661f9d0b5f5222ce08202e6b2e9648d1713ca37 # untagged 2025-07-01 commit
with:
tool: sccache
- name: Configure sccache
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Build release binaries
run: |
cargo build --locked --release \
--bin arc-node-execution \
--bin arc-node-consensus \
--bin arc-snapshots
env:
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
- name: Package release assets
env:
TAG: ${{ steps.version.outputs.asset_tag }}
TARGET: ${{ matrix.target }}
run: ./scripts/release-package.sh "$TAG" "$TARGET"
- name: Upload build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-${{ matrix.target }}
path: release-assets/
sign:
name: Prepare Release Assets
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
env:
HAS_RELEASE_GPG_KEY: ${{ secrets.RELEASE_GPG_PRIVATE_KEY != '' }}
steps:
- name: Download build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: release-*
merge-multiple: true
path: release-assets/
- name: Import GPG key
if: env.HAS_RELEASE_GPG_KEY == 'true'
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
with:
gpg_private_key: ${{ secrets.RELEASE_GPG_PRIVATE_KEY }}
- name: GPG sign archives
if: env.HAS_RELEASE_GPG_KEY == 'true'
run: |
for archive in release-assets/*.tar.gz; do
gpg --batch --yes --detach-sign --armor --output "${archive}.asc" "${archive}"
done
- name: Upload release assets
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-assets
path: release-assets/
release:
name: Create Release
needs: sign
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Resolve tag
id: tag
env:
INPUT_TAG: ${{ inputs.tag }}
run: |
TAG="$INPUT_TAG"
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.+-]+)?$ ]]; then
echo "::error::Invalid tag format: '$TAG' (expected e.g. v1.2.3 or v1.2.3-rc.1)"
exit 1
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+- ]]; then
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
else
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
fi
- name: Download build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-assets
path: release-assets/
- name: Upload release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
TAG: ${{ steps.tag.outputs.tag }}
IS_PRERELEASE: ${{ steps.tag.outputs.is_prerelease }}
run: |
create_args=(--draft --generate-notes)
edit_args=(--draft=false)
if [[ "$IS_PRERELEASE" == "true" ]]; then
create_args+=(--prerelease)
edit_args+=(--prerelease=true)
fi
if release_is_draft="$(gh release view "${TAG}" --json isDraft --jq '.isDraft' 2>/dev/null)"; then
if [[ "$release_is_draft" != "true" ]]; then
echo "::error::Release ${TAG} is already published; refusing to replace public assets"
exit 1
fi
echo "::notice::Draft release ${TAG} already exists; replacing draft release assets"
gh release upload "${TAG}" release-assets/* --clobber
gh release edit "${TAG}" "${edit_args[@]}"
exit 0
fi
gh release create "${TAG}" "${create_args[@]}"
gh release upload "${TAG}" release-assets/*
gh release edit "${TAG}" "${edit_args[@]}"