Skip to content

release

release #12

Workflow file for this run

name: release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g. v0.1.0). Required only when artifact_only is false."
required: false
artifact_only:
description: "Build a test app artifact without publishing a GitHub Release."
required: true
type: boolean
default: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write
actions: write
id-token: write
jobs:
macos:
# wisp-desktop links against APIs that ship only in macOS 26 (Tahoe),
# so the build host has to be Tahoe too. macos-26 is the
# GitHub-hosted Apple Silicon runner image for it.
runs-on: macos-26
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.artifact_only && github.ref || github.event.inputs.tag || github.ref }}
- name: Resolve tag and version
id: meta
run: |
set -euo pipefail
ARTIFACT_ONLY="${{ github.event_name == 'workflow_dispatch' && inputs.artifact_only }}"
if [ "$ARTIFACT_ONLY" = "true" ]; then
TAG=""
VERSION="0.0.0"
else
TAG="${{ github.event.inputs.tag || github.ref_name }}"
if [ -z "$TAG" ]; then
echo "tag is required when artifact_only is false" >&2
exit 1
fi
VERSION="${TAG#v}"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Select Xcode
run: |
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcrun swift --version
xcrun --find metal
- uses: ./.github/actions/setup-nix
- uses: ./.github/actions/setup-rust-cache
- name: Build Wisp binaries (release)
run: nix develop .#ci --quiet --command cargo build -p wisp-desktop -p wisp-mcp --release --locked
- name: Assemble Wisp.app bundle
run: |
set -euo pipefail
APP="Wisp.app"
rm -rf "$APP"
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
cp target/release/wisp-desktop "$APP/Contents/MacOS/wisp-desktop"
cp target/release/wisp-mcp "$APP/Contents/MacOS/wisp-mcp"
chmod +x "$APP/Contents/MacOS/wisp-desktop"
chmod +x "$APP/Contents/MacOS/wisp-mcp"
cp apps/wisp-desktop/assets/AppIcon.icns "$APP/Contents/Resources/AppIcon.icns"
sed "s/__VERSION__/${{ steps.meta.outputs.version }}/g" \
apps/wisp-desktop/Info.plist > "$APP/Contents/Info.plist"
/usr/bin/plutil -lint "$APP/Contents/Info.plist"
- name: Ad-hoc codesign
# No Apple Developer ID yet; ad-hoc (`--sign -`) is enough to make
# Gatekeeper let users open the app via right-click → Open. Once a
# cert is available, swap in the real identity and add notarization.
#
# The entitlements file is required even for ad-hoc signing because
# we enable hardened runtime (--options runtime). Without
# `com.apple.security.device.audio-input`, AVCaptureDevice and
# AVAudioApplication silently return "denied" for the mic
# permission request — no OS prompt, no TCC entry.
run: |
set -euo pipefail
codesign --force --deep --options runtime \
--entitlements apps/wisp-desktop/wisp-desktop.entitlements \
--sign - Wisp.app
codesign --verify --verbose Wisp.app
- name: Create DMG
id: dmg
run: |
set -euo pipefail
SHORT_SHA="${GITHUB_SHA::7}"
ARTIFACT_ONLY="${{ github.event_name == 'workflow_dispatch' && inputs.artifact_only }}"
if [ "$ARTIFACT_ONLY" = "true" ]; then
DMG="wisp-test-${SHORT_SHA}-aarch64-apple-darwin.dmg"
ZIP="wisp-test-${SHORT_SHA}-aarch64-apple-darwin.zip"
ditto -c -k --sequesterRsrc --keepParent Wisp.app "$ZIP"
echo "zip=$ZIP" >> "$GITHUB_OUTPUT"
else
DMG="wisp-${{ steps.meta.outputs.version }}-aarch64-apple-darwin.dmg"
echo "zip=" >> "$GITHUB_OUTPUT"
fi
STAGE="$(mktemp -d)"
ditto Wisp.app "$STAGE/Wisp.app"
ln -s /Applications "$STAGE/Applications"
hdiutil create \
-volname "Wisp" \
-srcfolder "$STAGE" \
-ov -format UDZO \
"$DMG"
echo "path=$DMG" >> "$GITHUB_OUTPUT"
- name: Upload test app artifact
if: ${{ github.event_name == 'workflow_dispatch' && inputs.artifact_only }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: wisp-macos-app-${{ github.sha }}
path: |
${{ steps.dmg.outputs.path }}
${{ steps.dmg.outputs.zip }}
if-no-files-found: error
retention-days: 14
- name: Publish release
if: ${{ github.event_name != 'workflow_dispatch' || !inputs.artifact_only }}
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
with:
tag_name: ${{ steps.meta.outputs.tag }}
name: ${{ steps.meta.outputs.tag }}
draft: false
prerelease: false
generate_release_notes: true
fail_on_unmatched_files: true
files: ${{ steps.dmg.outputs.path }}