Compare commits
5 Commits
2a848c3251
...
bff7c44c29
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bff7c44c29 | ||
|
|
f930e8334f | ||
|
|
0032ad9b57 | ||
|
|
b7f5d1a762 | ||
|
|
5dc5621871 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,6 +1,24 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Unreleased
|
## 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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 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.
|
- `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.
|
- Further reduced MainView console noise by removing redundant refresh/onAppear logs.
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ final class SparkleUpdater: NSObject, ObservableObject {
|
|||||||
private let logger = Logger(subsystem: "net.24unix.iKeyMon", category: "Sparkle")
|
private let logger = Logger(subsystem: "net.24unix.iKeyMon", category: "Sparkle")
|
||||||
private let verboseLogging: Bool
|
private let verboseLogging: Bool
|
||||||
@Published var logMessages: [String] = []
|
@Published var logMessages: [String] = []
|
||||||
@Published var showLogs: Bool = false
|
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
self.verboseLogging = ProcessInfo.processInfo.environment["SPARKLE_VERBOSE_LOGGING"] == "1"
|
self.verboseLogging = ProcessInfo.processInfo.environment["SPARKLE_VERBOSE_LOGGING"] == "1"
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ struct MainView: View {
|
|||||||
private static let serverOrderKeyStatic = "serverOrder"
|
private static let serverOrderKeyStatic = "serverOrder"
|
||||||
private static let storedServersKeyStatic = "storedServers"
|
private static let storedServersKeyStatic = "storedServers"
|
||||||
|
|
||||||
@EnvironmentObject private var sparkleUpdater: SparkleUpdater
|
|
||||||
@State var showAddServerSheet: Bool = false
|
@State var showAddServerSheet: Bool = false
|
||||||
@State private var serverBeingEdited: Server?
|
@State private var serverBeingEdited: Server?
|
||||||
@State private var serverToDelete: Server?
|
@State private var serverToDelete: Server?
|
||||||
@@ -60,14 +59,6 @@ struct MainView: View {
|
|||||||
}
|
}
|
||||||
.help("Add Host")
|
.help("Add Host")
|
||||||
}
|
}
|
||||||
ToolbarItem {
|
|
||||||
Button {
|
|
||||||
sparkleUpdater.checkForUpdates()
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "square.and.arrow.down")
|
|
||||||
}
|
|
||||||
.help("Check for Updates")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationTitle("Servers")
|
.navigationTitle("Servers")
|
||||||
.onChange(of: selectedServerID) {
|
.onChange(of: selectedServerID) {
|
||||||
|
|||||||
@@ -252,69 +252,11 @@ private struct UpdatesPreferencesView: View {
|
|||||||
Label("Check for Updates Now", systemImage: "sparkles")
|
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)
|
|
||||||
|
|
||||||
Divider()
|
|
||||||
.padding(.vertical, 8)
|
|
||||||
|
|
||||||
Button(action: { sparkleUpdater.showLogs.toggle() }) {
|
|
||||||
Label(sparkleUpdater.showLogs ? "Hide Logs" : "Show Logs", systemImage: "terminal.fill")
|
|
||||||
}
|
|
||||||
|
|
||||||
if sparkleUpdater.showLogs {
|
|
||||||
logsView
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var logsView: some View {
|
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
|
||||||
Text("Update Logs")
|
|
||||||
.font(.caption)
|
|
||||||
.fontWeight(.semibold)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
|
|
||||||
ScrollView {
|
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
|
||||||
if sparkleUpdater.logMessages.isEmpty {
|
|
||||||
Text("No logs yet. Check for updates to see activity.")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.italic()
|
|
||||||
} else {
|
|
||||||
ForEach(sparkleUpdater.logMessages, id: \.self) { message in
|
|
||||||
Text(message)
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.lineLimit(3)
|
|
||||||
.textSelection(.enabled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(8)
|
|
||||||
}
|
|
||||||
.frame(height: 200)
|
|
||||||
.background(Color(nsColor: .controlBackgroundColor))
|
|
||||||
.cornerRadius(6)
|
|
||||||
.border(.separator, width: 1)
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
sparkleUpdater.logMessages.removeAll()
|
|
||||||
}) {
|
|
||||||
Label("Clear Logs", systemImage: "trash")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
.disabled(sparkleUpdater.logMessages.isEmpty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct NotificationsPreferencesView: View {
|
private struct NotificationsPreferencesView: View {
|
||||||
|
|||||||
16
Sparkle/appcast.xml
vendored
16
Sparkle/appcast.xml
vendored
@@ -2,6 +2,14 @@
|
|||||||
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
|
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
|
||||||
<channel>
|
<channel>
|
||||||
<title>iKeyMon</title>
|
<title>iKeyMon</title>
|
||||||
|
<item>
|
||||||
|
<title>26.0.58</title>
|
||||||
|
<pubDate>Tue, 30 Dec 2025 19:36:28 +0100</pubDate>
|
||||||
|
<sparkle:version>128</sparkle:version>
|
||||||
|
<sparkle:shortVersionString>26.0.58</sparkle:shortVersionString>
|
||||||
|
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
|
||||||
|
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.58/iKeyMon-26.0.58.zip" length="2993175" type="application/octet-stream" sparkle:edSignature="LwsVJJa6ibPNFN04K5g41lZjrGPhBszCIefY34atBdsNqDhW2ZuapTlgxoWWi7j2wt7CMJf/fDBJUEhdI71zDQ=="/>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<title>26.0.57</title>
|
<title>26.0.57</title>
|
||||||
<pubDate>Tue, 30 Dec 2025 19:20:10 +0100</pubDate>
|
<pubDate>Tue, 30 Dec 2025 19:20:10 +0100</pubDate>
|
||||||
@@ -18,13 +26,5 @@
|
|||||||
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
|
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
|
||||||
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.56/iKeyMon-26.0.56.zip" length="3007443" type="application/octet-stream" sparkle:edSignature="m6cENPrqaTn3Y2pP+9UTkKFgNuJy7Rbs0pMxKSpNhBPEK2jwUm0cNNdIbpXWgD0kw6U2pTBLzYoWg7FFJQnqDA=="/>
|
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.56/iKeyMon-26.0.56.zip" length="3007443" type="application/octet-stream" sparkle:edSignature="m6cENPrqaTn3Y2pP+9UTkKFgNuJy7Rbs0pMxKSpNhBPEK2jwUm0cNNdIbpXWgD0kw6U2pTBLzYoWg7FFJQnqDA=="/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<title>26.0.55</title>
|
|
||||||
<pubDate>Tue, 30 Dec 2025 18:59:41 +0100</pubDate>
|
|
||||||
<sparkle:version>119</sparkle:version>
|
|
||||||
<sparkle:shortVersionString>26.0.55</sparkle:shortVersionString>
|
|
||||||
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
|
|
||||||
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.55/iKeyMon-26.0.55.zip" length="4842575" type="application/octet-stream" sparkle:edSignature="3xK6KKwXxArmlpuqIgWAQVhAKmv29PB1id/jAMIcwipeGZYcqW9oXvB48tUN6Wu5jyn2QSUjdNmduhfjdWh9CA=="/>
|
|
||||||
</item>
|
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
@@ -322,7 +322,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = iKeyMon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = iKeyMon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 123;
|
CURRENT_PROJECT_VERSION = 128;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = Q5486ZVAFT;
|
DEVELOPMENT_TEAM = Q5486ZVAFT;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -337,7 +337,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 26.0.57;
|
MARKETING_VERSION = 26.0.58;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.24unix.iKeyMon;
|
PRODUCT_BUNDLE_IDENTIFIER = net.24unix.iKeyMon;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -353,7 +353,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = iKeyMon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = iKeyMon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 123;
|
CURRENT_PROJECT_VERSION = 128;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = Q5486ZVAFT;
|
DEVELOPMENT_TEAM = Q5486ZVAFT;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -368,7 +368,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 26.0.57;
|
MARKETING_VERSION = 26.0.58;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.24unix.iKeyMon;
|
PRODUCT_BUNDLE_IDENTIFIER = net.24unix.iKeyMon;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"marketing_version": "26.0.57"
|
"marketing_version": "26.0.58"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user