name: Release
permissions:
contents: write
on:
workflow_call:
inputs:
tag:
required: true
type: string
secrets:
CERTIFICATES_P12:
required: true
CERTIFICATES_P12_PASS:
required: true
APPLE_API_KEY_P8:
required: true
APPLE_API_KEY_ID:
required: true
APPLE_API_ISSUER_ID:
required: true
env:
CARGO_TERM_COLOR: always
jobs:
upload-assets:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
- target: aarch64-pc-windows-msvc
os: windows-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with:
ref: refs/tags/${{ inputs.tag }}
persist-credentials: false
- name: Import codesign certs
if: startsWith(matrix.os, 'macos')
uses: apple-actions/import-codesign-certs@5142e029c445c10ffc7149d172e540235a065466 with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASS }}
- name: Install cross-compilation tools
uses: taiki-e/setup-cross-toolchain-action@12b7ad4acfa95a1476779d6c06699b96ec1691f8 with:
target: ${{ matrix.target }}
- name: Upload binary (macOS signed)
if: startsWith(matrix.os, 'macos')
uses: taiki-e/upload-rust-binary-action@f0d45ae91ee7b8ee928de7a9d04d893a08bcbec6 with:
bin: communique
target: ${{ matrix.target }}
ref: refs/tags/${{ inputs.tag }}
token: ${{ secrets.GITHUB_TOKEN }}
codesign: "Developer ID Application: Jeffrey Dickey (4993Y37DX6)"
codesign_prefix: dev.jdx.
codesign_options: runtime
- name: Notarize macOS binary
if: startsWith(matrix.os, 'macos')
shell: bash
env:
APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
BINARIES: target/${{ matrix.target }}/release/communique
run: |
set -euo pipefail
if [ -z "$APPLE_API_KEY_P8" ] || [ -z "$APPLE_API_KEY_ID" ] || [ -z "$APPLE_API_ISSUER_ID" ]; then
echo "::error title=Notarization credentials missing::App Store Connect credentials are required for macOS releases."
exit 1
fi
# Outside the workspace, so no later step can sweep the key into an
# artifact, and removed even when notarization fails.
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
printf '%s' "$APPLE_API_KEY_P8" | base64 --decode > "$work/key.p8"
# notarytool takes a container, never a bare executable. Copies are
# fine: a Mach-O carries its signature inline, so the cdhash the
# ticket is keyed to is the one already shipped.
mkdir "$work/stage"
for binary in $BINARIES; do
cp "$binary" "$work/stage/"
done
# Apple rejects a binary without the hardened runtime or a secure
# timestamp. Naming a missing one here turns an opaque Invalid verdict
# into a diagnosis; Apple stays the authority on whether to accept.
for binary in $BINARIES; do
details="$(codesign --display --verbose=4 "$binary" 2>&1 || true)"
case "$details" in
*runtime*) ;;
*) echo "::warning title=No hardened runtime::$binary is signed without --options runtime, which Apple requires; expect an Invalid verdict below." ;;
esac
case "$details" in
*Timestamp=*) ;;
*) echo "::warning title=No secure timestamp::$binary carries no secure timestamp, which Apple requires; expect an Invalid verdict below." ;;
esac
done
ditto -c -k --norsrc --noextattr "$work/stage" "$work/submission.zip"
# `--wait` is not a gate on its own: it can return zero on an Invalid
# submission, so the reported status is what decides. plutil reads
# JSON and ships with macOS, which keeps this off jq.
result="$(xcrun notarytool submit "$work/submission.zip" \
--key "$work/key.p8" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_ISSUER_ID" \
--output-format json \
--timeout 30m \
--wait)"
echo "$result"
status="$(printf '%s' "$result" | plutil -extract status raw -o - -)"
id="$(printf '%s' "$result" | plutil -extract id raw -o - -)"
if [ "$status" != "Accepted" ]; then
echo "::error::notarization returned $status for submission $id"
xcrun notarytool log "$id" \
--key "$work/key.p8" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_ISSUER_ID" || true
exit 1
fi
echo "notarized: submission $id"
- name: Upload binary (non-macOS)
if: ${{ !startsWith(matrix.os, 'macos') }}
uses: taiki-e/upload-rust-binary-action@f0d45ae91ee7b8ee928de7a9d04d893a08bcbec6 with:
bin: communique
target: ${{ matrix.target }}
ref: refs/tags/${{ inputs.tag }}
token: ${{ secrets.GITHUB_TOKEN }}
packslip:
name: Publish the packslip
needs: upload-assets
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
steps:
- name: Download the release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ inputs.tag }}
REPO: ${{ github.repository }}
run: |
mkdir -p dist
gh release download "$TAG" --repo "$REPO" --dir dist --clobber \
--pattern 'communique-*.tar.gz' --pattern 'communique-*.zip'
ls -la dist
- name: Publish the CLI specification
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ inputs.tag }}
REPO: ${{ github.repository }}
run: |
mkdir -p bin
tar -xzf dist/communique-x86_64-unknown-linux-gnu.tar.gz -C bin
bin/communique usage > communique.usage.kdl
gh release upload "$TAG" communique.usage.kdl --repo "$REPO" --clobber
- uses: jdx/packslip@433c0c0f033e64c22f12433471768c255ef2bd93 with:
tag: ${{ inputs.tag }}
artifacts: dist/communique-*.tar.gz dist/communique-*.zip
bin: communique
resources: cli-spec/usage=asset:communique.usage.kdl