name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
permissions:
contents: write
jobs:
release:
strategy:
fail-fast: false
matrix:
platform:
- runs-on: ubuntu-24.04
target: x86_64-unknown-linux-musl
- runs-on: ubuntu-24.04
target: aarch64-unknown-linux-musl
- runs-on: ubuntu-24.04
target: armv7-unknown-linux-musleabihf
- runs-on: macos-latest
target: x86_64-apple-darwin
- runs-on: macos-latest
target: aarch64-apple-darwin
- runs-on: windows-latest
target: aarch64-pc-windows-msvc
- runs-on: windows-latest
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.platform.runs-on }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Generate changelog
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --output CHANGELOG.md
- name: Build
uses: houseabsolute/actions-rust-cross@v1
with:
target: ${{ matrix.platform.target }}
args: "--release"
- name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v7
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
- name: Sign binaries
shell: bash
run: |
target_dir="target/${{ matrix.platform.target }}/release"
binary_name="gitsnitch"
case "${{ matrix.platform.target }}" in
*windows*) binary_name="${binary_name}.exe" ;;
esac
gpg --batch --sign --detach-sign \
--local-user "${{ steps.import_gpg.outputs.keyid }}" \
"${target_dir}/${binary_name}"
mv \
"${target_dir}/${binary_name}.sig" \
"gitsnitch-${{ matrix.platform.target }}.sig"
- name: Release
uses: houseabsolute/actions-rust-release@v0.0.7
with:
changes-file: CHANGELOG.md
executable-name: gitsnitch
target: ${{ matrix.platform.target }}
action-gh-release-parameters: '{"token":"${{ secrets.GITHUB_TOKEN }}"}'
- name: Upload detached signature
uses: softprops/action-gh-release@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: gitsnitch-${{ matrix.platform.target }}.sig
release-macos-universal2:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Generate changelog
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --output CHANGELOG.md
- name: Build x86_64 macOS binary
uses: houseabsolute/actions-rust-cross@v1
with:
target: x86_64-apple-darwin
args: "--release"
- name: Build arm64 macOS binary
uses: houseabsolute/actions-rust-cross@v1
with:
target: aarch64-apple-darwin
args: "--release"
- name: Create universal2 binary
shell: bash
run: |
mkdir -p target/release
lipo -create \
target/x86_64-apple-darwin/release/gitsnitch \
target/aarch64-apple-darwin/release/gitsnitch \
-output target/release/gitsnitch
chmod 755 target/release/gitsnitch
- name: Import GPG key
id: import_gpg_universal2
uses: crazy-max/ghaction-import-gpg@v7
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
- name: Sign universal2 binary
shell: bash
run: |
gpg --batch --sign --detach-sign \
--local-user "${{ steps.import_gpg_universal2.outputs.keyid }}" \
target/release/gitsnitch
mv target/release/gitsnitch.sig gitsnitch-universal2-apple-darwin.sig
- name: Release universal2 archive
uses: houseabsolute/actions-rust-release@v0
with:
changes-file: CHANGELOG.md
executable-name: gitsnitch
archive-name: gitsnitch-macOS-universal2
action-gh-release-parameters: '{"token":"${{ secrets.GITHUB_TOKEN }}"}'
- name: Upload detached signature
uses: softprops/action-gh-release@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: gitsnitch-universal2-apple-darwin.sig
notify-homebrew-tap:
name: Notify homebrew-tap
runs-on: ubuntu-latest
needs:
- release
- release-macos-universal2
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Notify homebrew-tap
env:
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
VERSION: ${{ github.ref_name }}
run: |
set -euo pipefail
VERSION="${VERSION#v}"
REPOSITORY="${GITHUB_REPOSITORY#*/}"
fetch_sha() {
local name="$1"
local attempts="${SHA_RETRY_ATTEMPTS:-5}"
local delay_seconds="${SHA_RETRY_DELAY_SECONDS:-8}"
local i=1
local sha=""
local url="https://github.com/${GITHUB_REPOSITORY}/releases/download/v${VERSION}/${name}.sha256"
while [[ "$i" -le "$attempts" ]]; do
sha="$(curl -fsSL "$url" 2>/dev/null \
| tr -d '\r' \
| awk 'NR==1 {print $1}' || true)"
if [[ "$sha" =~ ^[0-9a-fA-F]{64}$ ]]; then
printf '%s\n' "$sha"
return 0
fi
echo "Attempt ${i}/${attempts} failed for ${name}; retrying in ${delay_seconds}s..."
sleep "$delay_seconds"
i=$((i + 1))
done
echo "Failed to resolve valid SHA256 from sidecar after ${attempts} attempts: ${name}"
return 1
}
SHA_MACOS="$(fetch_sha "gitsnitch-macOS-universal2.tar.gz")"
SHA_LINUX_X86="$(fetch_sha "gitsnitch-Linux-musl-x86_64.tar.gz")"
SHA_LINUX_ARM64="$(fetch_sha "gitsnitch-Linux-musl-arm64.tar.gz")"
SHA_LINUX_ARMV7="$(fetch_sha "gitsnitch-Linux-musleabihf-armv7.tar.gz")"
payload="$(jq -n \
--arg version "$VERSION" \
--arg sha_macos "$SHA_MACOS" \
--arg sha_x86 "$SHA_LINUX_X86" \
--arg sha_arm64 "$SHA_LINUX_ARM64" \
--arg sha_armv7 "$SHA_LINUX_ARMV7" \
--arg repository "$REPOSITORY" \
'{
event_type: "formula-update",
client_payload: {
repository: $repository,
version: $version,
sha256_macos_universal2: $sha_macos,
sha256_linux_x86_64: $sha_x86,
sha256_linux_arm64: $sha_arm64,
sha256_linux_armv7: $sha_armv7
}
}')"
curl --fail-with-body -sS \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${TAP_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/iilei/homebrew-tap/dispatches \
-d "$payload"
publish-nuget:
name: Publish to NuGet / Chocolatey
runs-on: windows-latest
needs:
- release
- release-macos-universal2
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Detect dry-run mode
id: dry_run
shell: bash
run: |
git fetch origin master --depth=1 2>/dev/null || true
if [[ "$GITHUB_REF" == refs/heads/master ]] || \
git merge-base --is-ancestor "$GITHUB_SHA" origin/master 2>/dev/null; then
echo "DRY_RUN=false" >> "$GITHUB_ENV"
echo "value=false" >> "$GITHUB_OUTPUT"
else
echo "DRY_RUN=true" >> "$GITHUB_ENV"
echo "value=true" >> "$GITHUB_OUTPUT"
echo "Commit not reachable from master — dry-run mode."
fi
- name: Resolve version and SHA256
id: resolve
shell: bash
env:
VERSION: ${{ github.ref_name }}
run: |
VERSION="${VERSION#v}"
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
SHA=$(curl -sL \
"https://github.com/iilei/gitsnitch/releases/download/v${VERSION}/gitsnitch-Windows-msvc-x86_64.zip.sha256" \
| awk '{print $1}')
if [[ -z "$SHA" ]]; then
echo "Failed to fetch SHA256 for Windows release asset."
exit 1
fi
echo "SHA256_WIN_X64=${SHA}" >> "$GITHUB_ENV"
- name: Stamp install script
shell: pwsh
run: |
$script = Get-Content packaging\choco\tools\chocolateyInstall.ps1 -Raw
$script = $script -replace '{{VERSION}}', $env:VERSION
$script = $script -replace '{{SHA256_WIN_X64}}', $env:SHA256_WIN_X64
Set-Content packaging\choco\tools\chocolateyInstall.ps1 $script
- name: Pack Chocolatey package
uses: crazy-max/ghaction-chocolatey@v4
with:
args: pack packaging\choco\gitsnitch.nuspec --version ${{ steps.resolve.outputs.version }} --outputdirectory out\choco
- name: Stage NuGet package assets
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path packaging\nuget\docs | Out-Null
New-Item -ItemType Directory -Force -Path packaging\nuget\images | Out-Null
Copy-Item README.md packaging\nuget\docs\README.md
Copy-Item gitsnitch_banner.png packaging\nuget\images\icon.png
- name: Pack NuGet package
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path out\nuget | Out-Null
nuget pack packaging\nuget\gitsnitch.nuspec `
-Version ${{ steps.resolve.outputs.version }} `
-OutputDirectory out\nuget `
-NoDefaultExcludes
- name: Push to NuGet.org
shell: pwsh
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
$pkg = Get-ChildItem out\nuget\*.nupkg | Select-Object -First 1
dotnet nuget push $pkg.FullName `
--api-key $env:NUGET_API_KEY `
--source https://api.nuget.org/v3/index.json `
--skip-duplicate
- name: Push to Chocolatey Community
continue-on-error: true
uses: crazy-max/ghaction-chocolatey@v4
with:
args: push out\choco\gitsnitch.${{ steps.resolve.outputs.version }}.nupkg --source https://push.chocolatey.org/ --api-key ${{ secrets.CHOCOLATEY_API_KEY }}