blob: 674fc076379330dc76c8b51f87bee4c226e10247 [file]
---
name: Release Binary
# DISCUSS: adding a whitelist for binaries to publish
on:
workflow_call:
inputs:
submodules:
description: Whether to checkout submodules. Use `true` to checkout submodules or `recursive` to recursively checkout submodules -- see https://github.com/actions/checkout
required: false
type: string
default: false
targets:
description: Whitelist of compilation targets to upload GitHub release binaries for. Must be a subset of supported targets.
type: string
default: |
aarch64-apple-darwin
x86_64-apple-darwin
x86_64-pc-windows-gnu
x86_64-unknown-linux-gnu
toolchain:
description: Rust toolchain specification
type: string
default: stable
disable-semantic-release-cargo:
description: Disable semantic-release-cargo in your release flow. Only takes effect if the action semantic-release config is used.
type: boolean
default: false
disable-semantic-release-git:
description: Disable @semantic-release/git in your release flow. Only takes effect if the action semantic-release config is used.
type: boolean
default: false
secrets:
cargo-registry-token:
description: API token for writing to your cargo registry
# Only allow one release workflow to execute at a time, since each release
# workflow uses shared resources (git tags, package registries)
concurrency:
group: ${{ github.workflow }}
env:
RUST_BACKTRACE: 1
jobs:
get-next-version:
uses: semantic-release-action/next-release-version/.github/workflows/next-release-version.yml@v4
configure-job-matrix:
name: Configure compilation targets
runs-on: ubuntu-latest
outputs:
compilation-matrix: ${{ steps.configure-job-matrix.outputs.compilation-matrix }}
test-matrix: ${{ steps.configure-job-matrix.outputs.test-matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4
- id: sanitize-input-targets
env:
raw_targets: ${{ inputs.targets }}
run: |
: sanitize targets input
targets="$(
jq \
--null-input \
--raw-input \
'[inputs] | map(splits("\\s+") | select(length > 0))' \
<<< "$raw_targets"
)"
# TODO: enforce that every target is in the whitelist of targets
# Array<string>
{
echo "targets<<EOF"
echo "$targets"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- id: get-binary-names
run: |
: detect binary names
# Array<{ binary_name: string, src_path: string }>
binary_manifest="$(
cargo metadata --no-deps --format-version 1 \
| jq '.packages | map(.targets[] | select(.kind[] | IN("bin")) | { binary_name: .name, src_path })'
)"
# Validate there are no binary-name collisions
# https://github.com/rust-lang/cargo/issues/6313
# Array<string>
name_collisions="$(
jq \
'group_by(.binary_name) | map(select(length > 1)[] | .binary_name) | unique' \
<<< "$binary_manifest"
)"
has_name_collisions="$(jq 'length > 0' <<< "$name_collisions")"
if [[ $has_name_collisions == true ]]; then
targets="$(jq --raw-output 'join(", ")' <<< "$name_collisions")"
echo "::error title={Binary name collision detected}::Refusing to process binary targets \"$targets\" due to name collisions"
exit 1
fi
binary_names="$(jq 'map({ binary_name })' <<< "$binary_manifest")"
# Array<{ binary_name: string }>
{
echo "binary-names<<EOF"
echo "$binary_names"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- id: configure-job-matrix
run: |
: configure compilation matrix
# Perform a cartesian product of target_triples and binary_names
compilation_matrix="$(
jq \
--null-input \
--argjson targets_whitelist "$targets_whitelist" \
--argjson target_triples "$target_triples" \
--argjson binary_names "$binary_names" \
'[$target_triples[] | select(.target | IN($targets_whitelist[])) + $binary_names[]]'
)"
# TODO: support running tests on aarch64-apple-darwin and x86_64-pc-windows-gnu. Cross was complaining
test_matrix="$(
jq \
--null-input \
--argjson targets_whitelist "$targets_whitelist" \
--argjson target_triples "$target_triples" \
'[$target_triples[] | select((.target | IN($targets_whitelist[])) and (.target != "aarch64-apple-darwin") and (.target != "x86_64-pc-windows-gnu"))]'
)"
{
echo "compilation-matrix<<EOF"
echo "$compilation_matrix"
echo "EOF"
echo "test-matrix<<EOF"
echo "$test_matrix"
echo "EOF"
} >> "$GITHUB_OUTPUT"
env:
targets_whitelist: ${{ steps.sanitize-input-targets.outputs.targets }}
binary_names: ${{ steps.get-binary-names.outputs.binary-names }}
target_triples: |
[
{
"target": "aarch64-apple-darwin",
"host": "macOS-latest",
"cross": true
},
{
"target": "aarch64-unknown-linux-gnu",
"host": "ubuntu-latest",
"cross": true
},
{
"target": "aarch64-unknown-linux-musl",
"host": "ubuntu-latest",
"cross": true
},
{
"target": "i686-unknown-linux-gnu",
"host": "ubuntu-latest",
"cross": true
},
{
"target": "i686-unknown-linux-musl",
"host": "ubuntu-latest",
"cross": true
},
{
"target": "x86_64-apple-darwin",
"host": "macOS-latest",
"cross": false
},
{
"target": "x86_64-pc-windows-gnu",
"host": "ubuntu-latest",
"cross": true
},
{
"target": "x86_64-unknown-linux-gnu",
"host": "ubuntu-latest",
"cross": false
},
{
"target": "x86_64-unknown-linux-musl",
"host": "ubuntu-latest",
"cross": false
}
]
test:
name: Test ${{ matrix.test.target }}
runs-on: ${{ matrix.test.host }}
needs:
- configure-job-matrix
strategy:
fail-fast: false
matrix:
test: ${{ fromJson(needs.configure-job-matrix.outputs.test-matrix) }}
steps:
###
# Phase 1: set up
- name: Checkout
uses: actions/checkout@v4
with:
submodules: ${{ inputs.submodules }}
- name: Install build inputs
if: runner.os == 'Linux' && !matrix.test.cross && endsWith(matrix.test.target, '-musl')
run: sudo apt install musl-tools
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.toolchain }}
target: ${{ matrix.test.target }}
- name: Install cross
uses: taiki-e/install-action@v2
if: matrix.test.cross
with:
tool: cross
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
###
# Phase 2: test
# Separate compilation from testing to accurately report the runtime of each
- env:
CARGO: ${{ matrix.test.cross && 'cross' || 'cargo' }}
run: |
: build tests
$CARGO test \
--target ${{ matrix.test.target }} \
--no-run \
--locked
- env:
CARGO: ${{ matrix.test.cross && 'cross' || 'cargo' }}
run: |
: tests
$CARGO test \
--target ${{ matrix.test.target }} \
--locked \
-- \
--nocapture
# Compile release artifacts
build-release:
name: (${{ matrix.build.binary_name }}, ${{ matrix.build.target }})
if: needs.get-next-version.outputs.new-release-published == 'true'
runs-on: ${{ matrix.build.host }}
env:
VERSION: ${{ needs.get-next-version.outputs.new-release-version }}
needs:
- get-next-version
- configure-job-matrix
strategy:
fail-fast: false
matrix:
build: ${{ fromJson(needs.configure-job-matrix.outputs.compilation-matrix) }}
steps:
###
# Phase 1: set up
- name: Checkout
uses: actions/checkout@v4
with:
submodules: ${{ inputs.submodules }}
- name: Install build inputs
if: runner.os == 'Linux' && !matrix.build.cross && endsWith(matrix.build.target, '-musl')
run: sudo apt install musl-tools
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.toolchain }}
target: ${{ matrix.build.target }}
- name: Install cross
uses: taiki-e/install-action@v2
if: matrix.build.cross
with:
tool: cross
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
###
# Phase 2: compile release binary
- name: Compile release binary
env:
CARGO: ${{ matrix.build.cross && 'cross' || 'cargo' }}
run: |
${{ env.CARGO }} build \
--verbose \
--release \
--bin ${{ matrix.build.binary_name }} \
--target ${{ matrix.build.target }}
###
# Phase 3: configure release artifacts
- name: Install semantic-release-cargo
uses: EricCrosson/install-github-release-binary@v2
with:
targets: semantic-release-cargo/semantic-release-cargo@v2
# NOTE: semantic-release-cargo currently implements the "lock-step"
# versioning strategy, where all crates in a workspace are bumped to the
# next version simultaneously.
- name: Prepare semantic-release for Rust
run: |
semantic-release-cargo \
--verbose --verbose --verbose \
prepare ${{ needs.get-next-version.outputs.new-release-version }}
- name: Create release artifacts ( non-Windows )
if: matrix.build.target != 'x86_64-pc-windows-gnu'
run: |
mkdir dist
cp \
target/${{ matrix.build.target }}/release/${{ matrix.build.binary_name }} \
dist/${{ matrix.build.binary_name }}-${{ matrix.build.target }}
- name: Create release artifacts ( Windows )
if: matrix.build.target == 'x86_64-pc-windows-gnu'
run: |
mkdir dist
cp \
target/${{ matrix.build.target }}/release/${{ matrix.build.binary_name }}.exe \
dist/${{ matrix.build.binary_name }}-${{ matrix.build.target }}
- name: Create binary checksum
run: |
shasum \
--algorithm 256 \
--binary ${{ matrix.build.binary_name }}-${{ matrix.build.target }} \
| tee ${{ matrix.build.binary_name }}-${{ matrix.build.target }}-SHA256SUM.txt
working-directory: ./dist
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.build.binary_name }}-${{ matrix.build.target }}
path: |
dist/${{ matrix.build.binary_name }}-${{ matrix.build.target }}
dist/${{ matrix.build.binary_name }}-${{ matrix.build.target }}-SHA256SUM.txt
if-no-files-found: error
retention-days: 1
release:
name: Release
if: needs.get-next-version.outputs.new-release-published == 'true'
runs-on: ubuntu-latest
needs:
- get-next-version
- configure-job-matrix
- test
- build-release
steps:
- name: Release
uses: casbin-rs/semantic-release-action-rust/semantic-release-binary@master
with:
toolchain: ${{ inputs.toolchain }}
compiled-targets: ${{ needs.configure-job-matrix.outputs.compilation-matrix }}
cargo-registry-token: ${{ secrets.cargo-registry-token }}
disable-semantic-release-cargo: ${{ inputs.disable-semantic-release-cargo }}
disable-semantic-release-git: ${{ inputs.disable-semantic-release-git }}
next-version: ${{ needs.get-next-version.outputs.new-release-version }}
submodules: ${{ inputs.submodules }}