diff --git a/Package.swift b/Package.swift index ed35abc..037a92b 100644 --- a/Package.swift +++ b/Package.swift @@ -10,10 +10,22 @@ let package = Package( products: [ .executable(name: "AgentBar", targets: ["AgentBar"]), ], + dependencies: [ + .package(url: "https://github.com/sparkle-project/Sparkle", from: "2.9.4"), + ], targets: [ .executableTarget( name: "AgentBar", - path: "Sources/AgentBar" + dependencies: [ + .product(name: "Sparkle", package: "Sparkle"), + ], + path: "Sources/AgentBar", + linkerSettings: [ + .unsafeFlags([ + "-Xlinker", "-rpath", + "-Xlinker", "@executable_path/../Frameworks", + ]), + ] ), ] ) diff --git a/Sources/AgentBar/App/AppDelegate.swift b/Sources/AgentBar/App/AppDelegate.swift index 83308b7..b30c8de 100644 --- a/Sources/AgentBar/App/AppDelegate.swift +++ b/Sources/AgentBar/App/AppDelegate.swift @@ -1,5 +1,6 @@ import AppKit import Combine +import Sparkle import SwiftUI @MainActor @@ -14,10 +15,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate { private var globalEventMonitor: Any? private var cancellables = Set() private var lastShownCLIUpgradeAlertMessage: String? + private var updaterController: SPUStandardUpdaterController? func applicationDidFinishLaunching(_ notification: Notification) { NSApp.setActivationPolicy(.accessory) NSApp.applicationIconImage = AppIcon.image() + configureUpdater() configureStatusItem() configurePopover() bindStore() @@ -51,6 +54,20 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate { menuBarView = view } + private func configureUpdater() { + guard Self.hasSparkleConfiguration else { + return + } + + let updaterController = SPUStandardUpdaterController( + startingUpdater: true, + updaterDelegate: nil, + userDriverDelegate: nil + ) + self.updaterController = updaterController + updaterController.updater.checkForUpdatesInBackground() + } + private func configurePopover() { let popover = NSPopover() popover.behavior = .transient @@ -214,6 +231,22 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate { let pointInView = view.convert(pointInWindow, from: nil) return view.bounds.contains(pointInView) } + + private static var hasSparkleConfiguration: Bool { + guard + let infoDictionary = Bundle.main.infoDictionary, + let feedURL = trimmedInfoValue("SUFeedURL", in: infoDictionary), + let publicKey = trimmedInfoValue("SUPublicEDKey", in: infoDictionary) + else { + return false + } + + return !feedURL.isEmpty && !publicKey.isEmpty + } + + private static func trimmedInfoValue(_ key: String, in infoDictionary: [String: Any]) -> String? { + (infoDictionary[key] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) + } } private final class VisualEffectHostingController: NSViewController { diff --git a/appcast.xml b/appcast.xml new file mode 100644 index 0000000..7f33f14 --- /dev/null +++ b/appcast.xml @@ -0,0 +1,11 @@ + + + + AgentBar Updates + https://github.com/lonewolfyx/codex-agent-bar + Appcast feed for AgentBar automatic updates. + en + + diff --git a/scripts/build-dmg.sh b/scripts/build-dmg.sh index 1ce7b4e..eb29f70 100755 --- a/scripts/build-dmg.sh +++ b/scripts/build-dmg.sh @@ -11,6 +11,9 @@ CONFIGURATION="${CONFIGURATION:-release}" DMG_VOLUME_NAME="${DMG_VOLUME_NAME:-$APP_NAME}" SIGN_APP="${SIGN_APP:-1}" CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY:--}" +SPARKLE_FEED_URL="${SPARKLE_FEED_URL:-https://raw.githubusercontent.com/lonewolfyx/codex-agent-bar/main/appcast.xml}" +SPARKLE_PUBLIC_ED_KEY="${SPARKLE_PUBLIC_ED_KEY:-KUrqIsbJSH1fYUKJnDxuzKdAWmXt/Tu7JGPSCwkLGtU=}" +SPARKLE_FRAMEWORK_PATH="${SPARKLE_FRAMEWORK_PATH:-}" ENV_VERSION_WAS_SET=0 ENV_BUILD_NUMBER_WAS_SET=0 @@ -64,7 +67,8 @@ Options: Environment overrides: APP_NAME, BUNDLE_ID, VERSION, BUILD_NUMBER, CONFIGURATION, DIST_DIR, - DMG_VOLUME_NAME, VERSION_ENV_FILE, SIGN_APP, CODE_SIGN_IDENTITY + DMG_VOLUME_NAME, VERSION_ENV_FILE, SIGN_APP, CODE_SIGN_IDENTITY, + SPARKLE_FEED_URL, SPARKLE_PUBLIC_ED_KEY, SPARKLE_FRAMEWORK_PATH Version file: $VERSION_ENV_FILE @@ -129,6 +133,7 @@ WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/AgentBar-dmg.XXXXXX")" APP_BUNDLE="$WORK_DIR/$APP_NAME.app" STAGING_DIR="$WORK_DIR/dmg-staging" TEMP_DMG="$WORK_DIR/$APP_NAME-temp.dmg" +FRAMEWORKS_DIR="$APP_BUNDLE/Contents/Frameworks" ICONSET_DIR="$WORK_DIR/AppIcon.iconset" APP_ICON="$WORK_DIR/AppIcon.icns" ARROW_ICON_WORK="$WORK_DIR/DmgArrowIcon.png" @@ -214,10 +219,37 @@ sign_app_bundle() { echo "Signing app bundle with identity: $CODE_SIGN_IDENTITY" fi - codesign --force --sign "$CODE_SIGN_IDENTITY" "$APP_BUNDLE" + codesign --force --deep --sign "$CODE_SIGN_IDENTITY" "$APP_BUNDLE" codesign --verify --deep --strict --verbose=2 "$APP_BUNDLE" } +copy_sparkle_framework() { + if [ -n "$SPARKLE_FRAMEWORK_PATH" ]; then + if [ ! -d "$SPARKLE_FRAMEWORK_PATH" ]; then + echo "Sparkle framework not found: $SPARKLE_FRAMEWORK_PATH" >&2 + exit 1 + fi + else + SPARKLE_FRAMEWORK_PATH="$( + find "$REPO_ROOT/.build" -path "*/Sparkle.framework" -type d 2>/dev/null \ + | grep '/macos' \ + | head -n 1 + )" + fi + + if [ -z "$SPARKLE_FRAMEWORK_PATH" ]; then + echo "Expected Sparkle.framework under $REPO_ROOT/.build after swift build." >&2 + echo "Set SPARKLE_FRAMEWORK_PATH to the macOS Sparkle.framework path if SwiftPM stores it elsewhere." >&2 + exit 1 + fi + + echo "Embedding Sparkle.framework..." + mkdir -p "$FRAMEWORKS_DIR" + rm -rf "$FRAMEWORKS_DIR/Sparkle.framework" + cp -R "$SPARKLE_FRAMEWORK_PATH" "$FRAMEWORKS_DIR/" + chmod -R u+w "$FRAMEWORKS_DIR/Sparkle.framework" +} + write_dmg_layout() { base64 -D > "$STAGING_DIR/.DS_Store" <<'EOF' AAAAAUJ1ZDEAABAAAAAIAAAAEAAAAAQKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAgAAAABAAAQAAAgACBJbG9jYmxvYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACAAAAABAAAAQAAAAAEAAACAAAAAAQAAAQAAAAABAAACAAAAAAEAAAQAAAAAAAAAAAEAABAAAAAAAQAAIAAAAAABAABAAAAAAAEAAIAAAAAAAQABAAAAAAABAAIAAAAAAAEABAAAAAAAAQAIAAAAAAABABAAAAAAAAEAIAAAAAAAAQBAAAAAAAABAIAAAAAAAAEBAAAAAAAAAQIAAAAAAAABBAAAAAAAAAEIAAAAAAAAARAAAAAAAAABIAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAEAsAAABFAAAECgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBERTREIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAIAAAAGAAAAAAAAAAAQAAAIAAAAABAAABAAAAAAEAAAIAAAAAAAAAAAIAAAgAAAAYAAAAAAAAAAABAAAgAAAAAAEAAEAAAAAAAQAAgAAAAAABAAEAAAAAAAEAAgAAAAAAAQAEAAAAAAABAAgAAAAAAAEAEAAAAAAAAQAgAAAAAAABAEAAAAAAAAEAgAAAAAAAAQEAAAAAAAABAgAAAAAAAAEEAAAAAAAAAQgAAAAAAAABEAAAAAAAAAEgAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== @@ -254,9 +286,11 @@ rm -rf "$DMG_PATH" mkdir -p \ "$APP_BUNDLE/Contents/MacOS" \ "$APP_BUNDLE/Contents/Resources" \ + "$FRAMEWORKS_DIR" \ "$STAGING_DIR" \ "$(dirname -- "$DMG_PATH")" cp "$EXECUTABLE" "$APP_BUNDLE/Contents/MacOS/$APP_NAME" +copy_sparkle_framework echo "Preparing app icon..." mkdir -p "$ICONSET_DIR" @@ -307,10 +341,22 @@ cat > "$APP_BUNDLE/Contents/Info.plist" < NSHighResolutionCapable + SUAutomaticallyUpdate + + SUEnableAutomaticChecks + + SUFeedURL + $SPARKLE_FEED_URL + SUPublicEDKey + $SPARKLE_PUBLIC_ED_KEY EOF +if [ -z "$SPARKLE_PUBLIC_ED_KEY" ]; then + echo "Warning: SPARKLE_PUBLIC_ED_KEY is empty; automatic updates will be disabled in this build." >&2 +fi + sign_app_bundle echo "Preparing DMG staging directory..." diff --git a/scripts/update-appcast.sh b/scripts/update-appcast.sh new file mode 100755 index 0000000..bb128dc --- /dev/null +++ b/scripts/update-appcast.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env sh + +set -eu + +REPO_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)" +VERSION_ENV_FILE="${VERSION_ENV_FILE:-$REPO_ROOT/version.env}" + +APP_NAME="${APP_NAME:-AgentBar}" +DIST_DIR="${DIST_DIR:-$REPO_ROOT/dist}" +APPCAST_PATH="${APPCAST_PATH:-$REPO_ROOT/appcast.xml}" +SPARKLE_GENERATE_APPCAST="${SPARKLE_GENERATE_APPCAST:-generate_appcast}" + +VERSION="1.0.0" +BUILD_NUMBER="1" +if [ -f "$VERSION_ENV_FILE" ]; then + . "$VERSION_ENV_FILE" +fi + +RELEASE_TAG="${RELEASE_TAG:-v$VERSION}" +DMG_PATH="${DMG_PATH:-$DIST_DIR/$APP_NAME-$VERSION.dmg}" +DOWNLOAD_URL_PREFIX="${DOWNLOAD_URL_PREFIX:-https://github.com/lonewolfyx/codex-agent-bar/releases/download/$RELEASE_TAG/}" + +usage() { + cat <&2 + exit 1 + fi + DMG_PATH="$2" + shift 2 + ;; + -o|--output) + if [ "$#" -lt 2 ]; then + echo "Missing value for $1" >&2 + exit 1 + fi + APPCAST_PATH="$2" + shift 2 + ;; + -t|--tag) + if [ "$#" -lt 2 ]; then + echo "Missing value for $1" >&2 + exit 1 + fi + RELEASE_TAG="$2" + DOWNLOAD_URL_PREFIX="https://github.com/lonewolfyx/codex-agent-bar/releases/download/$RELEASE_TAG/" + shift 2 + ;; + -u|--download-url) + if [ "$#" -lt 2 ]; then + echo "Missing value for $1" >&2 + exit 1 + fi + DOWNLOAD_URL_PREFIX="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [ ! -f "$DMG_PATH" ]; then + echo "Expected DMG not found: $DMG_PATH" >&2 + exit 1 +fi + +if ! command -v "$SPARKLE_GENERATE_APPCAST" >/dev/null 2>&1; then + CANDIDATE="$( + find "$REPO_ROOT/.build" -name generate_appcast -type f 2>/dev/null \ + | head -n 1 + )" + if [ -n "$CANDIDATE" ]; then + SPARKLE_GENERATE_APPCAST="$CANDIDATE" + else + echo "Missing Sparkle generate_appcast tool." >&2 + echo "Install Sparkle tools or set SPARKLE_GENERATE_APPCAST=/path/to/generate_appcast." >&2 + exit 1 + fi +fi + +WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/AgentBar-appcast.XXXXXX")" +cleanup() { + rm -rf "$WORK_DIR" +} +trap cleanup EXIT HUP INT TERM + +cp "$DMG_PATH" "$WORK_DIR/" + +"$SPARKLE_GENERATE_APPCAST" \ + --download-url-prefix "$DOWNLOAD_URL_PREFIX" \ + "$WORK_DIR" + +if [ ! -f "$WORK_DIR/appcast.xml" ]; then + echo "generate_appcast did not create appcast.xml" >&2 + exit 1 +fi + +mkdir -p "$(dirname -- "$APPCAST_PATH")" +cp "$WORK_DIR/appcast.xml" "$APPCAST_PATH" + +echo "Updated: $APPCAST_PATH"