Files
ccs/macos-bar/Scripts/package_app.sh
T
Tam Nhu Tran b3a9abffbc ci(bar): auto-build and publish CCS Bar on main via self-hosted macOS runner
Releasing the macOS bar app was fully manual (run package_app.sh on a Mac, then
gh release upload --clobber), so Swift-side changes never reached users until
someone remembered to rebuild and re-upload the floating ccs-bar-latest asset.

Add a tightly-scoped Bar Release workflow that does it automatically:
- triggers ONLY on push to main touching macos-bar/**, or manual dispatch, so
  regular PRs, dev pushes, and non-bar changes never start it
- runs ONLY on the dedicated self-hosted macOS runner (label ccs-bar); the Linux
  CI runners never match it and it never competes for them
- least-privilege contents:write, single-flight via concurrency

Version is sourced from a new macos-bar/VERSION single-line file (the workflow
reads it; the asset is always the latest build regardless). package_app.sh now
defaults to that file when no version arg is passed, so the local manual path and
CI share one source of truth. Documents the release process in docs/ccs-bar.md.
2026-06-20 22:17:40 -04:00

105 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Assemble and sign "CCS Bar.app" from a release build.
#
# Signing mode (CCS_BAR_SIGNING):
# adhoc (default) ad-hoc sign with `codesign -s -`. Free, no Apple
# Developer account. Users open via right-click > Open or clear
# quarantine with `xattr -dr com.apple.quarantine`.
# developer-id Sign with a Developer ID Application identity (set
# CCS_BAR_SIGN_IDENTITY) for the notarized public-launch path.
#
# Usage:
# ./Scripts/package_app.sh [version]
# CCS_BAR_SIGNING=developer-id CCS_BAR_SIGN_IDENTITY="Developer ID Application: ..." ./Scripts/package_app.sh 0.1.0
set -euo pipefail
SIGNING="${CCS_BAR_SIGNING:-adhoc}"
APP_NAME="CCS Bar"
EXEC_NAME="CCSBar"
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
# Version precedence: explicit arg, else the committed `VERSION` file (the single
# source of truth shared with the Bar Release CI workflow), else 0.0.0.
VERSION="${1:-}"
if [[ -z "$VERSION" && -f "$ROOT/VERSION" ]]; then
VERSION="$(tr -d '[:space:]' < "$ROOT/VERSION")"
fi
VERSION="${VERSION:-0.0.0}"
DIST="$ROOT/dist"
APP="$DIST/$APP_NAME.app"
echo "[i] Building release binary..."
( cd "$ROOT" && swift build -c release )
BIN="$ROOT/.build/release/$EXEC_NAME"
if [[ ! -x "$BIN" ]]; then
echo "[X] Release binary not found at $BIN" >&2
exit 1
fi
echo "[i] Assembling $APP_NAME.app (version $VERSION)..."
rm -rf "$APP"
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
cp "$BIN" "$APP/Contents/MacOS/$EXEC_NAME"
sed "s/__VERSION__/$VERSION/g" "$ROOT/Resources/Info.plist" > "$APP/Contents/Info.plist"
# Bundle the CCS icon assets (menu-bar color/template + header logo) so
# Bundle.main can resolve them at runtime.
if [[ -d "$ROOT/Resources/Assets" ]]; then
cp "$ROOT/Resources/Assets/"*.png "$APP/Contents/Resources/" 2>/dev/null || true
fi
# Build AppIcon.icns from AppIcon-source.png (1024x1024 RGBA PNG).
ICON_SRC="$ROOT/Resources/AppIcon-source.png"
if [[ -f "$ICON_SRC" ]]; then
echo "[i] Building AppIcon.icns..."
ICONSET_DIR="$(mktemp -d)/AppIcon.iconset"
mkdir -p "$ICONSET_DIR"
sips -z 16 16 "$ICON_SRC" --out "$ICONSET_DIR/icon_16x16.png" > /dev/null
sips -z 32 32 "$ICON_SRC" --out "$ICONSET_DIR/icon_16x16@2x.png" > /dev/null
sips -z 32 32 "$ICON_SRC" --out "$ICONSET_DIR/icon_32x32.png" > /dev/null
sips -z 64 64 "$ICON_SRC" --out "$ICONSET_DIR/icon_32x32@2x.png" > /dev/null
sips -z 128 128 "$ICON_SRC" --out "$ICONSET_DIR/icon_128x128.png" > /dev/null
sips -z 256 256 "$ICON_SRC" --out "$ICONSET_DIR/icon_128x128@2x.png" > /dev/null
sips -z 256 256 "$ICON_SRC" --out "$ICONSET_DIR/icon_256x256.png" > /dev/null
sips -z 512 512 "$ICON_SRC" --out "$ICONSET_DIR/icon_256x256@2x.png" > /dev/null
sips -z 512 512 "$ICON_SRC" --out "$ICONSET_DIR/icon_512x512.png" > /dev/null
sips -z 1024 1024 "$ICON_SRC" --out "$ICONSET_DIR/icon_512x512@2x.png" > /dev/null
iconutil -c icns "$ICONSET_DIR" -o "$APP/Contents/Resources/AppIcon.icns"
rm -rf "$(dirname "$ICONSET_DIR")"
echo "[OK] AppIcon.icns built."
else
echo "[!] AppIcon-source.png not found at $ICON_SRC — skipping icon." >&2
fi
echo "[i] Signing ($SIGNING)..."
case "$SIGNING" in
adhoc)
codesign --force --deep --sign - "$APP"
;;
developer-id)
: "${CCS_BAR_SIGN_IDENTITY:?Set CCS_BAR_SIGN_IDENTITY for developer-id signing}"
codesign --force --deep --options runtime --timestamp \
--sign "$CCS_BAR_SIGN_IDENTITY" "$APP"
echo "[i] Signed with Developer ID. Notarize before public distribution:"
echo " xcrun notarytool submit <zip> --keychain-profile <profile> --wait"
;;
*)
echo "[X] Unknown CCS_BAR_SIGNING: $SIGNING (expected adhoc|developer-id)" >&2
exit 1
;;
esac
ZIP="$DIST/CCS-Bar.app.zip"
echo "[i] Zipping -> $ZIP"
rm -f "$ZIP"
( cd "$DIST" && ditto -c -k --keepParent "$APP_NAME.app" "CCS-Bar.app.zip" )
echo "[OK] Packaged: $APP"
echo "[OK] Asset: $ZIP"
if [[ "$SIGNING" == "adhoc" ]]; then
echo "[!] Ad-hoc build: first launch needs right-click > Open, or"
echo " xattr -dr com.apple.quarantine \"/Applications/$APP_NAME.app\""
fi
echo "[i] To publish: gh release upload ccs-bar-latest \"$ZIP\" --clobber"