Skip auto-update for dev builds
`make install` now stamps "dev" as the local SHA256, which tells the auto-updater to skip update checks entirely. DMG-based installs continue to work normally with real SHA256 comparison. Clicking "Check for Updates" on a dev build shows a clear "Dev Build" message instead of prompting to downgrade. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
c14ab51724
commit
9746499231
4
Makefile
4
Makefile
|
|
@ -53,7 +53,9 @@ install: bundle
|
||||||
@rm -rf /Applications/$(APP_NAME).app
|
@rm -rf /Applications/$(APP_NAME).app
|
||||||
@cp -R $(APP_BUNDLE) /Applications/$(APP_NAME).app
|
@cp -R $(APP_BUNDLE) /Applications/$(APP_NAME).app
|
||||||
@xattr -cr /Applications/$(APP_NAME).app
|
@xattr -cr /Applications/$(APP_NAME).app
|
||||||
@echo "Installed /Applications/$(APP_NAME).app"
|
@mkdir -p "$(HOME)/Library/Application Support/Pommedoro"
|
||||||
|
@echo "dev" > "$(HOME)/Library/Application Support/Pommedoro/current.sha256"
|
||||||
|
@echo "Installed /Applications/$(APP_NAME).app (dev build – auto-update disabled)"
|
||||||
|
|
||||||
run: bundle
|
run: bundle
|
||||||
@open $(APP_BUNDLE)
|
@open $(APP_BUNDLE)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ enum AutoUpdater {
|
||||||
|
|
||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
|
|
||||||
|
/// Sentinel value written by `make install` to indicate a dev build.
|
||||||
|
/// The updater skips automatic updates when this is the local stamp.
|
||||||
|
private static let devSentinel = "dev"
|
||||||
|
|
||||||
/// Raw URL for the SHA256 file in the repo (Gitea raw endpoint)
|
/// Raw URL for the SHA256 file in the repo (Gitea raw endpoint)
|
||||||
private static let remoteSHA256URL = URL(
|
private static let remoteSHA256URL = URL(
|
||||||
string: "https://git.nixc.us/colin/pommedoro/raw/branch/main/releases/Pommedoro.dmg.sha256"
|
string: "https://git.nixc.us/colin/pommedoro/raw/branch/main/releases/Pommedoro.dmg.sha256"
|
||||||
|
|
@ -49,48 +53,56 @@ enum AutoUpdater {
|
||||||
// MARK: - Core Logic
|
// MARK: - Core Logic
|
||||||
|
|
||||||
private static func performCheck(silent: Bool) {
|
private static func performCheck(silent: Bool) {
|
||||||
// 1. Fetch remote SHA256
|
// 1. Read local SHA256 (written at last install/build)
|
||||||
|
let localSHA = readLocalSHA()
|
||||||
|
|
||||||
|
// 2. Dev builds are stamped "dev" – skip silent checks entirely
|
||||||
|
if localSHA == devSentinel {
|
||||||
|
if !silent {
|
||||||
|
showAlert(title: "Dev Build", message: "Auto-update is disabled for local dev builds.")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Fetch remote SHA256
|
||||||
guard let remoteSHA = fetchRemoteSHA() else {
|
guard let remoteSHA = fetchRemoteSHA() else {
|
||||||
if !silent { showAlert(title: "Update Check Failed", message: "Could not reach the update server.") }
|
if !silent { showAlert(title: "Update Check Failed", message: "Could not reach the update server.") }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Read local SHA256 (written at last install/build)
|
// 4. Compare
|
||||||
let localSHA = readLocalSHA()
|
|
||||||
|
|
||||||
// 3. Compare
|
|
||||||
if remoteSHA == localSHA {
|
if remoteSHA == localSHA {
|
||||||
if !silent { showAlert(title: "Up to Date", message: "You are running the latest version.") }
|
if !silent { showAlert(title: "Up to Date", message: "You are running the latest version.") }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. There's an update – prompt user
|
// 5. There's an update – prompt user
|
||||||
let shouldUpdate = promptForUpdate()
|
let shouldUpdate = promptForUpdate()
|
||||||
guard shouldUpdate else { return }
|
guard shouldUpdate else { return }
|
||||||
|
|
||||||
// 5. Download new DMG
|
// 6. Download new DMG
|
||||||
guard downloadDMG() else {
|
guard downloadDMG() else {
|
||||||
showAlert(title: "Update Failed", message: "Could not download the update.")
|
showAlert(title: "Update Failed", message: "Could not download the update.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Verify downloaded DMG matches remote SHA
|
// 7. Verify downloaded DMG matches remote SHA
|
||||||
guard let downloadedSHA = sha256(of: downloadedDMGPath), downloadedSHA == remoteSHA else {
|
guard let downloadedSHA = sha256(of: downloadedDMGPath), downloadedSHA == remoteSHA else {
|
||||||
showAlert(title: "Update Failed", message: "Downloaded file integrity check failed.")
|
showAlert(title: "Update Failed", message: "Downloaded file integrity check failed.")
|
||||||
try? FileManager.default.removeItem(at: downloadedDMGPath)
|
try? FileManager.default.removeItem(at: downloadedDMGPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Extract new app from DMG to staging area
|
// 8. Extract new app from DMG to staging area
|
||||||
guard stageFromDMG() else {
|
guard stageFromDMG() else {
|
||||||
showAlert(title: "Update Failed", message: "Could not extract the update.")
|
showAlert(title: "Update Failed", message: "Could not extract the update.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Save new SHA so we don't re-update next launch
|
// 9. Save new SHA so we don't re-update next launch
|
||||||
try? remoteSHA.write(to: localSHAPath, atomically: true, encoding: .utf8)
|
try? remoteSHA.write(to: localSHAPath, atomically: true, encoding: .utf8)
|
||||||
|
|
||||||
// 9. Spawn swap script, then terminate so applicationWillTerminate saves state
|
// 10. Spawn swap script, then terminate so applicationWillTerminate saves state
|
||||||
spawnSwapAndRelaunch()
|
spawnSwapAndRelaunch()
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
|
@ -142,7 +154,7 @@ enum AutoUpdater {
|
||||||
|
|
||||||
// MARK: - SHA256 Computation
|
// MARK: - SHA256 Computation
|
||||||
|
|
||||||
private static func sha256(of fileURL: URL) -> String? {
|
static func sha256(of fileURL: URL) -> String? {
|
||||||
guard let handle = try? FileHandle(forReadingFrom: fileURL) else { return nil }
|
guard let handle = try? FileHandle(forReadingFrom: fileURL) else { return nil }
|
||||||
defer { handle.closeFile() }
|
defer { handle.closeFile() }
|
||||||
|
|
||||||
|
|
@ -242,7 +254,6 @@ enum AutoUpdater {
|
||||||
proc.arguments = [scriptPath]
|
proc.arguments = [scriptPath]
|
||||||
proc.standardOutput = FileHandle.nullDevice
|
proc.standardOutput = FileHandle.nullDevice
|
||||||
proc.standardError = FileHandle.nullDevice
|
proc.standardError = FileHandle.nullDevice
|
||||||
// Detach from our process group so it survives our exit
|
|
||||||
proc.qualityOfService = .utility
|
proc.qualityOfService = .utility
|
||||||
try? proc.run()
|
try? proc.run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
7e15ab9b42481c28c9566c7d71bad03558bb2cdec7dcceb956443caf5cb1724a
|
98fa348545458a05d36e15656fdd788564e309784c4536a060499f0827141260
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue