mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
310 lines
12 KiB
YAML
310 lines
12 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v[0-9]*'
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: "Semver version (e.g. 0.4.0) — only for manual runs"
|
|
required: true
|
|
ref:
|
|
description: "Branch, tag, or SHA to build — only for manual runs"
|
|
default: main
|
|
required: true
|
|
|
|
jobs:
|
|
release:
|
|
name: Release
|
|
runs-on: macos-latest
|
|
timeout-minutes: 60
|
|
permissions:
|
|
contents: write
|
|
id-token: write # required by block/apple-codesign-action for OIDC
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
steps:
|
|
- name: Determine version
|
|
id: version
|
|
env:
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
INPUT_VERSION: ${{ inputs.version }}
|
|
run: |
|
|
if [[ "$EVENT_NAME" == "push" ]]; then
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
else
|
|
VERSION="$INPUT_VERSION"
|
|
fi
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Validate version
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
|
|
echo "::error::Invalid version '$VERSION'. Expected semver (e.g. 0.4.0 or 1.0.0-beta.1)"
|
|
exit 1
|
|
fi
|
|
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
with:
|
|
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
|
|
persist-credentials: false
|
|
|
|
- uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
|
|
|
|
- name: Install desktop dependencies
|
|
run: just desktop-install-ci
|
|
|
|
- name: Patch version
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
|
|
cd src-tauri && cargo update --workspace
|
|
|
|
- name: Generate release config
|
|
run: cd desktop && node scripts/build-release-config.mjs
|
|
env:
|
|
SPROUT_UPDATER_PUBLIC_KEY: ${{ secrets.SPROUT_UPDATER_PUBLIC_KEY }}
|
|
SPROUT_UPDATER_ENDPOINT: https://github.com/block/sprout/releases/download/sprout-desktop-latest/latest.json
|
|
|
|
- name: Build sidecars
|
|
run: |
|
|
cargo build --release -p sprout-acp -p sprout-mcp -p sprout-agent -p sprout-dev-mcp -p git-credential-nostr -p sprout-cli
|
|
./scripts/bundle-sidecars.sh
|
|
|
|
- name: Build unsigned Tauri app
|
|
run: cd desktop && pnpm tauri build --verbose --no-sign --config src-tauri/tauri.release.conf.json
|
|
env:
|
|
SPROUT_UPDATER_PUBLIC_KEY: ${{ secrets.SPROUT_UPDATER_PUBLIC_KEY }}
|
|
SPROUT_UPDATER_ENDPOINT: https://github.com/block/sprout/releases/download/sprout-desktop-latest/latest.json
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
|
|
|
- name: Locate unsigned DMG
|
|
id: unsigned
|
|
run: |
|
|
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
|
|
DMG=$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -type f | head -1)
|
|
if [[ -z "$DMG" ]]; then
|
|
echo "::error::No DMG found in $BUNDLE_DIR/dmg"
|
|
exit 1
|
|
fi
|
|
echo "dmg=$DMG" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Codesign and Notarize
|
|
id: codesign
|
|
uses: block/apple-codesign-action@679535d1ab7c5a7c18e6f9afcba3464512cc3dde # v1.1.0
|
|
with:
|
|
osx-codesign-role: ${{ secrets.OSX_CODESIGN_ROLE }}
|
|
codesign-s3-bucket: ${{ secrets.CODESIGN_S3_BUCKET }}
|
|
unsigned-artifact-path: ${{ steps.unsigned.outputs.dmg }}
|
|
entitlements-plist-path: desktop/src-tauri/Entitlements.plist
|
|
artifact-name: sprout-${{ github.sha }}-${{ github.run_id }}-arm64
|
|
|
|
- name: Replace DMG and rebuild updater archive
|
|
env:
|
|
SIGNED_DMG: ${{ steps.codesign.outputs.signed-dmg-path }}
|
|
SIGNED_APP_ZIP: ${{ steps.codesign.outputs.signed-artifact-path }}
|
|
UNSIGNED_DMG: ${{ steps.unsigned.outputs.dmg }}
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
|
|
APP_DIR="${BUNDLE_DIR}/macos"
|
|
|
|
# Replace unsigned DMG with the signed/notarized one.
|
|
cp "$SIGNED_DMG" "$UNSIGNED_DMG"
|
|
|
|
# Swap the unsigned .app for the signed .app extracted from the action's zip.
|
|
EXTRACT_DIR="${RUNNER_TEMP}/signed-app-extract"
|
|
rm -rf "$EXTRACT_DIR" && mkdir -p "$EXTRACT_DIR"
|
|
ditto -x -k "$SIGNED_APP_ZIP" "$EXTRACT_DIR"
|
|
rm -rf "${APP_DIR}/Sprout.app"
|
|
cp -R "${EXTRACT_DIR}/Sprout.app" "${APP_DIR}/Sprout.app"
|
|
|
|
# Rebuild the updater archive from the signed .app and re-sign it with the Tauri updater key.
|
|
rm -f "${APP_DIR}/Sprout.app.tar.gz" "${APP_DIR}/Sprout.app.tar.gz.sig"
|
|
(cd "$APP_DIR" && tar -czf Sprout.app.tar.gz Sprout.app)
|
|
TARBALL_ABS="$(pwd)/${APP_DIR}/Sprout.app.tar.gz"
|
|
(cd desktop && pnpm tauri signer sign "$TARBALL_ABS")
|
|
|
|
- name: Verify code signature
|
|
run: |
|
|
codesign --verify --deep --strict --verbose=2 \
|
|
desktop/src-tauri/target/release/bundle/macos/Sprout.app
|
|
spctl --assess --type execute --verbose=4 \
|
|
desktop/src-tauri/target/release/bundle/macos/Sprout.app
|
|
|
|
- name: Locate build artifacts
|
|
id: artifacts
|
|
run: |
|
|
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
|
|
|
|
# Find the DMG (Tauri names it Sprout_<version>_<arch>.dmg)
|
|
DMG=$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -type f | head -1)
|
|
if [[ -z "$DMG" ]]; then
|
|
echo "::error::No DMG found in $BUNDLE_DIR/dmg"
|
|
exit 1
|
|
fi
|
|
echo "dmg=$DMG" >> "$GITHUB_OUTPUT"
|
|
|
|
# Find the updater .tar.gz and .sig
|
|
ARCHIVE=$(find "$BUNDLE_DIR/macos" -name '*.tar.gz' ! -name '*.sig' -type f | head -1)
|
|
SIG="${ARCHIVE}.sig"
|
|
if [[ -z "$ARCHIVE" || ! -f "$SIG" ]]; then
|
|
echo "::error::Updater archive or signature not found in $BUNDLE_DIR/macos"
|
|
exit 1
|
|
fi
|
|
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
|
echo "archive_name=$(basename "$ARCHIVE")" >> "$GITHUB_OUTPUT"
|
|
echo "sig=$SIG" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Generate latest.json
|
|
run: |
|
|
bash desktop/scripts/generate-oss-latest-json.sh \
|
|
"$VERSION" \
|
|
"$SIG_PATH" \
|
|
"https://github.com/block/sprout/releases/download/sprout-desktop-latest/$ARCHIVE_NAME" \
|
|
> latest.json
|
|
cat latest.json
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
SIG_PATH: ${{ steps.artifacts.outputs.sig }}
|
|
ARCHIVE_NAME: ${{ steps.artifacts.outputs.archive_name }}
|
|
|
|
- name: Create versioned GitHub release
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
DMG_PATH: ${{ steps.artifacts.outputs.dmg }}
|
|
run: |
|
|
RELEASE_SHA=$(git rev-parse HEAD)
|
|
NOTES=$(awk "/^## v${VERSION}$/,/^## v/" CHANGELOG.md | head -n -1)
|
|
if [[ -z "$NOTES" ]]; then
|
|
NOTES="Sprout Desktop v${VERSION}"
|
|
fi
|
|
gh release create "v${VERSION}" \
|
|
--target "$RELEASE_SHA" \
|
|
--title "Sprout Desktop v${VERSION}" \
|
|
--notes "$NOTES" \
|
|
"$DMG_PATH"
|
|
|
|
- name: Update rolling release for auto-updater
|
|
run: |
|
|
gh release create sprout-desktop-latest \
|
|
--prerelease \
|
|
--title "Sprout Desktop Auto-Update" \
|
|
--notes "Rolling release for the Tauri auto-updater. Do not download manually — use the versioned release instead." \
|
|
2>/dev/null || true
|
|
gh release upload sprout-desktop-latest \
|
|
latest.json \
|
|
"$ARCHIVE_PATH" \
|
|
"$SIG_PATH" \
|
|
--clobber
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
ARCHIVE_PATH: ${{ steps.artifacts.outputs.archive }}
|
|
SIG_PATH: ${{ steps.artifacts.outputs.sig }}
|
|
|
|
release-linux:
|
|
name: Release Linux
|
|
runs-on: ubuntu-latest
|
|
needs: release
|
|
timeout-minutes: 60
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
with:
|
|
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
|
|
persist-credentials: false
|
|
|
|
- uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
|
|
|
|
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
|
with:
|
|
workspaces: desktop/src-tauri
|
|
lookup-only: true
|
|
|
|
- name: Install Tauri dependencies (Linux)
|
|
env:
|
|
DEBIAN_FRONTEND: noninteractive
|
|
run: |
|
|
sudo apt-get update \
|
|
-o Acquire::Retries=3 \
|
|
-o Acquire::http::Timeout=30 \
|
|
-o Acquire::https::Timeout=30
|
|
sudo apt-get install -y --no-install-recommends \
|
|
-o Acquire::Retries=3 \
|
|
-o Acquire::http::Timeout=30 \
|
|
-o Acquire::https::Timeout=30 \
|
|
-o DPkg::Lock::Timeout=120 \
|
|
build-essential \
|
|
curl \
|
|
file \
|
|
libasound2-dev \
|
|
libayatana-appindicator3-dev \
|
|
libgtk-3-dev \
|
|
librsvg2-dev \
|
|
libssl-dev \
|
|
libwebkit2gtk-4.1-dev \
|
|
libxdo-dev \
|
|
patchelf \
|
|
wget
|
|
|
|
- name: Install desktop dependencies
|
|
run: just desktop-install-ci
|
|
|
|
- name: Patch version
|
|
env:
|
|
VERSION: ${{ needs.release.outputs.version }}
|
|
run: |
|
|
cd desktop && node scripts/set-version-from-tag.mjs "$VERSION"
|
|
cd src-tauri && cargo update --workspace
|
|
|
|
- name: Build sidecars
|
|
run: |
|
|
cargo build --release -p sprout-acp -p sprout-mcp -p sprout-agent -p sprout-dev-mcp -p git-credential-nostr -p sprout-cli
|
|
./scripts/bundle-sidecars.sh
|
|
|
|
- name: Build Linux Tauri app
|
|
run: cd desktop && pnpm tauri build --verbose --ci --bundles deb,appimage
|
|
env:
|
|
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
|
|
|
- name: Locate Linux build artifacts
|
|
id: linux-artifacts
|
|
run: |
|
|
BUNDLE_DIR="desktop/src-tauri/target/release/bundle"
|
|
|
|
DEB=$(find "$BUNDLE_DIR/deb" -name '*.deb' -type f | head -1)
|
|
if [[ -z "$DEB" ]]; then
|
|
echo "::error::No DEB found in $BUNDLE_DIR/deb"
|
|
exit 1
|
|
fi
|
|
echo "deb=$DEB" >> "$GITHUB_OUTPUT"
|
|
|
|
APPIMAGE=$(find "$BUNDLE_DIR/appimage" -name '*.AppImage' -type f | head -1)
|
|
if [[ -z "$APPIMAGE" ]]; then
|
|
echo "::error::No AppImage found in $BUNDLE_DIR/appimage"
|
|
exit 1
|
|
fi
|
|
echo "appimage=$APPIMAGE" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Upload Linux artifacts to versioned GitHub release
|
|
env:
|
|
VERSION: ${{ needs.release.outputs.version }}
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
DEB_PATH: ${{ steps.linux-artifacts.outputs.deb }}
|
|
APPIMAGE_PATH: ${{ steps.linux-artifacts.outputs.appimage }}
|
|
run: |
|
|
gh release upload "v$VERSION" \
|
|
"$DEB_PATH" \
|
|
"$APPIMAGE_PATH" \
|
|
--clobber
|