diff --git a/doc-onevcat/scripts/release.sh b/doc-onevcat/scripts/release.sh index 17511041..2317ad54 100755 --- a/doc-onevcat/scripts/release.sh +++ b/doc-onevcat/scripts/release.sh @@ -223,6 +223,19 @@ if [[ "$SENTRY_ENABLED" -eq 1 ]]; then log "WARNING: $DSYM_DIR not found, skipping dSYM upload" fi + # Sparkle ships as a prebuilt binaryTarget (Sparkle.xcframework), so Xcode never emits a + # dSYM for it into the archive — the archive-dSYM upload above therefore never covers + # Sparkle. Upload the dSYMs bundled inside the xcframework directly so each new Sparkle + # version is symbolicated automatically, instead of relying on a one-off manual bulk upload. + SPARKLE_XCFRAMEWORK="$HOME/Library/Caches/supacode-spm-cache/SourcePackages/artifacts/sparkle/Sparkle/Sparkle.xcframework" + if [[ -d "$SPARKLE_XCFRAMEWORK" ]]; then + log "uploading Sparkle xcframework dSYMs to Sentry..." + sentry-cli debug-files upload --wait "$SPARKLE_XCFRAMEWORK" \ + || log "WARNING: Sparkle dSYM upload failed (release will continue)" + else + log "WARNING: $SPARKLE_XCFRAMEWORK not found, skipping Sparkle dSYM upload" + fi + log "associating commits with Sentry release..." sentry-cli releases set-commits "$SENTRY_RELEASE_NAME" --auto \ || log "WARNING: failed to associate commits (continuing)" diff --git a/supacode.xcodeproj/project.pbxproj b/supacode.xcodeproj/project.pbxproj index 815775ff..e4f1e5d7 100644 --- a/supacode.xcodeproj/project.pbxproj +++ b/supacode.xcodeproj/project.pbxproj @@ -670,7 +670,7 @@ repositoryURL = "https://github.com/sparkle-project/Sparkle"; requirement = { kind = exactVersion; - version = "2.9.0-beta.2"; + version = "2.9.2"; }; }; 911B4AAD84AF4B34BE232133 /* XCRemoteSwiftPackageReference "swift-case-paths" */ = { diff --git a/supacode.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/supacode.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index fe38be89..89359eb8 100644 --- a/supacode.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/supacode.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/sparkle-project/Sparkle", "state" : { - "revision" : "afea2cda87819c960114f26e26f369a1a0945b17", - "version" : "2.9.0-beta.2" + "revision" : "6276ba2b404829d139c45ff98427cf90e2efc59b", + "version" : "2.9.2" } }, { diff --git a/supacode/Clients/Updates/UpdaterClient.swift b/supacode/Clients/Updates/UpdaterClient.swift index e0e9e532..ef8cf140 100644 --- a/supacode/Clients/Updates/UpdaterClient.swift +++ b/supacode/Clients/Updates/UpdaterClient.swift @@ -43,135 +43,105 @@ final class SilentUpdateDriver: NSObject, SPUUserDriver { self.continuation = continuation } - nonisolated func show( + // `SPUUserDriver` is declared `NS_SWIFT_UI_ACTOR` (main-actor isolated) as of Sparkle 2.9, + // so these callbacks are guaranteed to arrive on the main thread. Implement them as plain + // `@MainActor` methods and let the compiler enforce isolation, rather than reaching for + // `MainActor.assumeIsolated`, which would crash on any future off-main delivery. + func show( _ request: SPUUpdatePermissionRequest, reply: @escaping @Sendable (SUUpdatePermissionResponse) -> Void ) { - MainActor.assumeIsolated { - standard.show(request, reply: reply) - } + standard.show(request, reply: reply) } - nonisolated func showUserInitiatedUpdateCheck(cancellation: @escaping @Sendable () -> Void) { - MainActor.assumeIsolated { - standard.showUserInitiatedUpdateCheck(cancellation: cancellation) - } + func showUserInitiatedUpdateCheck(cancellation: @escaping @Sendable () -> Void) { + standard.showUserInitiatedUpdateCheck(cancellation: cancellation) } - nonisolated func showUpdateFound( + func showUpdateFound( with appcastItem: SUAppcastItem, state: SPUUserUpdateState, reply: @escaping @Sendable (SPUUserUpdateChoice) -> Void ) { - MainActor.assumeIsolated { - if state.userInitiated { - standard.showUpdateFound(with: appcastItem, state: state, reply: reply) - return - } - // Background check: surface the availability silently, then defer so Sparkle - // will re-offer the same update on the next (user-initiated) check. - continuation?.yield(.silentUpdateFound(version: appcastItem.displayVersionString)) - reply(.dismiss) + if state.userInitiated { + standard.showUpdateFound(with: appcastItem, state: state, reply: reply) + return } + // Background check: surface the availability silently, then defer so Sparkle + // will re-offer the same update on the next (user-initiated) check. + continuation?.yield(.silentUpdateFound(version: appcastItem.displayVersionString)) + reply(.dismiss) } - nonisolated func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { - MainActor.assumeIsolated { - standard.showUpdateReleaseNotes(with: downloadData) - } + func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { + standard.showUpdateReleaseNotes(with: downloadData) } - nonisolated func showUpdateReleaseNotesFailedToDownloadWithError(_ error: any Error) { - MainActor.assumeIsolated { - standard.showUpdateReleaseNotesFailedToDownloadWithError(error) - } + func showUpdateReleaseNotesFailedToDownloadWithError(_ error: any Error) { + standard.showUpdateReleaseNotesFailedToDownloadWithError(error) } - nonisolated func showUpdateNotFoundWithError( + func showUpdateNotFoundWithError( _ error: any Error, acknowledgement: @escaping @Sendable () -> Void ) { - MainActor.assumeIsolated { - standard.showUpdateNotFoundWithError(error, acknowledgement: acknowledgement) - } + standard.showUpdateNotFoundWithError(error, acknowledgement: acknowledgement) } - nonisolated func showUpdaterError( + func showUpdaterError( _ error: any Error, acknowledgement: @escaping @Sendable () -> Void ) { - MainActor.assumeIsolated { - standard.showUpdaterError(error, acknowledgement: acknowledgement) - } + standard.showUpdaterError(error, acknowledgement: acknowledgement) } - nonisolated func showDownloadInitiated(cancellation: @escaping @Sendable () -> Void) { - MainActor.assumeIsolated { - standard.showDownloadInitiated(cancellation: cancellation) - } + func showDownloadInitiated(cancellation: @escaping @Sendable () -> Void) { + standard.showDownloadInitiated(cancellation: cancellation) } - nonisolated func showDownloadDidReceiveExpectedContentLength(_ expectedContentLength: UInt64) { - MainActor.assumeIsolated { - standard.showDownloadDidReceiveExpectedContentLength(expectedContentLength) - } + func showDownloadDidReceiveExpectedContentLength(_ expectedContentLength: UInt64) { + standard.showDownloadDidReceiveExpectedContentLength(expectedContentLength) } - nonisolated func showDownloadDidReceiveData(ofLength length: UInt64) { - MainActor.assumeIsolated { - standard.showDownloadDidReceiveData(ofLength: length) - } + func showDownloadDidReceiveData(ofLength length: UInt64) { + standard.showDownloadDidReceiveData(ofLength: length) } - nonisolated func showDownloadDidStartExtractingUpdate() { - MainActor.assumeIsolated { - standard.showDownloadDidStartExtractingUpdate() - } + func showDownloadDidStartExtractingUpdate() { + standard.showDownloadDidStartExtractingUpdate() } - nonisolated func showExtractionReceivedProgress(_ progress: Double) { - MainActor.assumeIsolated { - standard.showExtractionReceivedProgress(progress) - } + func showExtractionReceivedProgress(_ progress: Double) { + standard.showExtractionReceivedProgress(progress) } - nonisolated func showReady(toInstallAndRelaunch reply: @escaping @Sendable (SPUUserUpdateChoice) -> Void) { - MainActor.assumeIsolated { - standard.showReady(toInstallAndRelaunch: reply) - } + func showReady(toInstallAndRelaunch reply: @escaping @Sendable (SPUUserUpdateChoice) -> Void) { + standard.showReady(toInstallAndRelaunch: reply) } - nonisolated func showInstallingUpdate( + func showInstallingUpdate( withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping @Sendable () -> Void ) { - MainActor.assumeIsolated { - standard.showInstallingUpdate( - withApplicationTerminated: applicationTerminated, - retryTerminatingApplication: retryTerminatingApplication - ) - } + standard.showInstallingUpdate( + withApplicationTerminated: applicationTerminated, + retryTerminatingApplication: retryTerminatingApplication + ) } - nonisolated func showUpdateInstalledAndRelaunched( + func showUpdateInstalledAndRelaunched( _ relaunched: Bool, acknowledgement: @escaping @Sendable () -> Void ) { - MainActor.assumeIsolated { - standard.showUpdateInstalledAndRelaunched(relaunched, acknowledgement: acknowledgement) - } + standard.showUpdateInstalledAndRelaunched(relaunched, acknowledgement: acknowledgement) } - nonisolated func showUpdateInFocus() { - MainActor.assumeIsolated { - standard.showUpdateInFocus() - } + func showUpdateInFocus() { + standard.showUpdateInFocus() } - nonisolated func dismissUpdateInstallation() { - MainActor.assumeIsolated { - standard.dismissUpdateInstallation() - } + func dismissUpdateInstallation() { + standard.dismissUpdateInstallation() } }