#!/usr/bin/env bash set -euo pipefail CONF=${1:-release} ALLOW_LLDB=${CODEXBAR_ALLOW_LLDB:-0} ROOT=$(cd "$(dirname "$0")/.." && pwd) cd "$ROOT" # Load version info source "$ROOT/version.env" # Force a clean build to avoid stale binaries. rm -rf "$ROOT/.build" swift package clean >/dev/null 2>&1 || true swift build -c "$CONF" --arch arm64 APP="$ROOT/CodexBar.app" rm -rf "$APP" mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources" "$APP/Contents/Frameworks" mkdir -p "$APP/Contents/Helpers" "$APP/Contents/PlugIns" # Convert new .icon bundle to .icns if present (macOS 14+/IconStudio export) ICON_SOURCE="$ROOT/Icon.icon" ICON_TARGET="$ROOT/Icon.icns" if [[ -f "$ICON_SOURCE" ]]; then iconutil --convert icns --output "$ICON_TARGET" "$ICON_SOURCE" fi BUNDLE_ID="com.steipete.codexbar" FEED_URL="https://raw.githubusercontent.com/steipete/CodexBar/main/appcast.xml" AUTO_CHECKS=true LOWER_CONF=$(printf "%s" "$CONF" | tr '[:upper:]' '[:lower:]') if [[ "$LOWER_CONF" == "debug" ]]; then BUNDLE_ID="com.steipete.codexbar.debug" FEED_URL="" AUTO_CHECKS=false fi WIDGET_BUNDLE_ID="${BUNDLE_ID}.widget" APP_GROUP_ID="group.com.steipete.codexbar" if [[ "$BUNDLE_ID" == *".debug"* ]]; then APP_GROUP_ID="group.com.steipete.codexbar.debug" fi ENTITLEMENTS_DIR="$ROOT/.build/entitlements" APP_ENTITLEMENTS="${ENTITLEMENTS_DIR}/CodexBar.entitlements" WIDGET_ENTITLEMENTS="${ENTITLEMENTS_DIR}/CodexBarWidget.entitlements" mkdir -p "$ENTITLEMENTS_DIR" if [[ "$ALLOW_LLDB" == "1" && "$LOWER_CONF" != "debug" ]]; then echo "ERROR: CODEXBAR_ALLOW_LLDB requires debug configuration" >&2 exit 1 fi cat > "$APP_ENTITLEMENTS" < com.apple.security.application-groups ${APP_GROUP_ID} $(if [[ "$ALLOW_LLDB" == "1" ]]; then echo " com.apple.security.get-task-allow"; fi) PLIST cat > "$WIDGET_ENTITLEMENTS" < com.apple.security.app-sandbox com.apple.security.application-groups ${APP_GROUP_ID} PLIST BUILD_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") cat > "$APP/Contents/Info.plist" < CFBundleNameCodexBar CFBundleDisplayNameCodexBar CFBundleIdentifier${BUNDLE_ID} CFBundleExecutableCodexBar CFBundlePackageTypeAPPL CFBundleShortVersionString${MARKETING_VERSION} CFBundleVersion${BUILD_NUMBER} LSMinimumSystemVersion15.0 LSUIElement CFBundleIconFileIcon NSHumanReadableCopyright© 2025 Peter Steinberger. MIT License. SUFeedURL${FEED_URL} SUPublicEDKeyAGCY8w5vHirVfGGDGc8Szc5iuOqupZSh9pMj/Qs67XI= SUEnableAutomaticChecks<${AUTO_CHECKS}/> CodexBuildTimestamp${BUILD_TIMESTAMP} CodexGitCommit${GIT_COMMIT} PLIST cp ".build/$CONF/CodexBar" "$APP/Contents/MacOS/CodexBar" chmod +x "$APP/Contents/MacOS/CodexBar" # Ship CodexBarCLI alongside the app for easy symlinking. if [[ -f ".build/$CONF/CodexBarCLI" ]]; then cp ".build/$CONF/CodexBarCLI" "$APP/Contents/Helpers/CodexBarCLI" chmod +x "$APP/Contents/Helpers/CodexBarCLI" fi # Watchdog helper: ensures `claude` probes die when CodexBar crashes/gets killed. if [[ -f ".build/$CONF/CodexBarClaudeWatchdog" ]]; then cp ".build/$CONF/CodexBarClaudeWatchdog" "$APP/Contents/Helpers/CodexBarClaudeWatchdog" chmod +x "$APP/Contents/Helpers/CodexBarClaudeWatchdog" fi if [[ -f ".build/$CONF/CodexBarWidget" ]]; then WIDGET_APP="$APP/Contents/PlugIns/CodexBarWidget.appex" mkdir -p "$WIDGET_APP/Contents/MacOS" "$WIDGET_APP/Contents/Resources" cat > "$WIDGET_APP/Contents/Info.plist" < CFBundleNameCodexBarWidget CFBundleDisplayNameCodexBar CFBundleIdentifier${WIDGET_BUNDLE_ID} CFBundleExecutableCodexBarWidget CFBundlePackageTypeXPC! CFBundleShortVersionString${MARKETING_VERSION} CFBundleVersion${BUILD_NUMBER} LSMinimumSystemVersion15.0 NSExtension NSExtensionPointIdentifiercom.apple.widgetkit-extension NSExtensionPrincipalClassCodexBarWidget.CodexBarWidgetBundle PLIST cp ".build/$CONF/CodexBarWidget" "$WIDGET_APP/Contents/MacOS/CodexBarWidget" chmod +x "$WIDGET_APP/Contents/MacOS/CodexBarWidget" fi # Embed Sparkle.framework if [[ -d ".build/$CONF/Sparkle.framework" ]]; then cp -R ".build/$CONF/Sparkle.framework" "$APP/Contents/Frameworks/" chmod -R a+rX "$APP/Contents/Frameworks/Sparkle.framework" install_name_tool -add_rpath "@executable_path/../Frameworks" "$APP/Contents/MacOS/CodexBar" # Re-sign Sparkle and all nested components with Developer ID + timestamp SPARKLE="$APP/Contents/Frameworks/Sparkle.framework" if [[ "$ALLOW_LLDB" == "1" ]]; then CODESIGN_ID="-" CODESIGN_ARGS=(--force --sign "$CODESIGN_ID") else CODESIGN_ID="${APP_IDENTITY:-Developer ID Application: Peter Steinberger (Y5PE65HELJ)}" CODESIGN_ARGS=(--force --timestamp --options runtime --sign "$CODESIGN_ID") fi function resign() { codesign "${CODESIGN_ARGS[@]}" "$1"; } # Sign innermost binaries first, then the framework root to seal resources resign "$SPARKLE" resign "$SPARKLE/Versions/B/Sparkle" resign "$SPARKLE/Versions/B/Autoupdate" resign "$SPARKLE/Versions/B/Updater.app" resign "$SPARKLE/Versions/B/Updater.app/Contents/MacOS/Updater" resign "$SPARKLE/Versions/B/XPCServices/Downloader.xpc" resign "$SPARKLE/Versions/B/XPCServices/Downloader.xpc/Contents/MacOS/Downloader" resign "$SPARKLE/Versions/B/XPCServices/Installer.xpc" resign "$SPARKLE/Versions/B/XPCServices/Installer.xpc/Contents/MacOS/Installer" resign "$SPARKLE/Versions/B" resign "$SPARKLE" fi if [[ -f "$ICON_TARGET" ]]; then cp "$ICON_TARGET" "$APP/Contents/Resources/Icon.icns" fi # Bundle app resources (provider icons, etc.). APP_RESOURCES_DIR="$ROOT/Sources/CodexBar/Resources" if [[ -d "$APP_RESOURCES_DIR" ]]; then cp -R "$APP_RESOURCES_DIR/." "$APP/Contents/Resources/" fi # Strip extended attributes to prevent AppleDouble (._*) files that break code sealing xattr -cr "$APP" find "$APP" -name '._*' -delete # Sign widget extension if present if [[ -d "${APP}/Contents/PlugIns/CodexBarWidget.appex" ]]; then codesign "${CODESIGN_ARGS[@]}" \ --entitlements "$WIDGET_ENTITLEMENTS" \ "$APP/Contents/PlugIns/CodexBarWidget.appex/Contents/MacOS/CodexBarWidget" codesign "${CODESIGN_ARGS[@]}" \ --entitlements "$WIDGET_ENTITLEMENTS" \ "$APP/Contents/PlugIns/CodexBarWidget.appex" fi # Finally sign the app bundle itself codesign "${CODESIGN_ARGS[@]}" \ --entitlements "$APP_ENTITLEMENTS" \ "$APP" echo "Created $APP"