feat: in-app auto-updater — download, swap and relaunch
CheckForUpdate now also resolves the release's downloadable asset (portable .exe, or a .zip to unpack). DownloadAndApplyUpdate streams it with progress events (update:progress 0-100), swaps it in for the running exe (rename the current one to .old — allowed for a running image on Windows — move the new one into place, roll back on failure), then relaunches with --post-update and quits. main: a --post-update relaunch retries the single-instance mutex for ~20 s so the fresh process waits for the old one to exit and free it, then clears the .old exe left behind. Falls back to opening the release page when a release has no auto-download asset.
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"embed"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
@@ -35,15 +36,53 @@ func profileArg(args []string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// hasFlag reports whether flag is present in args.
|
||||
func hasFlag(args []string, flag string) bool {
|
||||
for _, a := range args {
|
||||
if a == flag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// acquireInstance grabs the single-instance mutex. On a normal launch it's a plain
|
||||
// try (fail → another OpsLog is running, so exit). On a --post-update relaunch the
|
||||
// previous instance may still be shutting down and holding the mutex, so retry for
|
||||
// a few seconds until it frees.
|
||||
func acquireInstance(postUpdate bool) bool {
|
||||
if acquireSingleInstance() {
|
||||
return true
|
||||
}
|
||||
if !postUpdate {
|
||||
return false
|
||||
}
|
||||
deadline := time.Now().Add(20 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
if acquireSingleInstance() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Single-instance guard: if OpsLog is already running, focus that window and
|
||||
// exit instead of spawning a duplicate. A second process would open its own
|
||||
// CAT (FlexRadio) connection and Ultrabeam follow loop, and the two would
|
||||
// fight over the rig/antenna frequency — the cause of "the antenna re-tunes on
|
||||
// its own" when a windowless zombie instance was left running.
|
||||
if !acquireSingleInstance() {
|
||||
// A --post-update relaunch (from the auto-updater) may start while the previous
|
||||
// instance is still exiting and holding the single-instance mutex — wait for it
|
||||
// to free instead of bailing out. Then clear the old exe it left behind.
|
||||
postUpdate := hasFlag(os.Args[1:], "--post-update")
|
||||
if !acquireInstance(postUpdate) {
|
||||
return
|
||||
}
|
||||
if postUpdate {
|
||||
cleanupOldUpdateBinary()
|
||||
}
|
||||
|
||||
// Create an instance of the app structure
|
||||
app := NewApp()
|
||||
|
||||
Reference in New Issue
Block a user