Merge pull request #1126 from mitchellh/sparkle

macOS: Sparkle update framework
This commit is contained in:
Mitchell Hashimoto
2023-12-19 09:47:25 -08:00
committed by GitHub
9 changed files with 237 additions and 7 deletions

View File

@ -48,6 +48,17 @@ jobs:
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
# Setup Sparkle
- name: Setup Sparkle
env:
SPARKLE_VERSION: 2.5.1
run: |
mkdir -p .action/sparkle
cd .action/sparkle
curl -L https://github.com/sparkle-project/Sparkle/releases/download/${SPARKLE_VERSION}/Sparkle-for-Swift-Package-Manager.zip > sparkle.zip
unzip sparkle.zip
echo "$(pwd)/bin" >> $GITHUB_PATH
# Setup our S3 client # Setup our S3 client
- name: Setup s3cmd - name: Setup s3cmd
uses: s3-actions/s3cmd@v1.5.0 uses: s3-actions/s3cmd@v1.5.0
@ -55,7 +66,7 @@ jobs:
provider: cloudflare provider: cloudflare
account_id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} account_id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
access_key: ${{ secrets.CF_R2_TIP_AWS_KEY }} access_key: ${{ secrets.CF_R2_TIP_AWS_KEY }}
secreT_key: ${{ secrets.CF_R2_TIP_SECRET_KEY }} secret_key: ${{ secrets.CF_R2_TIP_SECRET_KEY }}
# Load Build Number # Load Build Number
- name: Build Number - name: Build Number
@ -76,12 +87,18 @@ jobs:
# We inject the "build number" as simply the number of commits since HEAD. # We inject the "build number" as simply the number of commits since HEAD.
# This will be a monotonically always increasing build number that we use. # This will be a monotonically always increasing build number that we use.
- name: Inject Build Number - name: Update Info.plist
env:
SPARKLE_KEY_PUB: ${{ secrets.PROD_MACOS_SPARKLE_KEY_PUB }}
run: | run: |
# Version Info
/usr/libexec/PlistBuddy -c "Set :GhosttyCommit $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist" /usr/libexec/PlistBuddy -c "Set :GhosttyCommit $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $GHOSTTY_BUILD" "macos/build/Release/Ghostty.app/Contents/Info.plist" /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $GHOSTTY_BUILD" "macos/build/Release/Ghostty.app/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist" /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"
# Updater
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
- name: Zip Unsigned App - name: Zip Unsigned App
run: nix develop -c sh -c 'cd macos/build/Release && zip -9 -r --symlinks ../../../ghostty-macos-universal-unsigned.zip Ghostty.app' run: nix develop -c sh -c 'cd macos/build/Release && zip -9 -r --symlinks ../../../ghostty-macos-universal-unsigned.zip Ghostty.app'
@ -115,8 +132,19 @@ jobs:
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain
# We finally codesign our app bundle, specifying the Hardened runtime option # Codesign Sparkle. Some notes here:
/usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime macos/build/Release/Ghostty.app -v # - The XPC services aren't used since we don't sandbox Ghostty,
# but since they're part of the build, they still need to be
# codesigned.
# - The binaries in the "Versions" folders need to NOT be symlinks.
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc"
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Installer.xpc"
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate"
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app"
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework"
# Codesign the app bundle
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime macos/build/Release/Ghostty.app
- name: "Notarize app bundle" - name: "Notarize app bundle"
env: env:
@ -162,7 +190,19 @@ jobs:
files: ghostty-macos-universal.zip files: ghostty-macos-universal.zip
token: ${{ secrets.GH_RELEASE_TOKEN }} token: ${{ secrets.GH_RELEASE_TOKEN }}
# Create our appcast for Sparkle
- name: Generate Appcast
env:
SPARKLE_KEY: ${{ secrets.PROD_MACOS_SPARKLE_KEY }}
run: |
echo $SPARKLE_KEY > signing.key
sign_update -f signing.key ghostty-macos-universal.zip > sign_update.txt
curl -L https://tip.files.ghostty.dev/appcast.xml > appcast.xml
python3 ./dist/macos/update_appcast_tip.py
test -f appcast_new.xml
# Update Blob Storage # Update Blob Storage
- name: Upload to Blob Storage - name: Upload to Blob Storage
run: | run: |
s3cmd put ghostty-macos-universal.zip s3://ghostty-tip/${GHOSTTY_BUILD}/ghostty-macos-universal.zip s3cmd put ghostty-macos-universal.zip s3://ghostty-tip/${GHOSTTY_BUILD}/ghostty-macos-universal.zip
s3cmd put appcast_new.xml s3://ghostty-tip/appcast.xml

75
dist/macos/update_appcast_tip.py vendored Normal file
View File

@ -0,0 +1,75 @@
"""
This script is used to update the appcast.xml file for Ghostty releases.
The script is currently hardcoded to only work for tip releases and therefore
doesn't have rich release notes, hardcodes the URL to the tip bucket, etc.
This expects the following files in the current directory:
- sign_update.txt - contains the output from "sign_update" in the Sparkle
framework for the current build.
- appcast.xml - the existing appcast file.
And the following environment variables to be set:
- GHOSTTY_BUILD - the build number
- GHOSTTY_COMMIT - the commit hash
The script will output a new appcast file called appcast_new.xml.
"""
import os
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
build = os.environ["GHOSTTY_BUILD"]
commit = os.environ["GHOSTTY_COMMIT"]
# Read our sign_update output
with open("sign_update.txt", "r") as f:
# format is a=b b=c etc. create a map of this. values may contain equal
# signs, so we can't just split on equal signs.
attrs = {}
for pair in f.read().split(" "):
key, value = pair.split("=", 1)
value = value.strip()
if value[0] == '"':
value = value[1:-1]
attrs[key] = value
# We need to register our namespaces before reading or writing any files.
ET.register_namespace("sparkle", "http://www.andymatuschak.org/xml-namespaces/sparkle")
# Open our existing appcast and find the channel element. This is where
# we'll add our new item.
et = ET.parse('appcast.xml')
channel = et.find("channel")
# Create the item using some absoultely terrible XML manipulation.
item = ET.SubElement(channel, "item")
elem = ET.SubElement(item, "title")
elem.text = f"Build {build}"
elem = ET.SubElement(item, "pubDate")
elem.text = now.strftime("%a, %d %b %Y %H:%M:%S %z")
elem = ET.SubElement(item, "sparkle:version")
elem.text = build
elem = ET.SubElement(item, "sparkle:shortVersionString")
elem.text = commit
elem = ET.SubElement(item, "sparkle:minimumSystemVersion")
elem.text = "12.0.0"
elem = ET.SubElement(item, "description")
elem.text = f"""
<p>Automated build from commit <code>{commit}</code>.</p>
<p>
These are automatic per-commit builds generated from the main Git branch.
We do not generate any release notes for these builds. You can view the full
commit history
<a href="https://github.com/mitchellh/ghostty">on GitHub</a> for all changes.
</p>
"""
elem = ET.SubElement(item, "enclosure")
elem.set("url", f"https://tip.files.ghostty.dev/{build}/ghostty-macos-universal.zip")
elem.set("type", "application/octet-stream")
for key, value in attrs.items():
elem.set(key, value)
# Output the new appcast.
et.write("appcast_new.xml", xml_declaration=True, encoding="utf-8")

View File

@ -91,6 +91,8 @@
<string>A program in Ghostty wants to use speech recognition.</string> <string>A program in Ghostty wants to use speech recognition.</string>
<key>NSSystemAdministrationUsageDescription</key> <key>NSSystemAdministrationUsageDescription</key>
<string>A program in Ghostty requires elevated privileges.</string> <string>A program in Ghostty requires elevated privileges.</string>
<key>SUPublicEDKey</key>
<string>wsNcGf5hirwtdXMVnYoxRIX/SqZQLMOsYlD3q3imeok=</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string></string> <string></string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>

View File

@ -13,6 +13,8 @@
A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */ = {isa = PBXBuildFile; fileRef = A51BFC1D2B2FB5CE00E92F16 /* About.xib */; }; A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */ = {isa = PBXBuildFile; fileRef = A51BFC1D2B2FB5CE00E92F16 /* About.xib */; };
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */; }; A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */; };
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC212B2FB6B400E92F16 /* AboutView.swift */; }; A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC212B2FB6B400E92F16 /* AboutView.swift */; };
A51BFC272B30F1B800E92F16 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = A51BFC262B30F1B800E92F16 /* Sparkle */; };
A51BFC2B2B30F6BE00E92F16 /* UpdateDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */; };
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */; }; A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */; };
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */; }; A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */; };
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; }; A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; };
@ -57,6 +59,8 @@
A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = "<group>"; }; A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = "<group>"; };
A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; }; A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; };
A51BFC212B2FB6B400E92F16 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; }; A51BFC212B2FB6B400E92F16 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
A51BFC282B30F26D00E92F16 /* GhosttyDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GhosttyDebug.entitlements; sourceTree = "<group>"; };
A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateDelegate.swift; sourceTree = "<group>"; };
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Input.swift; sourceTree = "<group>"; }; A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Input.swift; sourceTree = "<group>"; };
A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; }; A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
@ -102,6 +106,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A51BFC272B30F1B800E92F16 /* Sparkle in Frameworks */,
A56B880B2A840447007A0E29 /* Carbon.framework in Frameworks */, A56B880B2A840447007A0E29 /* Carbon.framework in Frameworks */,
A571AB1D2A206FCF00248498 /* GhosttyKit.xcframework in Frameworks */, A571AB1D2A206FCF00248498 /* GhosttyKit.xcframework in Frameworks */,
); );
@ -120,6 +125,14 @@
path = About; path = About;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
A51BFC292B30F69F00E92F16 /* Update */ = {
isa = PBXGroup;
children = (
A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */,
);
path = Update;
sourceTree = "<group>";
};
A53426362A7DC53000EBB7A2 /* Features */ = { A53426362A7DC53000EBB7A2 /* Features */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -128,6 +141,7 @@
A5E112912AF73E4D00C6E0C2 /* ClipboardConfirmation */, A5E112912AF73E4D00C6E0C2 /* ClipboardConfirmation */,
A534263E2A7DCC5800EBB7A2 /* Settings */, A534263E2A7DCC5800EBB7A2 /* Settings */,
A51BFC1C2B2FB5AB00E92F16 /* About */, A51BFC1C2B2FB5AB00E92F16 /* About */,
A51BFC292B30F69F00E92F16 /* Update */,
); );
path = Features; path = Features;
sourceTree = "<group>"; sourceTree = "<group>";
@ -221,6 +235,7 @@
A571AB1C2A206FC600248498 /* Ghostty-Info.plist */, A571AB1C2A206FC600248498 /* Ghostty-Info.plist */,
A5B30538299BEAAB0047F10C /* Assets.xcassets */, A5B30538299BEAAB0047F10C /* Assets.xcassets */,
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */, A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */,
A51BFC282B30F26D00E92F16 /* GhosttyDebug.entitlements */,
A54CD6ED299BEB14008C95BB /* Sources */, A54CD6ED299BEB14008C95BB /* Sources */,
A5D495A3299BECBA00DD1313 /* Frameworks */, A5D495A3299BECBA00DD1313 /* Frameworks */,
A5A1F8862A489D7400D1E8BC /* Resources */, A5A1F8862A489D7400D1E8BC /* Resources */,
@ -280,6 +295,9 @@
dependencies = ( dependencies = (
); );
name = Ghostty; name = Ghostty;
packageProductDependencies = (
A51BFC262B30F1B800E92F16 /* Sparkle */,
);
productName = Ghostty; productName = Ghostty;
productReference = A5B30531299BEAAA0047F10C /* Ghostty.app */; productReference = A5B30531299BEAAA0047F10C /* Ghostty.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@ -308,6 +326,9 @@
Base, Base,
); );
mainGroup = A5B30528299BEAAA0047F10C; mainGroup = A5B30528299BEAAA0047F10C;
packageReferences = (
A51BFC252B30F1B700E92F16 /* XCRemoteSwiftPackageReference "Sparkle" */,
);
productRefGroup = A5B30532299BEAAA0047F10C /* Products */; productRefGroup = A5B30532299BEAAA0047F10C /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@ -349,6 +370,7 @@
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */, A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
A59630972AEE163600D64628 /* HostingWindow.swift in Sources */, A59630972AEE163600D64628 /* HostingWindow.swift in Sources */,
A59630A02AEF6AEB00D64628 /* TerminalManager.swift in Sources */, A59630A02AEF6AEB00D64628 /* TerminalManager.swift in Sources */,
A51BFC2B2B30F6BE00E92F16 /* UpdateDelegate.swift in Sources */,
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */, A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */,
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */, A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */, A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
@ -496,7 +518,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_ENTITLEMENTS = Ghostty.entitlements; CODE_SIGN_ENTITLEMENTS = GhosttyDebug.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
@ -583,6 +605,25 @@
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
A51BFC252B30F1B700E92F16 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/sparkle-project/Sparkle";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.5.1;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
A51BFC262B30F1B800E92F16 /* Sparkle */ = {
isa = XCSwiftPackageProductDependency;
package = A51BFC252B30F1B700E92F16 /* XCRemoteSwiftPackageReference "Sparkle" */;
productName = Sparkle;
};
/* End XCSwiftPackageProductDependency section */
}; };
rootObject = A5B30529299BEAAA0047F10C /* Project object */; rootObject = A5B30529299BEAAA0047F10C /* Project object */;
} }

View File

@ -0,0 +1,14 @@
{
"pins" : [
{
"identity" : "sparkle",
"kind" : "remoteSourceControl",
"location" : "https://github.com/sparkle-project/Sparkle",
"state" : {
"revision" : "1f07f4096e52f19b5e7abaa697b7fc592b7ff57c",
"version" : "2.5.1"
}
}
],
"version" : 2
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.personal-information.addressbook</key>
<true/>
<key>com.apple.security.personal-information.calendars</key>
<true/>
<key>com.apple.security.personal-information.location</key>
<true/>
<key>com.apple.security.personal-information.photos-library</key>
<true/>
</dict>
</plist>

View File

@ -1,9 +1,15 @@
import AppKit import AppKit
import UserNotifications import UserNotifications
import OSLog import OSLog
import Sparkle
import GhosttyKit import GhosttyKit
class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNotificationCenterDelegate, GhosttyAppStateDelegate { class AppDelegate: NSObject,
ObservableObject,
NSApplicationDelegate,
UNUserNotificationCenterDelegate,
GhosttyAppStateDelegate
{
// The application logger. We should probably move this at some point to a dedicated // The application logger. We should probably move this at some point to a dedicated
// class/struct but for now it lives here! 🤷 // class/struct but for now it lives here! 🤷
static let logger = Logger( static let logger = Logger(
@ -12,6 +18,7 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
) )
/// Various menu items so that we can programmatically sync the keyboard shortcut with the Ghostty config. /// Various menu items so that we can programmatically sync the keyboard shortcut with the Ghostty config.
@IBOutlet private var menuCheckForUpdates: NSMenuItem?
@IBOutlet private var menuOpenConfig: NSMenuItem? @IBOutlet private var menuOpenConfig: NSMenuItem?
@IBOutlet private var menuReloadConfig: NSMenuItem? @IBOutlet private var menuReloadConfig: NSMenuItem?
@IBOutlet private var menuQuit: NSMenuItem? @IBOutlet private var menuQuit: NSMenuItem?
@ -56,8 +63,18 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
/// Manages our terminal windows. /// Manages our terminal windows.
let terminalManager: TerminalManager let terminalManager: TerminalManager
/// Manages updates
let updaterController: SPUStandardUpdaterController
let updaterDelegate: UpdaterDelegate = UpdaterDelegate()
override init() { override init() {
self.terminalManager = TerminalManager(ghostty) terminalManager = TerminalManager(ghostty)
updaterController = SPUStandardUpdaterController(
startingUpdater: true,
updaterDelegate: updaterDelegate,
userDriverDelegate: nil
)
super.init() super.init()
ghostty.delegate = self ghostty.delegate = self
@ -80,6 +97,10 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
"ApplePressAndHoldEnabled": false, "ApplePressAndHoldEnabled": false,
]) ])
// Hook up updater menu
menuCheckForUpdates?.target = updaterController
menuCheckForUpdates?.action = #selector(SPUStandardUpdaterController.checkForUpdates(_:))
// Let's launch our first window. We only do this if we have no other windows. It // Let's launch our first window. We only do this if we have no other windows. It
// is possible to have other windows if we're opening a URL since `application(_:openFile:)` // is possible to have other windows if we're opening a URL since `application(_:openFile:)`
// is called before this. // is called before this.

View File

@ -0,0 +1,11 @@
import Sparkle
class UpdaterDelegate: NSObject, SPUUpdaterDelegate {
func feedURLString(for updater: SPUUpdater) -> String? {
// Eventually w want to support multiple channels. Sparkle itself supports
// channels but we probably don't want some appcasts in the same file (i.e.
// tip) so this would be the place to change that. For now, we hardcode the
// tip appcast URL since it is all we support.
return "https://tip.files.ghostty.dev/appcast.xml"
}
}

View File

@ -14,6 +14,7 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="bbz-4X-AYv" userLabel="AppDelegate" customClass="AppDelegate" customModule="Ghostty" customModuleProvider="target"> <customObject id="bbz-4X-AYv" userLabel="AppDelegate" customClass="AppDelegate" customModule="Ghostty" customModuleProvider="target">
<connections> <connections>
<outlet property="menuCheckForUpdates" destination="GEA-5y-yzH" id="0nV-Tf-nJQ"/>
<outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/> <outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
<outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/> <outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/>
<outlet property="menuCloseWindow" destination="W5w-UZ-crk" id="6ff-BT-ENV"/> <outlet property="menuCloseWindow" destination="W5w-UZ-crk" id="6ff-BT-ENV"/>
@ -58,6 +59,9 @@
<action selector="showAbout:" target="-1" id="tGt-68-tLn"/> <action selector="showAbout:" target="-1" id="tGt-68-tLn"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Check for Updates..." id="GEA-5y-yzH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/> <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"> <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
<connections> <connections>