111 Commits

Author SHA1 Message Date
Micha
ae83ea7dab chore: release 26.0.69 2026-01-03 14:00:42 +01:00
Micha
39205230b6 Sparkle test 2026-01-03 13:58:38 +01:00
Micha
55a266014c chore: release 26.0.68 2026-01-03 13:52:34 +01:00
Micha
c002cab616 chore: disable sandbox for now to use Sparkle updates
Disable app-sandbox to allow Sparkle auto-updates to work properly.
Keep entitlements file structure for future sandbox re-enablement.
Sandbox integration with Sparkle requires more complex authorization
setup that can be tackled later when preparing for App Store.
2026-01-03 13:50:26 +01:00
Micha
117134cead chore: release 26.0.67 2026-01-03 13:36:30 +01:00
Micha
35711d33c0 Sparkle test 2026-01-03 13:34:50 +01:00
Micha
4f3d56dc3c chore: release 26.0.66 2026-01-03 13:28:45 +01:00
Micha
0d80a0f912 fix: use basic Sparkle updater for sandboxed apps
Disable SUEnableInstallerLauncherService and remove XPC entitlements.
Use Sparkle's standard update mechanism which works with sandboxed apps.
Add file access entitlements for update storage.
2026-01-03 13:26:37 +01:00
Micha
b1d6e61f05 chore: release 26.0.65 2025-12-30 20:19:29 +01:00
Micha
2dd2c2154f fix: sign Sparkle framework separately for sandboxed builds
Sign the Sparkle framework before signing the whole app to ensure
proper code signature chain for sandboxed installation.
2025-12-30 20:17:47 +01:00
Micha
c6ecbbe511 chore: release 26.0.64 2025-12-30 20:14:03 +01:00
Micha
77a145604c Sparkle test 2025-12-30 20:12:24 +01:00
Micha
0016030ff3 chore: release 26.0.63 2025-12-30 20:06:55 +01:00
Micha
615d664731 fix: configure sandbox for Sparkle installer with proper entitlements
- Add downloads folder read-write access for installer
- Enable SUEnableInstallerLauncherService for sandboxed update installation
- Keep XPC service entitlements for installer communication
2025-12-30 20:04:26 +01:00
Micha
5644fbdfe0 chore: release 26.0.62 2025-12-30 19:55:55 +01:00
Micha
9b5883fe77 Sparkle test 2025-12-30 19:54:08 +01:00
Micha
5d15810802 chore: release 26.0.61 2025-12-30 19:53:12 +01:00
Micha
519d15ed10 Sparkle test 2025-12-30 19:51:35 +01:00
Micha
446bbe7f98 chore: release 26.0.60 2025-12-30 19:50:43 +01:00
Micha
b67fffd3f0 fix: re-add XPC service entitlements for sandboxed Sparkle installer
Add back InstallerConnection and InstallerStatus entitlements which are
required for the sandboxed app to communicate with Sparkle's installer
XPC service.
2025-12-30 19:48:44 +01:00
Micha
84935ee8fd chore: release 26.0.59 2025-12-30 19:42:29 +01:00
Micha
0f266f7046 Sparkle test 2025-12-30 19:40:37 +01:00
Micha
bff7c44c29 chore: release 26.0.58 2025-12-30 19:36:29 +01:00
Micha
f930e8334f chore: remove configuration note from updates preferences
Remove the explanation text about configuring appcast URL and EdDSA key.
This is configuration for developers, not end users.
2025-12-30 19:33:14 +01:00
Micha
0032ad9b57 chore: remove update button from main toolbar
Remove the 'Check for Updates' button from the main window toolbar.
Updates are available via Preferences → Updates, which is sufficient.
2025-12-30 19:29:01 +01:00
Micha
b7f5d1a762 chore: remove update logs UI from preferences
Remove the Show/Hide Logs button and logs view from the Updates preferences
tab. Keep the logging infrastructure in SparkleUpdater for diagnostics,
but don't display it in the UI.
2025-12-30 19:27:06 +01:00
Micha
5dc5621871 docs: update changelog with Sparkle updater fixes and improvements
Document the key fixes that made Sparkle updates work:
- Using ditto instead of zip to preserve code signatures
- XPC service entitlements for sandboxed apps
- In-app logging for update debugging
- Re-enabled sandbox with minimal entitlements
2025-12-30 19:23:36 +01:00
Micha
2a848c3251 chore: release 26.0.57 2025-12-30 19:20:11 +01:00
Micha
bb4f972d58 feat: re-enable sandbox with minimal entitlements
- Re-enable app-sandbox
- Add network.client entitlement (required for Sparkle updates)
- Keep build script passing entitlements to codesign
- Use ditto for ZIP to preserve code signatures

This is a minimal sandbox configuration focused on security while
keeping updates working.
2025-12-30 19:14:40 +01:00
Micha
62d4a9ac96 chore: release 26.0.56 2025-12-30 19:09:35 +01:00
Micha
75fe670779 fix: use ditto instead of zip to preserve code signatures
zip breaks code signatures on macOS. Use ditto -c -k to create the update
ZIP archive while preserving the embedded code signature of the app bundle.
2025-12-30 19:07:10 +01:00
Micha
a961baab28 chore: release 26.0.55 2025-12-30 18:59:42 +01:00
Micha
7246b132f6 Sparkle test 2025-12-30 18:58:04 +01:00
Micha
1afce31641 chore: release 26.0.54 2025-12-30 18:52:40 +01:00
Micha
281016bfc9 fix: remove entitlements from code signing for non-sandboxed app
For non-sandboxed apps, don't pass --entitlements to codesign.
This was causing code signature issues.
2025-12-30 18:50:41 +01:00
Micha
144ad27aa6 chore: release 26.0.53 2025-12-30 18:43:27 +01:00
Micha
aa655bb7d6 Sparkle test 2025-12-30 18:41:33 +01:00
Micha
1ac34e1f04 chore: release 26.0.52 2025-12-30 18:33:44 +01:00
Micha
5f045c113a chore: simplify to non-sandboxed app with no entitlements
Remove all sandbox and XPC entitlements to test if Sparkle works without them.
2025-12-30 18:31:17 +01:00
Micha
2dbe739c97 chore: release 26.0.51 2025-12-30 18:24:24 +01:00
Micha
da9dd2f7ed Sparkle test 2025-12-30 18:22:30 +01:00
Micha
2ae67b6675 chore: release 26.0.50 2025-12-30 18:10:29 +01:00
Micha
989717539c Sparkle test 2025-12-30 18:08:49 +01:00
Micha
6d05419abb chore: release 26.0.49 2025-12-30 18:03:29 +01:00
Micha
be37bf526a Sparkle test 2025-12-30 18:01:57 +01:00
Micha
6c1f5c6d25 chore: release 26.0.48 2025-12-30 17:20:31 +01:00
Micha
32f97ff7d4 Sparkle test 2025-12-30 17:18:45 +01:00
Micha
dbbe1752d1 fix: disable InstallerLauncherService for sandboxed app
For sandboxed apps, use standard Sparkle updater instead of the
InstallerLauncherService. Also add Downloader XPC service identifiers.
2025-12-30 16:40:27 +01:00
Micha
2fe9821ac1 chore: release 26.0.47 2025-12-30 16:07:14 +01:00
Micha
87d4bffb99 Sparkle test 2025-12-30 16:05:16 +01:00
Micha
92782716fc chore: release 26.0.46 2025-12-30 16:00:25 +01:00
Micha
002c9e8cf2 Sparkle test 2025-12-30 15:58:45 +01:00
Micha
8820244589 chore: release 26.0.45 2025-12-30 15:51:46 +01:00
Micha
48d2f0ea42 Sparkle test 2025-12-30 15:49:57 +01:00
Micha
1947d05d78 fix: specify exact Sparkle XPC service identifiers
Change XPC entitlements from boolean true to arrays with specific
service identifiers for InstallerConnection and InstallerStatus.
2025-12-30 15:45:10 +01:00
Micha
86039cd5a9 chore: release 26.0.44 2025-12-30 15:35:40 +01:00
Micha
4f9c008498 Sparkle test 2025-12-30 15:33:58 +01:00
Micha
76818578b9 chore: remove duplicate v26.0.43 entry with incorrect size 2025-12-30 15:31:51 +01:00
Micha
9070882f38 fix: add XPC service entitlements for Sparkle installer
Add com.apple.security.xpc.aConnectionServices and
com.apple.security.xpc.aStatusServices entitlements to allow sandboxed
app to communicate with Sparkle's Installer and Downloader XPC services.
2025-12-30 15:27:19 +01:00
Micha
541927c30a chore: release 26.0.43 2025-12-30 15:18:43 +01:00
Micha
ab3a7ca469 Sparkle test 2025-12-30 15:17:03 +01:00
Micha
ee27efc0d4 chore: release 26.0.42 2025-12-30 14:23:40 +01:00
Micha
c3f445e3c3 Sparkle test 2025-12-30 14:21:53 +01:00
Micha
215c24d5a2 improvement: enhance Sparkle error logging with error codes
Add error domain and code to abort error messages to help diagnose installation failures.
2025-12-30 13:27:35 +01:00
Micha
b96b018f70 chore: release 26.0.41 2025-12-30 13:18:57 +01:00
Micha
65a65939a7 chore: remove local appcast testing scripts
Remove make_local_appcast.sh and serve_local_appcast.sh as they added
complexity without sufficient benefit. Test updates directly with published releases.
2025-12-30 13:15:27 +01:00
Micha
25723b7f07 feat: add in-app Sparkle update logging
- Add published logMessages array to SparkleUpdater to track all update events
- Display logs in the Updates preferences tab with Show/Hide toggle
- Each log entry is timestamped and shows both info and error messages
- Logs persist during session with max 100 entries
- Users can clear logs manually
- Helps diagnose update failures directly in the app UI
2025-12-30 13:12:27 +01:00
Micha
10683ebc73 chore: release 26.0.40 2025-12-30 13:01:20 +01:00
Micha
393bcf27e1 Sparkle test 2025-12-30 12:59:38 +01:00
Micha
839a513fde chore: release 26.0.39 2025-12-30 12:48:55 +01:00
Micha
77e82753ba chore: remove duplicate appcast entry for v26.0.38
Remove the old build 79 entry (from Dec 8) and keep only the new properly-signed build 80.
2025-12-30 12:46:35 +01:00
Micha
bbb0b580b0 chore: release 26.0.38 2025-12-08 19:31:41 +01:00
Micha
dd225b2b8e Sparkle fixes 2025-12-08 19:30:12 +01:00
Micha
76b01352ac chore: release 26.0.37 2025-12-08 19:07:36 +01:00
Micha
fcca8cee38 Sparkle fixes 2025-12-08 19:05:49 +01:00
Micha
94d1b3fec4 chore: release 26.0.36 2025-12-08 18:45:18 +01:00
Micha
4352ae1476 Sparkle fixes 2025-12-08 18:43:48 +01:00
Micha
846e0b149b chore: release 26.0.35 2025-12-08 18:37:49 +01:00
Micha
11ca4dbede Sparkle fixes 2025-12-08 18:36:23 +01:00
Micha
1d8bdfe491 chore: release 26.0.34 2025-12-08 18:27:08 +01:00
Micha
4f5a07822f Sparkle fixes 2025-12-08 18:25:34 +01:00
Micha
67709dfda6 chore: release 26.0.33 2025-12-07 20:22:09 +01:00
Micha
6753226087 Sparkle fixes 2025-12-07 20:20:34 +01:00
Micha
a3671acf38 chore: release 26.0.32 2025-12-07 20:18:04 +01:00
Micha
0aa773a0b3 Sparkle fixes 2025-12-07 20:16:37 +01:00
Micha
adbc061d0b chore: release 26.0.31 2025-12-07 17:52:45 +01:00
Micha
4deae63d43 Sparkle fixes 2025-12-07 17:50:58 +01:00
Micha
b570006074 chore: release 26.0.30 2025-12-07 17:48:38 +01:00
Micha
fd0d8d1adb Sparkle fixes 2025-12-07 17:46:59 +01:00
Micha
78d5bd9bd5 chore: release 26.0.29 2025-12-07 17:07:33 +01:00
Micha
091fd4ef38 Sparkle fixes 2025-12-07 17:05:51 +01:00
Micha
656d6403fd chore: release 26.0.28 2025-12-07 17:02:20 +01:00
Micha
db4c2aa930 Sparkle fixes 2025-12-07 16:52:13 +01:00
Micha
8f72fd0fea chore: release 26.0.27 2025-12-07 16:47:34 +01:00
Micha
ace1a008ef chore: release 26.0.26 2025-11-26 20:18:06 +01:00
Micha
354488d623 more Sparkle tests 2025-11-26 20:16:34 +01:00
Micha
61392a16a2 more Sparkle tests 2025-11-26 20:14:59 +01:00
Micha
d46e0450bf more Sparkle tests 2025-11-26 20:11:32 +01:00
Micha
5905ae5aa7 more Sparkle tests 2025-11-26 20:09:06 +01:00
Micha
9a6609df7b chore: release 26.0.22 2025-11-26 18:54:55 +01:00
Micha
eadd8e9d28 more Sparkle tests 2025-11-26 18:53:14 +01:00
Micha
473354a50a chore: release 26.0.21 2025-11-26 18:44:42 +01:00
Micha
1945b1dedb more Sparkle tests 2025-11-26 18:43:01 +01:00
Micha
dc7a516b6d chore: release 26.0.20 2025-11-26 18:36:43 +01:00
Micha
0d013e64f7 more Sparkle tests 2025-11-26 18:35:05 +01:00
Micha
c2cd66d2a3 chore: release 26.0.19 2025-11-26 18:03:44 +01:00
Micha
ad50433406 more Sparkle tests 2025-11-26 18:02:13 +01:00
Micha
fd83208760 chore: release 26.0.18 2025-11-25 19:17:04 +01:00
Micha
05017ffd5e more Sparkle tests 2025-11-25 19:15:25 +01:00
Micha
24794a1d63 chore: release 26.0.17 2025-11-25 19:04:13 +01:00
Micha
d65ec99cfb more Sparkle tests 2025-11-25 19:03:05 +01:00
18 changed files with 340 additions and 87 deletions

View File

@@ -1,6 +1,24 @@
# Changelog
## Unreleased
### Fixed
- Fixed Sparkle updater ZIP archive creation: replaced `zip` command with `ditto` to properly preserve app bundle code signatures during extraction, resolving "damaged app" errors on update installation.
- Fixed code signature issues for sandboxed apps by removing entitlements parameter from non-sandboxed builds.
- Fixed Sparkle framework deep code signing to handle complex framework structure.
- Fixed missing XPC service entitlements (`com.apple.security.xpc.aConnectionServices`, `com.apple.security.xpc.aStatusServices`) required for Sparkle installer to communicate with sandboxed app.
### Changed
- Re-enabled app sandbox with minimal entitlements (network.client only) for improved security while maintaining Sparkle update functionality.
- Enhanced Sparkle error logging to include error domain and code information, making update failures easier to diagnose.
- Updated build script to use `ditto -c -k --keepParent` for creating update ZIPs, which properly preserves code signatures that `zip` command breaks.
### Added
- Added in-app Sparkle update logs in Preferences → Updates tab with Show/Hide toggle for real-time debugging of update operations.
- Log entries include timestamps and distinguish between info and error messages.
- Users can clear logs manually and logs persist during the session (max 100 entries).
### Previous Changes
- Flattened the project structure so sources live at the repository root instead of the nested `iKeyMon/` folder and updated the Xcode project accordingly.
- Fixed build settings (entitlements, preview assets) and placeholder previews to work with the new layout.
- Migrated the updated API layer and unified `ServerInfo` model from the previous branch.
@@ -10,5 +28,5 @@
- Introduced repository-wide version management via `version.json` + `scripts/sync_version.sh`, ensuring Xcode targets and release artifacts stay aligned.
- Enhanced `scripts/build_release.sh` to timestamp/harden signatures, notarize DMGs, and optionally publish tagged releases (pre-release by default) with ZIP/DMG assets directly to Gitea when credentials are configured.
- Integrated Sparkle (via Swift Package Manager) to handle automatic update checks, downloads, signature verification, and relaunches, replacing the previous custom updater UI. Preferences now simply surface Sparkle's check/download toggles.
- `scripts/build_release.sh` can optionally run Sparkles `generate_appcast` (when signing key and download prefix env vars are set), producing a ready-to-host `appcast.xml` alongside the ZIP/DMG artifacts.
- `scripts/build_release.sh` can optionally run Sparkle's `generate_appcast` (when signing key and download prefix env vars are set), producing a ready-to-host `appcast.xml` alongside the ZIP/DMG artifacts.
- Further reduced MainView console noise by removing redundant refresh/onAppear logs.

View File

@@ -5,5 +5,5 @@
add a marker for "reboot required"
dummy
1112

View File

@@ -60,6 +60,7 @@ GITEA_REPO="iKeyMon"
# optional Sparkle feed helpers:
# SPARKLE_EDDSA_KEY_FILE="$HOME/.config/Sparkle/iKeyMon.key"
# SPARKLE_DOWNLOAD_BASE_TEMPLATE="https://git.24unix.net/tracer/iKeyMon/releases/download/v{{VERSION}}"
# If you prefer SPARKLE_DOWNLOAD_BASE_URL, it will automatically append `/v<version>` for you.
# SPARKLE_APPCAST_OUTPUT="$ROOT_DIR/Sparkle/appcast.xml" # default
```
@@ -72,15 +73,25 @@ If you re-run the release script for the same version, it removes any existing a
iKeyMon uses [Sparkle](https://sparkle-project.org/) for macOS-safe updates.
1. Generate an EdDSA key pair once (`./Packages/Sparkle/bin/generate_keys`). Store the private key on-disk (for example `~/.config/Sparkle/iKeyMon.key`, which the build script expects) and copy the public key into the `SUPublicEDKey` entry (see Info.plist notes below).
2. `./scripts/build_release.sh` signs the ZIP with Sparkles `sign_update` tool and invokes `generate_appcast` automatically when the Sparkle variables are present. The generated feed is written to `Sparkle/appcast.xml`, so commit that file after every release. Point `SPARKLE_DOWNLOAD_BASE_TEMPLATE` at your release download prefix to ensure the feed URLs resolve correctly. The feed stays inside the repo (it is not uploaded as a release asset).
2. `./scripts/build_release.sh` signs the ZIP with Sparkles `sign_update` tool and invokes `generate_appcast` automatically when the Sparkle variables are present. The generated feed is written to `Sparkle/appcast.xml`, so commit that file after every release. Point `SPARKLE_DOWNLOAD_BASE_TEMPLATE` at your release-download prefix (e.g. `https://git.24unix.net/tracer/iKeyMon/releases/download/v{{VERSION}}`) so the generated URLs already match Giteas asset paths. The feed stays inside the repo (it is not uploaded as a release asset).
3. Set `SUFeedURL` in Info.plist (or the corresponding build setting) to the raw URL of `Sparkle/appcast.xml` inside this repo (e.g. `https://git.24unix.net/tracer/iKeyMon/raw/branch/master/Sparkle/appcast.xml`).
Preferences expose Sparkles built-in toggles for “Automatically check” and “Automatically download”, and the toolbar button simply calls Sparkles “Check for Updates…” sheet.
> `./scripts/build_release.sh` will call `generate_appcast` for you when `SPARKLE_EDDSA_KEY_FILE` and either `SPARKLE_DOWNLOAD_BASE_TEMPLATE` (with `{{VERSION}}` placeholder) or `SPARKLE_DOWNLOAD_BASE_URL` are set. It tries to locate Sparkles CLI in DerivedData automatically, but you can override the path via `SPARKLE_GENERATE_APPCAST`. The resulting feed is written to `SPARKLE_APPCAST_OUTPUT` (defaults to `Sparkle/appcast.xml`).
> `./scripts/build_release.sh` will call `generate_appcast` for you when `SPARKLE_EDDSA_KEY_FILE` and `SPARKLE_DOWNLOAD_BASE_TEMPLATE` (or `SPARKLE_DOWNLOAD_BASE_URL`) are set. It tries to locate Sparkles CLI in DerivedData automatically, but you can override the path via `SPARKLE_GENERATE_APPCAST`. The resulting feed is written to `SPARKLE_APPCAST_OUTPUT` (defaults to `Sparkle/appcast.xml`).
> Build settings include `INFOPLIST_KEY_SUFeedURL` and `INFOPLIST_KEY_SUPublicEDKey`. Make sure to fill both before shipping a build so Sparkle knows where to fetch updates and how to verify them.
#### Debugging Sparkle updates
Launch the shipped app via CLI with `SPARKLE_VERBOSE_LOGGING=1` to mirror Sparkles activity in stdout/stderr:
```bash
SPARKLE_VERBOSE_LOGGING=1 /Applications/iKeyMon.app/Contents/MacOS/iKeyMon
```
Sparkles installer helper runs in a separate `SUPipedBinary` process. If the installer fails, collect additional details with `log show --process SUPipedBinary --last 5m`.
### Automated release push
If you want `git push origin master` to build/sign/notarize/upload automatically, enable the provided pre-push hook:

View File

@@ -65,8 +65,9 @@ struct ServerInfo: Codable, Hashable, Equatable {
}
struct PHPInterpreter: Codable, Hashable, Identifiable, Equatable {
var id: String { versionFull }
var id: String { [fullVersion, path ?? ""].joined(separator: "|") }
let version: String
let fullVersion: String
let path: String?
let configFile: String?
let extensions: [String]
@@ -75,6 +76,7 @@ struct ServerInfo: Codable, Hashable, Equatable {
init(
version: String,
fullVersion: String? = nil,
path: String? = nil,
configFile: String? = nil,
extensions: [String] = [],
@@ -82,6 +84,7 @@ struct ServerInfo: Codable, Hashable, Equatable {
maxExecutionTime: String? = nil
) {
self.version = version
self.fullVersion = fullVersion ?? version
self.path = path
self.configFile = configFile
self.extensions = extensions
@@ -89,8 +92,8 @@ struct ServerInfo: Codable, Hashable, Equatable {
self.maxExecutionTime = maxExecutionTime
}
var versionFull: String {
var components = [version]
var versionWithPath: String {
var components = [fullVersion]
if let path, !path.isEmpty {
components.append(path)
}
@@ -162,12 +165,18 @@ struct ServerInfo: Codable, Hashable, Equatable {
}
var formattedServerTime: String {
guard let date = ServerInfo.isoFormatter.date(from: serverTime) else {
return serverTime
}
let normalizedServerTime = ServerInfo.normalizedServerTime(serverTime)
if let date = ServerInfo.serverTimeParsers
.lazy
.compactMap({ $0.date(from: normalizedServerTime) })
.first {
return ServerInfo.displayFormatter.string(from: date)
}
return serverTime
}
var operatingSystemSummary: String? {
guard let operatingSystem else { return nil }
let components = [
@@ -181,19 +190,42 @@ struct ServerInfo: Codable, Hashable, Equatable {
// MARK: - Helpers & Sample Data
extension ServerInfo {
private static let isoFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds, .withColonSeparatorInTimeZone]
return formatter
private static let serverTimeParsers: [ISO8601DateFormatter] = {
let withFractional = ISO8601DateFormatter()
withFractional.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let withoutFractional = ISO8601DateFormatter()
withoutFractional.formatOptions = [.withInternetDateTime]
let noColonTimeZone = ISO8601DateFormatter()
noColonTimeZone.formatOptions = [.withFullDate, .withTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
return [withFractional, withoutFractional, noColonTimeZone]
}()
private static let displayFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = .autoupdatingCurrent
formatter.dateStyle = .medium
formatter.timeStyle = .medium
return formatter
}()
private static func normalizedServerTime(_ value: String) -> String {
if value.range(of: #"[+-]\d{2}:\d{2}$"#, options: .regularExpression) != nil {
return value
}
guard value.range(of: #"[+-]\d{4}$"#, options: .regularExpression) != nil else {
return value
}
var normalized = value
let insertionIndex = normalized.index(normalized.endIndex, offsetBy: -2)
normalized.insert(":", at: insertionIndex)
return normalized
}
static let placeholder = ServerInfo(
hostname: "preview.example.com",
ipAddresses: ["192.168.1.1", "fe80::1"],

View File

@@ -278,6 +278,7 @@ private extension APIv2_12 {
struct AdditionalInterpreter: Decodable {
let version: String
let versionFull: String?
let path: String?
let configFile: String?
}
@@ -398,6 +399,7 @@ private extension APIv2_12 {
additionalPHPInterpreters: additionalPhpInterpreters?.map {
ServerInfo.PHPInterpreter(
version: $0.version,
fullVersion: $0.versionFull,
path: $0.path,
configFile: $0.configFile,
extensions: [],

View File

@@ -278,6 +278,7 @@ private extension APIv2_13 {
struct AdditionalInterpreter: Decodable {
let version: String
let versionFull: String?
let path: String?
let configFile: String?
}
@@ -398,6 +399,7 @@ private extension APIv2_13 {
additionalPHPInterpreters: additionalPhpInterpreters?.map {
ServerInfo.PHPInterpreter(
version: $0.version,
fullVersion: $0.versionFull,
path: $0.path,
configFile: $0.configFile,
extensions: [],

View File

@@ -1,13 +1,21 @@
import Sparkle
import Foundation
import OSLog
@MainActor
final class SparkleUpdater: NSObject, ObservableObject {
let controller: SPUStandardUpdaterController
private lazy var controller: SPUStandardUpdaterController = {
SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: self, userDriverDelegate: nil)
}()
private let logger = Logger(subsystem: "net.24unix.iKeyMon", category: "Sparkle")
private let verboseLogging: Bool
@Published var logMessages: [String] = []
override init() {
self.controller = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
self.verboseLogging = ProcessInfo.processInfo.environment["SPARKLE_VERBOSE_LOGGING"] == "1"
super.init()
_ = controller
log("Sparkle updater initialized (verbose=\(verboseLogging)).")
}
var automaticallyChecksForUpdates: Bool {
@@ -21,6 +29,99 @@ final class SparkleUpdater: NSObject, ObservableObject {
}
func checkForUpdates() {
log("Manual check for updates triggered.")
controller.checkForUpdates(nil)
}
private func log(_ message: String) {
logger.log("\(message, privacy: .public)")
addLogMessage("[INFO] \(message)")
if verboseLogging {
print("[Sparkle] \(message)")
}
}
private func logError(_ message: String) {
logger.error("\(message, privacy: .public)")
addLogMessage("[ERROR] \(message)")
if verboseLogging {
fputs("[Sparkle][error] \(message)\n", stderr)
}
}
private func addLogMessage(_ message: String) {
let timestamp = DateFormatter.localizedString(from: Date(), dateStyle: .none, timeStyle: .medium)
let timestampedMessage = "[\(timestamp)] \(message)"
logMessages.append(timestampedMessage)
if logMessages.count > 100 {
logMessages.removeFirst()
}
}
private func describe(update item: SUAppcastItem) -> String {
let short = item.displayVersionString
let build = item.versionString
return "\(short) (build \(build))"
}
}
extension SparkleUpdater: SPUUpdaterDelegate {
nonisolated func updater(_ updater: SPUUpdater, didFinishLoading appcast: SUAppcast) {
Task { @MainActor in
log("Loaded Sparkle appcast containing \(appcast.items.count) item(s).")
}
}
nonisolated func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) {
Task { @MainActor in
log("Found valid update \(describe(update: item))")
}
}
nonisolated func updaterDidNotFindUpdate(_ updater: SPUUpdater) {
Task { @MainActor in
log("No updates available.")
}
}
nonisolated func updater(_ updater: SPUUpdater, willDownloadUpdate item: SUAppcastItem, with request: NSMutableURLRequest) {
Task { @MainActor in
log("Downloading \(describe(update: item)) from \(request.url?.absoluteString ?? "unknown URL")")
}
}
nonisolated func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) {
Task { @MainActor in
log("Finished downloading \(describe(update: item))")
}
}
nonisolated func updater(_ updater: SPUUpdater, failedToDownloadUpdate item: SUAppcastItem, error: Error) {
Task { @MainActor in
logError("Failed to download \(describe(update: item)): \(error.localizedDescription)")
}
}
nonisolated func userDidCancelDownload(_ updater: SPUUpdater) {
Task { @MainActor in
log("User cancelled Sparkle download.")
}
}
nonisolated func updater(_ updater: SPUUpdater, willInstallUpdate item: SUAppcastItem) {
Task { @MainActor in
log("Will install update \(describe(update: item))")
}
}
nonisolated func updater(_ updater: SPUUpdater, didAbortWithError error: Error) {
Task { @MainActor in
let errorDescription = error as NSError
let details = "Domain: \(errorDescription.domain), Code: \(errorDescription.code), Description: \(error.localizedDescription)"
logError("Sparkle aborted: \(details)")
if let underlying = errorDescription.userInfo[NSUnderlyingErrorKey] as? NSError {
logError("Underlying error: Domain: \(underlying.domain), Code: \(underlying.code), Description: \(underlying.localizedDescription)")
}
}
}
}

View File

@@ -5,12 +5,6 @@
// Created by tracer on 03.04.25.
//
//
// ResourcesBarRow.swift
// iKeyMon
//
// Created by tracer on 31.03.25.
//
import SwiftUI
@@ -32,6 +26,8 @@ struct InfoCell: View {
.font(monospaced ? .system(.body, design: .monospaced) : .body)
}
}
// if let subtext {
// Text(subtext)
// .font(.caption)

View File

@@ -12,7 +12,6 @@ struct MainView: View {
private static let serverOrderKeyStatic = "serverOrder"
private static let storedServersKeyStatic = "storedServers"
@EnvironmentObject private var sparkleUpdater: SparkleUpdater
@State var showAddServerSheet: Bool = false
@State private var serverBeingEdited: Server?
@State private var serverToDelete: Server?
@@ -60,14 +59,6 @@ struct MainView: View {
}
.help("Add Host")
}
ToolbarItem {
Button {
sparkleUpdater.checkForUpdates()
} label: {
Image(systemName: "square.and.arrow.down")
}
.help("Check for Updates")
}
}
.navigationTitle("Servers")
.onChange(of: selectedServerID) {

View File

@@ -252,11 +252,6 @@ private struct UpdatesPreferencesView: View {
Label("Check for Updates Now", systemImage: "sparkles")
}
Text("Updates are delivered via Sparkle. Configure your appcast URL and public EdDSA key in Info.plist (keys `SUFeedURL` and `SUPublicEDKey`).")
.font(.caption)
.foregroundColor(.secondary)
.padding(.top, 4)
Spacer()
}
.toggleStyle(.switch)

View File

@@ -106,7 +106,13 @@ struct GeneralView: View {
if interpreters.isEmpty {
return ["None"]
}
return interpreters.map { $0.versionFull }
let versions = interpreters
.map { $0.fullVersion }
.filter { !$0.isEmpty }
if versions.isEmpty {
return ["None"]
}
return [versions.joined(separator: "")]
}(),
monospaced: true
)

32
Sparkle/appcast.xml vendored
View File

@@ -1,30 +1,30 @@
<?xml version="1.0" standalone="yes"?>
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<title>iKeyMon</title>
<item>
<title>26.0.16</title>
<pubDate>Tue, 25 Nov 2025 18:34:19 +0100</pubDate>
<sparkle:version>39</sparkle:version>
<sparkle:shortVersionString>26.0.16</sparkle:shortVersionString>
<title>26.0.69</title>
<pubDate>Sat, 03 Jan 2026 14:00:40 +0100</pubDate>
<sparkle:version>150</sparkle:version>
<sparkle:shortVersionString>26.0.69</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/iKeyMon-26.0.16.zip" length="4801351" type="application/octet-stream" sparkle:edSignature="lbQEpxEElRxwyRdm0LQIxsnfh8o8Kt66wQlcl4PBs68lBmjkq0b/5EsVCElWQb0Nei/GCk6I/m2mSNL7mA3wBQ=="/>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.69/iKeyMon-26.0.69.zip" length="2993457" type="application/octet-stream" sparkle:edSignature="cIqWamcPRsxA7zPaGcUuUOqLYs5KTcoAgXQkhblCF+Wc2tEnGHFVysARtMH68jGq7ObfhDuI3oZJNg857rQ0Dg=="/>
</item>
<item>
<title>26.0.15</title>
<pubDate>Tue, 25 Nov 2025 18:11:17 +0100</pubDate>
<sparkle:version>35</sparkle:version>
<sparkle:shortVersionString>26.0.15</sparkle:shortVersionString>
<title>26.0.68</title>
<pubDate>Sat, 03 Jan 2026 13:52:33 +0100</pubDate>
<sparkle:version>148</sparkle:version>
<sparkle:shortVersionString>26.0.68</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/iKeyMon-26.0.15.zip" length="4801128" type="application/octet-stream" sparkle:edSignature="T16+tX44yN2UqIUsMJeZAxydOuLC6lcQQrlRElTkJlSWPheWLy9xPjP4T45mNSOcWTax0gRCnI50ab3geL9XAA=="/>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.68/iKeyMon-26.0.68.zip" length="2993469" type="application/octet-stream" sparkle:edSignature="M5WBkO4BN8RwMJ0ZU3Ku4CyQllnbEzz9X6MYR4IVX5prO9oyMBGoceHA3C97wZA6+++9u7RnRsKrFvei2CsWBQ=="/>
</item>
<item>
<title>26.0.15</title>
<pubDate>Tue, 25 Nov 2025 17:42:56 +0100</pubDate>
<sparkle:version>34</sparkle:version>
<sparkle:shortVersionString>26.0.15</sparkle:shortVersionString>
<title>26.0.67</title>
<pubDate>Sat, 03 Jan 2026 13:36:29 +0100</pubDate>
<sparkle:version>146</sparkle:version>
<sparkle:shortVersionString>26.0.67</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/iKeyMon-26.0.15.zip" length="4800821" type="application/octet-stream" sparkle:edSignature="bojJ638CY0n+34POoJX3OBrXRAiPOYPiDTfgJOS9fCslw8YGKZLviJvcExC2PKh1HDt0Raabo0FJUJrAFUMmBQ=="/>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.67/iKeyMon-26.0.67.zip" length="2993400" type="application/octet-stream" sparkle:edSignature="kr7vKMSM0002I/Fx2KVFGqNA6uMHb5Ll6Cr8NSG+8/Ct6KkC9dAwcd50xeUVPVJ7UT8lNBVPoBjZoFssgIEPAw=="/>
</item>
</channel>
</rss>

View File

@@ -3,9 +3,11 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REMOTE_NAME="${1:-origin}"
QUIET_RELEASE="${QUIET_RELEASE:-1}"
RELEASE_LOG="${RELEASE_LOG:-$ROOT_DIR/build/release.log}"
if [[ -n "${SKIP_RELEASE:-}" ]]; then
echo "⚙️ SKIP_RELEASE set — skipping automated release build."
echo "release: skipped (SKIP_RELEASE=1)"
exit 0
fi
@@ -34,29 +36,49 @@ if [[ "$should_release" != true ]]; then
exit 0
fi
echo "🚀 Detected push to master — bumping version and building release..."
if [[ "$QUIET_RELEASE" == "1" ]]; then
mkdir -p "$(dirname "$RELEASE_LOG")"
: >"$RELEASE_LOG"
fi
run_logged() {
if [[ "$QUIET_RELEASE" == "1" ]]; then
"$@" >>"$RELEASE_LOG" 2>&1
else
"$@"
fi
}
if [[ "$QUIET_RELEASE" == "1" ]]; then
NEW_VERSION="$("$ROOT_DIR/scripts/bump_version.sh" 2>>"$RELEASE_LOG" | tee -a "$RELEASE_LOG")"
else
NEW_VERSION="$("$ROOT_DIR/scripts/bump_version.sh")"
echo "🔢 marketing_version -> ${NEW_VERSION}"
"$ROOT_DIR/scripts/sync_version.sh"
fi
run_logged "$ROOT_DIR/scripts/sync_version.sh"
git -C "$ROOT_DIR" add "$ROOT_DIR/version.json" "$ROOT_DIR/iKeyMon.xcodeproj/project.pbxproj"
"$ROOT_DIR/scripts/build_release.sh"
echo "release: building v${NEW_VERSION}..."
if ! run_logged "$ROOT_DIR/scripts/build_release.sh"; then
echo "release: failed (log: $RELEASE_LOG)"
exit 1
fi
git -C "$ROOT_DIR" add "$ROOT_DIR/version.json" "$ROOT_DIR/iKeyMon.xcodeproj/project.pbxproj" "$ROOT_DIR/Sparkle/appcast.xml"
if git -C "$ROOT_DIR" diff --cached --quiet; then
echo "⚠️ No release changes detected; skipping release commit."
echo "release: no changes detected; skipping commit"
else
git -C "$ROOT_DIR" commit -m "chore: release ${NEW_VERSION}"
echo "📝 Committed release ${NEW_VERSION}."
run_logged git -C "$ROOT_DIR" commit -m "chore: release ${NEW_VERSION}" || {
echo "release: commit failed (log: $RELEASE_LOG)"
exit 1
}
fi
echo "📤 Pushing release commit..."
if SKIP_RELEASE=1 git -C "$ROOT_DIR" push "$REMOTE_NAME" "${release_local_ref:-refs/heads/master}:${release_remote_ref:-refs/heads/master}"; then
echo "✅ Release ${NEW_VERSION} pushed. Original push cancelled (already done)."
if SKIP_RELEASE=1 git -C "$ROOT_DIR" push --quiet "$REMOTE_NAME" "${release_local_ref:-refs/heads/master}:${release_remote_ref:-refs/heads/master}"; then
echo "release: success v${NEW_VERSION}"
exit 1
else
echo "❌ Failed to push release ${NEW_VERSION}. Please resolve manually."
echo "release: push failed (log: $RELEASE_LOG)"
exit 1
fi

View File

@@ -6,5 +6,7 @@
<string>https://git.24unix.net/tracer/iKeyMon/raw/branch/master/Sparkle/appcast.xml</string>
<key>SUPublicEDKey</key>
<string>EgJgrOGQ79L5me616jA7kDCEOgx+Rg11uYLYLLIyzTI=</string>
<key>SUEnableInstallerLauncherService</key>
<false/>
</dict>
</plist>

View File

@@ -3,10 +3,12 @@
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<false/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>

View File

@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
5221016D2EE5E82700D04952 /* appcast.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5221016B2EE5E82700D04952 /* appcast.xml */; };
52A9B79F2EC8E7EE004DD4A2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52A9B7872EC8E7EE004DD4A2 /* Assets.xcassets */; };
52A9B8222EC8FA8A004DD4A2 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 52A9B8212EC8FA8A004DD4A2 /* CHANGELOG.md */; };
52A9B9722ECF751C004DD4A2 /* signing.env.example in Resources */ = {isa = PBXBuildFile; fileRef = 52A9B9712ECF751C004DD4A2 /* signing.env.example */; };
@@ -28,6 +29,7 @@
/* Begin PBXFileReference section */
5203C24D2D997D2800576D4A /* iKeyMon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iKeyMon.app; sourceTree = BUILT_PRODUCTS_DIR; };
5221016B2EE5E82700D04952 /* appcast.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = appcast.xml; sourceTree = "<group>"; };
52A9B7872EC8E7EE004DD4A2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
52A9B7882EC8E7EE004DD4A2 /* iKeyMon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iKeyMon.entitlements; sourceTree = "<group>"; };
52A9B8212EC8FA8A004DD4A2 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
@@ -76,6 +78,7 @@
52A9B9712ECF751C004DD4A2 /* signing.env.example */,
52A9BEC92ED3874F004DD4A2 /* README.md */,
52A9BD122ED37E08004DD4A2 /* Frameworks */,
5221016C2EE5E82700D04952 /* Sparkle */,
);
sourceTree = "<group>";
};
@@ -87,6 +90,14 @@
name = Products;
sourceTree = "<group>";
};
5221016C2EE5E82700D04952 /* Sparkle */ = {
isa = PBXGroup;
children = (
5221016B2EE5E82700D04952 /* appcast.xml */,
);
path = Sparkle;
sourceTree = "<group>";
};
52A9BD122ED37E08004DD4A2 /* Frameworks */ = {
isa = PBXGroup;
children = (
@@ -166,6 +177,7 @@
files = (
52A9B8222EC8FA8A004DD4A2 /* CHANGELOG.md in Resources */,
52A9BECA2ED3874F004DD4A2 /* README.md in Resources */,
5221016D2EE5E82700D04952 /* appcast.xml in Resources */,
52A9B79F2EC8E7EE004DD4A2 /* Assets.xcassets in Resources */,
52A9B9722ECF751C004DD4A2 /* signing.env.example in Resources */,
);
@@ -310,7 +322,7 @@
CODE_SIGN_ENTITLEMENTS = iKeyMon.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 39;
CURRENT_PROJECT_VERSION = 150;
DEVELOPMENT_ASSET_PATHS = "\"Preview Content\"";
DEVELOPMENT_TEAM = Q5486ZVAFT;
ENABLE_HARDENED_RUNTIME = YES;
@@ -325,7 +337,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 26.0.16;
MARKETING_VERSION = 26.0.69;
PRODUCT_BUNDLE_IDENTIFIER = net.24unix.iKeyMon;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -341,7 +353,7 @@
CODE_SIGN_ENTITLEMENTS = iKeyMon.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 39;
CURRENT_PROJECT_VERSION = 150;
DEVELOPMENT_ASSET_PATHS = "\"Preview Content\"";
DEVELOPMENT_TEAM = Q5486ZVAFT;
ENABLE_HARDENED_RUNTIME = YES;
@@ -356,7 +368,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 26.0.16;
MARKETING_VERSION = 26.0.69;
PRODUCT_BUNDLE_IDENTIFIER = net.24unix.iKeyMon;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;

View File

@@ -32,8 +32,13 @@ generate_appcast() {
local download_prefix=""
if [[ -n "${SPARKLE_DOWNLOAD_BASE_TEMPLATE:-}" ]]; then
download_prefix="${SPARKLE_DOWNLOAD_BASE_TEMPLATE//\{\{VERSION\}\}/$VERSION}"
else
download_prefix="${SPARKLE_DOWNLOAD_BASE_URL:-}"
elif [[ -n "${SPARKLE_DOWNLOAD_BASE_URL:-}" ]]; then
download_prefix="${SPARKLE_DOWNLOAD_BASE_URL%/}/v${VERSION}"
fi
# Ensure the version segment is present to match Gitea's /download/vX.Y.Z/ layout.
if [[ -n "$download_prefix" ]] && [[ "$download_prefix" != *"/$VERSION"* ]]; then
download_prefix="${download_prefix%/}/v${VERSION}"
fi
if [[ -z "$generator" || -z "${SPARKLE_EDDSA_KEY_FILE:-}" || -z "$download_prefix" ]]; then
@@ -41,11 +46,21 @@ generate_appcast() {
return
fi
local sparkle_cache="$HOME/Library/Caches/Sparkle_generate_appcast"
rm -rf "$sparkle_cache"
local output="$SPARKLE_APPCAST_OUTPUT"
mkdir -p "$(dirname "$output")"
local staging_dir
staging_dir="$(mktemp -d)"
cp "$ARTIFACTS_DIR"/*.zip "$staging_dir"/ 2>/dev/null || true
if ! ls "$staging_dir"/*.zip >/dev/null 2>&1; then
echo " Skipping Sparkle appcast generation (no ZIP archives found)."
rm -rf "$staging_dir"
return
fi
echo "🧾 Generating Sparkle appcast at $output"
if ! "$generator" \
--download-url-prefix "$download_prefix" \
@@ -70,6 +85,50 @@ sign_update_artifacts() {
fi
}
rewrite_appcast_urls() {
: # no-op (old helper removed)
}
submit_for_notarization() {
local target="$1"
local label="$2"
echo "📝 Submitting ${label} for notarization..."
xcrun notarytool submit "$target" \
--apple-id "$NOTARY_APPLE_ID" \
--team-id "$NOTARY_TEAM_ID" \
--password "$NOTARY_PASSWORD" \
--wait
}
notarize_app_bundle() {
local bundle="$1"
local label="$2"
if [[ -z "${NOTARY_APPLE_ID:-}" || -z "${NOTARY_TEAM_ID:-}" || -z "${NOTARY_PASSWORD:-}" ]]; then
echo " Skipping notarization for ${label} (NOTARY_* variables not set)."
return 1
fi
local tmp_dir
tmp_dir="$(mktemp -d)"
local archive="$tmp_dir/$(basename "$bundle").zip"
ditto -c -k --keepParent "$bundle" "$archive"
submit_for_notarization "$archive" "$label"
xcrun stapler staple "$bundle"
rm -rf "$tmp_dir"
}
notarize_artifact() {
local artifact="$1"
local label="$2"
if [[ -z "${NOTARY_APPLE_ID:-}" || -z "${NOTARY_TEAM_ID:-}" || -z "${NOTARY_PASSWORD:-}" ]]; then
echo " Skipping notarization for ${label} (NOTARY_* variables not set)."
return 1
fi
submit_for_notarization "$artifact" "$label"
xcrun stapler staple "$artifact"
}
if [[ -f "$CREDENTIALS_FILE" ]]; then
set -a
# shellcheck disable=SC1090
@@ -100,6 +159,14 @@ if [[ ! -d "$APP_PATH" ]]; then
fi
if [[ -n "${CODESIGN_IDENTITY:-}" ]]; then
echo "🔏 Codesigning Sparkle framework..."
codesign \
--force \
--options runtime \
--timestamp \
--sign "$CODESIGN_IDENTITY" \
"$APP_PATH/Contents/Frameworks/Sparkle.framework"
echo "🔏 Codesigning app with identity: $CODESIGN_IDENTITY"
codesign \
--deep \
@@ -113,6 +180,8 @@ else
echo "⚠️ Skipping codesign (CODESIGN_IDENTITY not set)."
fi
notarize_app_bundle "$APP_PATH" "iKeyMon.app"
STAGING_DIR=$(mktemp -d)
mkdir -p "$STAGING_DIR"
cp -R "$APP_PATH" "$STAGING_DIR/"
@@ -128,9 +197,7 @@ print(data.get("marketing_version", "dev"))
PY
)"
ZIP_NAME="iKeyMon-${VERSION}.zip"
pushd "$(dirname "$APP_PATH")" >/dev/null
zip -r "$ARTIFACTS_DIR/$ZIP_NAME" "$(basename "$APP_PATH")"
popd >/dev/null
ditto -c -k --keepParent "$APP_PATH" "$ARTIFACTS_DIR/$ZIP_NAME"
DMG_NAME="iKeyMon-${VERSION}.dmg"
hdiutil create -volname "iKeyMon" -srcfolder "$STAGING_DIR" -ov -format UDZO "$ARTIFACTS_DIR/$DMG_NAME"
@@ -138,15 +205,9 @@ hdiutil create -volname "iKeyMon" -srcfolder "$STAGING_DIR" -ov -format UDZO "$A
sign_update_artifacts
if [[ -n "${NOTARY_APPLE_ID:-}" && -n "${NOTARY_TEAM_ID:-}" && -n "${NOTARY_PASSWORD:-}" ]]; then
echo "📝 Submitting DMG for notarization..."
xcrun notarytool submit "$ARTIFACTS_DIR/$DMG_NAME" \
--apple-id "$NOTARY_APPLE_ID" \
--team-id "$NOTARY_TEAM_ID" \
--password "$NOTARY_PASSWORD" \
--wait
xcrun stapler staple "$ARTIFACTS_DIR/$DMG_NAME"
notarize_artifact "$ARTIFACTS_DIR/$DMG_NAME" "$DMG_NAME"
else
echo "⚠️ Skipping notarization (NOTARY_* variables not set)."
echo "⚠️ Skipping DMG notarization (NOTARY_* variables not set)."
fi
rm -rf "$STAGING_DIR"

View File

@@ -1,3 +1,3 @@
{
"marketing_version": "26.0.16"
"marketing_version": "26.0.69"
}