Skip to content
Closed
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
164 changes: 164 additions & 0 deletions .github/workflows/release-rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
name: release Rust binary to GitHub Releases

on:
release:
types:
- published

# This workflow runs in parallel with the existing PyPI workflow in
# release.yml. Both publish artifacts for the same release tag until
# the Phase 1.6 channel switch replaces the PyPI path entirely.
#
# macOS binaries are signed with a Developer ID Application
# certificate and notarized through Apple's notarytool so users
# can run them without Gatekeeper warnings. The job fails the
# release if any of APPLE_* secrets is missing or wrong.
#
# Required GitHub Actions secrets:
#
# APPLE_CERTIFICATE # base64 of the .p12 Developer ID cert
# APPLE_CERTIFICATE_PASSWORD # password set when exporting the .p12
# APPLE_SIGNING_IDENTITY # e.g. "Developer ID Application: Mergify SAS (TEAMID)"
# APPLE_API_KEY # base64 of the App Store Connect .p8 API key
# APPLE_API_KEY_ID # 10-char key ID from App Store Connect
# APPLE_API_KEY_ISSUER # issuer UUID from App Store Connect

jobs:
linux-windows:
name: build ${{ matrix.target }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-24.04
- target: x86_64-unknown-linux-musl
os: ubuntu-24.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-24.04
- target: aarch64-unknown-linux-musl
os: ubuntu-24.04
- target: x86_64-pc-windows-msvc
os: windows-2025
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
fetch-tags: true

- name: Install Rust toolchain
run: |
rustup toolchain install stable --profile minimal
rustup default stable

- uses: taiki-e/upload-rust-binary-action@v1
with:
bin: mergify
target: ${{ matrix.target }}
archive: $bin-$tag-$target
tar: unix
zip: windows
checksum: sha256
token: ${{ secrets.GITHUB_TOKEN }}

macos:
name: build ${{ matrix.target }} (signed + notarized)
strategy:
fail-fast: false
matrix:
target:
- x86_64-apple-darwin
- aarch64-apple-darwin
runs-on: macos-15
permissions:
contents: write
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_KEY_ISSUER: ${{ secrets.APPLE_API_KEY_ISSUER }}
steps:
- uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
fetch-tags: true

- name: Install Rust toolchain
run: |
rustup toolchain install stable --profile minimal
rustup target add ${{ matrix.target }}
rustup default stable

- name: Build
run: cargo build --release --target ${{ matrix.target }}

- name: Import Developer ID certificate into a throwaway keychain
run: |
echo "$APPLE_CERTIFICATE" | base64 --decode -o "$RUNNER_TEMP/cert.p12"
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$RUNNER_TEMP/cert.p12" \
-k "$KEYCHAIN_PATH" \
-P "$APPLE_CERTIFICATE_PASSWORD" \
-T /usr/bin/codesign
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Prepend the build keychain so codesign searches it first,
# without dropping the runner's default keychains.
security list-keychains -d user -s \
"$KEYCHAIN_PATH" \
$(security list-keychains -d user | tr -d '"')

- name: Codesign binary (hardened runtime + timestamp)
run: |
codesign \
--force \
--options runtime \
--timestamp \
--sign "$APPLE_SIGNING_IDENTITY" \
"target/${{ matrix.target }}/release/mergify"
codesign --verify --strict --verbose=2 \
"target/${{ matrix.target }}/release/mergify"

- name: Notarize with App Store Connect API
run: |
KEY_DIR="$HOME/.appstoreconnect/private_keys"
mkdir -p "$KEY_DIR"
KEY_PATH="$KEY_DIR/AuthKey_${APPLE_API_KEY_ID}.p8"
echo "$APPLE_API_KEY" | base64 --decode > "$KEY_PATH"
# notarytool accepts zip, dmg, or pkg; wrap the bare binary
# in a zip so it can be submitted. Stapling isn't possible
# on a Mach-O, but online Gatekeeper checks still approve
# notarized binaries.
NOTARIZE_ZIP="$RUNNER_TEMP/mergify-notarize.zip"
ditto -c -k --keepParent \
"target/${{ matrix.target }}/release/mergify" \
"$NOTARIZE_ZIP"
xcrun notarytool submit "$NOTARIZE_ZIP" \
--key "$KEY_PATH" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_KEY_ISSUER" \
--wait

- name: Archive and upload to GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.event.release.tag_name }}
run: |
ARCHIVE="mergify-${TAG}-${{ matrix.target }}.tar.gz"
tar -C "target/${{ matrix.target }}/release" \
-czf "$RUNNER_TEMP/$ARCHIVE" mergify
(cd "$RUNNER_TEMP" && shasum -a 256 "$ARCHIVE" > "$ARCHIVE.sha256")
gh release upload "$TAG" \
"$RUNNER_TEMP/$ARCHIVE" \
"$RUNNER_TEMP/$ARCHIVE.sha256" \
--repo "${{ github.repository }}"
Loading