name: Release
on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
tag:
description: "Existing tag to build (e.g. v0.3.0)"
required: true
permissions:
contents: write
jobs:
build:
name: ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
os: macos-14
- target: x86_64-apple-darwin
os: macos-14
- target: x86_64-unknown-linux-gnu
os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-22.04
linker: gcc-aarch64-linux-gnu
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.tag || github.ref }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross linker
if: matrix.linker != ''
run: |
sudo apt-get update
sudo apt-get install -y ${{ matrix.linker }}
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
- name: Build
run: cargo build --release --locked --target ${{ matrix.target }}
- name: Package
id: package
shell: bash
run: |
set -euo pipefail
tag="${{ github.event.inputs.tag || github.ref_name }}"
name="essh-${tag}-${{ matrix.target }}"
mkdir -p "dist/${name}"
cp "target/${{ matrix.target }}/release/essh" "dist/${name}/"
cp README.md LICENSE "dist/${name}/" 2>/dev/null || cp README.md "dist/${name}/"
# Strip only when the host can: cross-built binaries need their own
# strip, and a failed strip must not fail the release.
strip "dist/${name}/essh" 2>/dev/null || true
tar -C dist -czf "${name}.tar.gz" "${name}"
shasum -a 256 "${name}.tar.gz" > "${name}.tar.gz.sha256"
echo "name=${name}" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.name }}
path: |
*.tar.gz
*.tar.gz.sha256
publish:
name: Publish release
needs: build
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.tag || github.ref }}
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Publish
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="${{ github.event.inputs.tag || github.ref_name }}"
# The tag's own annotation is the release note: it is written at
# tag time and cannot drift from what was tagged.
notes="$(git tag -l --format='%(contents)' "$tag")"
if [ -z "$notes" ]; then
notes="See CHANGELOG for ${tag}."
fi
if gh release view "$tag" >/dev/null 2>&1; then
gh release upload "$tag" artifacts/* --clobber
else
printf '%s\n' "$notes" > notes.md
gh release create "$tag" artifacts/* \
--title "$tag" --notes-file notes.md --verify-tag
fi