Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 226 additions & 0 deletions .github/workflows/release-binaries.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
name: Release Binaries

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to build and release (e.g. v0.6.0)'
required: true
type: string

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
CARGO_NET_GIT_FETCH_WITH_CLI: "true"

concurrency:
group: release-binaries-${{ inputs.tag || github.ref_name }}
cancel-in-progress: false

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:
EVENT_NAME: ${{ github.event_name }}
INPUT_TAG: ${{ inputs.tag }}
run: |
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
VERSION="$INPUT_TAG"
elif [[ "$GITHUB_REF" == refs/tags/* ]]; then
VERSION="${GITHUB_REF#refs/tags/}"
else
echo "::error::Unsupported release trigger: $EVENT_NAME $GITHUB_REF"
exit 1
fi
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.+-]+)?$ ]]; then
echo "::error::Invalid tag format: '$VERSION' (expected e.g. v1.2.3 or v1.2.3-rc.1)"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag) || github.ref }}
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@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
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.version }}
TARGET: ${{ matrix.target }}
run: ./scripts/release-package.sh "$TAG" "$TARGET"

- name: Upload build artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
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@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: release-assets
path: release-assets/

release:
name: Create Release
needs: sign
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Resolve tag
id: tag
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_TAG: ${{ inputs.tag }}
run: |
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
TAG="$INPUT_TAG"
elif [[ "$GITHUB_REF" == refs/tags/* ]]; then
TAG="${GITHUB_REF#refs/tags/}"
else
echo "::error::Unsupported release trigger: $EVENT_NAME $GITHUB_REF"
exit 1
fi
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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
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 gh release view "${TAG}" >/dev/null 2>&1; then
echo "::error::Release ${TAG} already exists; delete the existing draft/release before rerunning"
exit 1
fi

gh release create "${TAG}" "${create_args[@]}"
gh release upload "${TAG}" release-assets/*
gh release edit "${TAG}" "${edit_args[@]}"
Loading