41 Commits

Author SHA1 Message Date
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
11 changed files with 167 additions and 43 deletions

View File

@@ -5,5 +5,6 @@
add a marker for "reboot required" add a marker for "reboot required"
dummy22 dummy223

View File

@@ -60,6 +60,7 @@ GITEA_REPO="iKeyMon"
# optional Sparkle feed helpers: # optional Sparkle feed helpers:
# SPARKLE_EDDSA_KEY_FILE="$HOME/.config/Sparkle/iKeyMon.key" # SPARKLE_EDDSA_KEY_FILE="$HOME/.config/Sparkle/iKeyMon.key"
# SPARKLE_DOWNLOAD_BASE_TEMPLATE="https://git.24unix.net/tracer/iKeyMon/releases/download/v{{VERSION}}" # 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 # SPARKLE_APPCAST_OUTPUT="$ROOT_DIR/Sparkle/appcast.xml" # default
``` ```

View File

@@ -9,6 +9,8 @@ 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 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"
@@ -34,6 +36,7 @@ final class SparkleUpdater: NSObject, ObservableObject {
private func log(_ message: String) { private func log(_ message: String) {
logger.log("\(message, privacy: .public)") logger.log("\(message, privacy: .public)")
addLogMessage("[INFO] \(message)")
if verboseLogging { if verboseLogging {
print("[Sparkle] \(message)") print("[Sparkle] \(message)")
} }
@@ -41,11 +44,21 @@ final class SparkleUpdater: NSObject, ObservableObject {
private func logError(_ message: String) { private func logError(_ message: String) {
logger.error("\(message, privacy: .public)") logger.error("\(message, privacy: .public)")
addLogMessage("[ERROR] \(message)")
if verboseLogging { if verboseLogging {
fputs("[Sparkle][error] \(message)\n", stderr) 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 { private func describe(update item: SUAppcastItem) -> String {
let short = item.displayVersionString let short = item.displayVersionString
let build = item.versionString let build = item.versionString
@@ -53,41 +66,63 @@ final class SparkleUpdater: NSObject, ObservableObject {
} }
} }
@MainActor
extension SparkleUpdater: SPUUpdaterDelegate { extension SparkleUpdater: SPUUpdaterDelegate {
func updater(_ updater: SPUUpdater, didFinishLoading appcast: SUAppcast) { nonisolated func updater(_ updater: SPUUpdater, didFinishLoading appcast: SUAppcast) {
Task { @MainActor in
log("Loaded Sparkle appcast containing \(appcast.items.count) item(s).") log("Loaded Sparkle appcast containing \(appcast.items.count) item(s).")
} }
}
func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) { nonisolated func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) {
Task { @MainActor in
log("Found valid update \(describe(update: item))") log("Found valid update \(describe(update: item))")
} }
}
func updaterDidNotFindUpdate(_ updater: SPUUpdater) { nonisolated func updaterDidNotFindUpdate(_ updater: SPUUpdater) {
Task { @MainActor in
log("No updates available.") log("No updates available.")
} }
}
func updater(_ updater: SPUUpdater, willDownloadUpdate item: SUAppcastItem, with request: NSMutableURLRequest) { 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")") log("Downloading \(describe(update: item)) from \(request.url?.absoluteString ?? "unknown URL")")
} }
}
func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) { nonisolated func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) {
Task { @MainActor in
log("Finished downloading \(describe(update: item))") log("Finished downloading \(describe(update: item))")
} }
}
func updater(_ updater: SPUUpdater, failedToDownloadUpdate item: SUAppcastItem, error: Error) { nonisolated func updater(_ updater: SPUUpdater, failedToDownloadUpdate item: SUAppcastItem, error: Error) {
Task { @MainActor in
logError("Failed to download \(describe(update: item)): \(error.localizedDescription)") logError("Failed to download \(describe(update: item)): \(error.localizedDescription)")
} }
}
func userDidCancelDownload(_ updater: SPUUpdater) { nonisolated func userDidCancelDownload(_ updater: SPUUpdater) {
Task { @MainActor in
log("User cancelled Sparkle download.") log("User cancelled Sparkle download.")
} }
}
func updater(_ updater: SPUUpdater, willInstallUpdate item: SUAppcastItem) { nonisolated func updater(_ updater: SPUUpdater, willInstallUpdate item: SUAppcastItem) {
Task { @MainActor in
log("Will install update \(describe(update: item))") log("Will install update \(describe(update: item))")
} }
}
func updater(_ updater: SPUUpdater, didAbortWithError error: Error) { nonisolated func updater(_ updater: SPUUpdater, didAbortWithError error: Error) {
logError("Sparkle aborted: \(error.localizedDescription)") 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

@@ -27,6 +27,7 @@ struct InfoCell: View {
} }
} }
// if let subtext { // if let subtext {
// Text(subtext) // Text(subtext)
// .font(.caption) // .font(.caption)

View File

@@ -257,11 +257,64 @@ private struct UpdatesPreferencesView: View {
.foregroundColor(.secondary) .foregroundColor(.secondary)
.padding(.top, 4) .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 {

30
Sparkle/appcast.xml vendored
View File

@@ -3,28 +3,28 @@
<channel> <channel>
<title>iKeyMon</title> <title>iKeyMon</title>
<item> <item>
<title>26.0.28</title> <title>26.0.46</title>
<pubDate>Sun, 07 Dec 2025 17:02:18 +0100</pubDate> <pubDate>Tue, 30 Dec 2025 16:00:24 +0100</pubDate>
<sparkle:version>59</sparkle:version> <sparkle:version>100</sparkle:version>
<sparkle:shortVersionString>26.0.28</sparkle:shortVersionString> <sparkle:shortVersionString>26.0.46</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion> <sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/iKeyMon-26.0.28.zip" length="4811473" type="application/octet-stream" sparkle:edSignature="tF188T0m/j22MStCVbVkrf2JllyZ9wiEmc++kgF4GTKhoAuHURYvT/Euy+ivEodmQ/LzeFolN4lViqaVa3y+DQ=="/> <enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.46/iKeyMon-26.0.46.zip" length="4849933" type="application/octet-stream" sparkle:edSignature="RwnuNciWKNNxGwhYmGf5Cni8oZZ4jC7JIDNYem/4kThbjAsm32Hrqb6M3rvgEeL+eMmf4njeEdELKzSxMxZSAg=="/>
</item> </item>
<item> <item>
<title>26.0.27</title> <title>26.0.45</title>
<pubDate>Sun, 07 Dec 2025 16:47:33 +0100</pubDate> <pubDate>Tue, 30 Dec 2025 15:51:45 +0100</pubDate>
<sparkle:version>57</sparkle:version> <sparkle:version>98</sparkle:version>
<sparkle:shortVersionString>26.0.27</sparkle:shortVersionString> <sparkle:shortVersionString>26.0.45</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion> <sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/iKeyMon-26.0.27.zip" length="4811492" type="application/octet-stream" sparkle:edSignature="6aEv0ii20pAkIl8kYWNkHM7+8APyDQtsus0SkF3C7/7q2X73HAsrsskNXjiiq0YF6bPVNAEs5y8G8GpwmerrCw=="/> <enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.45/iKeyMon-26.0.45.zip" length="4849893" type="application/octet-stream" sparkle:edSignature="sHY651/cP1trYCeTN+z7czP4qVsbZkeVtzsh47RBYwfY6xYeXRnZ+CBOSE2kukNPH9Knj8x1MLe4M+YGxQ4AAw=="/>
</item> </item>
<item> <item>
<title>26.0.21</title> <title>26.0.44</title>
<pubDate>Wed, 26 Nov 2025 18:44:41 +0100</pubDate> <pubDate>Tue, 30 Dec 2025 15:47:07 +0100</pubDate>
<sparkle:version>49</sparkle:version> <sparkle:version>97</sparkle:version>
<sparkle:shortVersionString>26.0.21</sparkle:shortVersionString> <sparkle:shortVersionString>26.0.44</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion> <sparkle:minimumSystemVersion>15.2</sparkle:minimumSystemVersion>
<enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.21/iKeyMon-26.0.21.zip" length="4802995" type="application/octet-stream" sparkle:edSignature="bYXN15YyKlSmHKNXPizEW2WrVXQSgD5XOgbtzOYNL+maG8DB/jZ08A+cYtGgqUeSRd+X6Z5Ue+Tpdn4/ewsFBw=="/> <enclosure url="https://git.24unix.net/tracer/iKeyMon/releases/download/v26.0.44/iKeyMon-26.0.44.zip" length="4849925" type="application/octet-stream" sparkle:edSignature="t0Lp1ZifHkMCdfhaysIQfmWL3TnKXfl7GP+xVtysI/Yq62IyVEFYBcUUIARHjfEko+ewgcIupSF0EE0kgRTCCQ=="/>
</item> </item>
</channel> </channel>
</rss> </rss>

View File

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

View File

@@ -4,9 +4,23 @@
<dict> <dict>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.files.user-selected.read-only</key> <key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/> <true/>
<key>com.apple.security.network.client</key> <key>com.apple.security.network.client</key>
<true/> <true/>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.xpc.aConnectionServices</key>
<array>
<string>com.sparkle-project.InstallerConnection</string>
</array>
<key>com.apple.security.xpc.aStatusServices</key>
<array>
<string>com.sparkle-project.InstallerStatus</string>
</array>
</dict> </dict>
</plist> </plist>

View File

@@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; }; 52A9B79F2EC8E7EE004DD4A2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52A9B7872EC8E7EE004DD4A2 /* Assets.xcassets */; };
52A9B8222EC8FA8A004DD4A2 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 52A9B8212EC8FA8A004DD4A2 /* CHANGELOG.md */; }; 52A9B8222EC8FA8A004DD4A2 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 52A9B8212EC8FA8A004DD4A2 /* CHANGELOG.md */; };
52A9B9722ECF751C004DD4A2 /* signing.env.example in Resources */ = {isa = PBXBuildFile; fileRef = 52A9B9712ECF751C004DD4A2 /* signing.env.example */; }; 52A9B9722ECF751C004DD4A2 /* signing.env.example in Resources */ = {isa = PBXBuildFile; fileRef = 52A9B9712ECF751C004DD4A2 /* signing.env.example */; };
@@ -28,6 +29,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
5203C24D2D997D2800576D4A /* iKeyMon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iKeyMon.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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>"; }; 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>"; }; 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>"; }; 52A9B8212EC8FA8A004DD4A2 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
@@ -76,6 +78,7 @@
52A9B9712ECF751C004DD4A2 /* signing.env.example */, 52A9B9712ECF751C004DD4A2 /* signing.env.example */,
52A9BEC92ED3874F004DD4A2 /* README.md */, 52A9BEC92ED3874F004DD4A2 /* README.md */,
52A9BD122ED37E08004DD4A2 /* Frameworks */, 52A9BD122ED37E08004DD4A2 /* Frameworks */,
5221016C2EE5E82700D04952 /* Sparkle */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -87,6 +90,14 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
5221016C2EE5E82700D04952 /* Sparkle */ = {
isa = PBXGroup;
children = (
5221016B2EE5E82700D04952 /* appcast.xml */,
);
path = Sparkle;
sourceTree = "<group>";
};
52A9BD122ED37E08004DD4A2 /* Frameworks */ = { 52A9BD122ED37E08004DD4A2 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -166,6 +177,7 @@
files = ( files = (
52A9B8222EC8FA8A004DD4A2 /* CHANGELOG.md in Resources */, 52A9B8222EC8FA8A004DD4A2 /* CHANGELOG.md in Resources */,
52A9BECA2ED3874F004DD4A2 /* README.md in Resources */, 52A9BECA2ED3874F004DD4A2 /* README.md in Resources */,
5221016D2EE5E82700D04952 /* appcast.xml in Resources */,
52A9B79F2EC8E7EE004DD4A2 /* Assets.xcassets in Resources */, 52A9B79F2EC8E7EE004DD4A2 /* Assets.xcassets in Resources */,
52A9B9722ECF751C004DD4A2 /* signing.env.example in Resources */, 52A9B9722ECF751C004DD4A2 /* signing.env.example in Resources */,
); );
@@ -310,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 = 59; CURRENT_PROJECT_VERSION = 100;
DEVELOPMENT_ASSET_PATHS = "\"Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"Preview Content\"";
DEVELOPMENT_TEAM = Q5486ZVAFT; DEVELOPMENT_TEAM = Q5486ZVAFT;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@@ -325,7 +337,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 26.0.28; MARKETING_VERSION = 26.0.46;
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;
@@ -341,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 = 59; CURRENT_PROJECT_VERSION = 100;
DEVELOPMENT_ASSET_PATHS = "\"Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"Preview Content\"";
DEVELOPMENT_TEAM = Q5486ZVAFT; DEVELOPMENT_TEAM = Q5486ZVAFT;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@@ -356,7 +368,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 26.0.28; MARKETING_VERSION = 26.0.46;
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;

View File

@@ -32,8 +32,13 @@ generate_appcast() {
local download_prefix="" local download_prefix=""
if [[ -n "${SPARKLE_DOWNLOAD_BASE_TEMPLATE:-}" ]]; then if [[ -n "${SPARKLE_DOWNLOAD_BASE_TEMPLATE:-}" ]]; then
download_prefix="${SPARKLE_DOWNLOAD_BASE_TEMPLATE//\{\{VERSION\}\}/$VERSION}" download_prefix="${SPARKLE_DOWNLOAD_BASE_TEMPLATE//\{\{VERSION\}\}/$VERSION}"
else elif [[ -n "${SPARKLE_DOWNLOAD_BASE_URL:-}" ]]; then
download_prefix="${SPARKLE_DOWNLOAD_BASE_URL:-}" 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 fi
if [[ -z "$generator" || -z "${SPARKLE_EDDSA_KEY_FILE:-}" || -z "$download_prefix" ]]; then if [[ -z "$generator" || -z "${SPARKLE_EDDSA_KEY_FILE:-}" || -z "$download_prefix" ]]; then

View File

@@ -1,3 +1,3 @@
{ {
"marketing_version": "26.0.28" "marketing_version": "26.0.46"
} }