* feat: integrate Sparkle 2 for auto-update Adds the standard pieces for Sparkle-based silent updates: - Package.swift: add Sparkle 2 SPM dep, wired into OpenLessApp. - Sources/OpenLessApp/Updater/UpdaterController.swift: thin wrapper around SPUStandardUpdaterController. Owned by AppDelegate so the updater starts on app launch. - AppDelegate: instantiate UpdaterController before installing the main menu; pass it to ApplicationMenu so the menu item can target it. - ApplicationMenu: add "检查更新…" as the first item in the OpenLess menu when an UpdaterController target is present. - scripts/build-app.sh: copy Sparkle.framework (with Updater.app + XPC services + Autoupdate) from the SPM artifacts dir into Contents/Frameworks/, inject SUFeedURL / SUPublicEDKey / SUEnableAutomaticChecks / SUEnableInstallerLauncherService / SUScheduledCheckInterval into Info.plist, and ad-hoc sign the embedded helpers explicitly (deep sign alone is unreliable for Sparkle's nested XPC bundles). - scripts/release.sh: one-shot release pipeline. ./scripts/release.sh <version> [<build>] [<notes>] Bumps version in build-app.sh, builds, zips, signs the zip with the Keychain-stored EdDSA private key (auto-downloads sign_update from the Sparkle 2.9.1 release on first run), prepends a new <item> to appcast.xml, commits + tags + pushes main, and creates the GitHub release with the zip uploaded. - appcast.xml: empty initial feed file at repo root, served via the raw.githubusercontent.com URL referenced by SUFeedURL. Items are appended automatically by release.sh. Public EdDSA key embedded in the build: iT00+eUw/55obn1suEnWqI7za2pc8mHIFIdRbOWXW1Q= Private key lives only in the local Keychain on the release machine (account: macOS login user; service: https://sparkle-project.org). First-time install still requires the user to remove the quarantine xattr (no Apple Developer ID); subsequent updates flow through Sparkle's installer helper and bypass quarantine. * fix(sparkle): add @executable_path/../Frameworks rpath to OpenLessApp Without this, the SPM-built executable links Sparkle's symbols but ships with no rpath that points at the embedded framework, so dyld fails at launch with: Library not loaded: @rpath/Sparkle.framework/Versions/B/Sparkle …and the user sees the macOS Gatekeeper-styled "请与开发者联系,以确保 OpenLess 可以配合此 macOS 版本使用" error. Inject the rpath via linkerSettings.unsafeFlags so the binary itself knows where to look. --------- Co-authored-by: baiqing <lbx12309@icloud.com>
27 lines
951 B
Swift
27 lines
951 B
Swift
import AppKit
|
||
import Sparkle
|
||
|
||
/// Sparkle 更新通道:启动时自动检查、菜单「检查更新…」手动触发、新版本弹窗由 Sparkle 内置 UI 提供。
|
||
/// 默认配置:
|
||
/// - SUFeedURL:appcast.xml 的 raw GitHub URL(写在 Info.plist)
|
||
/// - SUPublicEDKey:EdDSA 公钥(写在 Info.plist);私钥只在发版机的 Keychain 里
|
||
/// - 启动后台后约 30s 做首次检查;之后每小时一次(也可在 Info.plist 调整)
|
||
@MainActor
|
||
final class UpdaterController: NSObject {
|
||
let updater: SPUStandardUpdaterController
|
||
|
||
override init() {
|
||
self.updater = SPUStandardUpdaterController(
|
||
startingUpdater: true,
|
||
updaterDelegate: nil,
|
||
userDriverDelegate: nil
|
||
)
|
||
super.init()
|
||
}
|
||
|
||
/// 菜单项 target = self,action = #selector(checkForUpdates(_:))
|
||
@objc func checkForUpdates(_ sender: Any?) {
|
||
updater.checkForUpdates(sender)
|
||
}
|
||
}
|