fix(update): the relaunched OpsLog was starting invisibly

The relaunch after an update called hideConsole on the command that
starts the new build. That sets SysProcAttr{HideWindow: true}, which on
Windows becomes SW_HIDE in the STARTUPINFO handed to CreateProcess — and
Windows applies it to the first top-level window the new process shows.
So the updated OpsLog started correctly, took the single-instance mutex,
opened the logbook and connected the rig, and never appeared.

That is the report, in full: a process in the task manager, no window,
no autostart programs, and ending it then launching OpsLog by hand
working every time. Two operators, both on 0.27.23.

Why 0.27.19 did not fix it: that commit fixed the other half of the same
symptom — the new instance being less patient than the old one is slow —
which was real and is still fixed. The window was never part of it.

Where it came from: removing the PowerShell helper. Start-Process
launched the exe with a normal show; the direct exec.Command that
replaced it borrowed hideConsole from the console tools next to it,
where hiding a console window is exactly right and where the three
remaining callers (tasklist, taskkill, the deferred-swap PowerShell)
still belong.

The proof it was this and not the waiting: OpsLog relaunches itself in
two places, and RestartApp — the database switch — was byte-identical
except that it never called hideConsole. It has never been reported
broken. Both now go through relaunchCmd, so the rule lives in one place
with the reason written down, rather than in two call sites that differed
by one line.

Two tests: relaunchCmd leaves SysProcAttr nil, and update.go does not
call hideConsole at all.
This commit is contained in:
2026-09-10 15:40:02 +02:00
parent 7e6e1335e3
commit a8e870e098
5 changed files with 110 additions and 15 deletions
+15 -11
View File
@@ -7,7 +7,6 @@ import (
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
@@ -228,16 +227,21 @@ func (a *App) DownloadAndApplyUpdate(url string) error {
// side now. The new instance is told OUR pid and waits for this process to
// end before taking the single-instance mutex.
//
// Waiting on the mutex alone was not enough, and that is the bug this line
// fixes: shutting down is allowed thirty seconds here (armExitWatchdog),
// because it closes a remote logbook, a CAT session and sometimes a backup,
// while the new instance was only patient for twenty. On a station where
// that ran long, the new process gave up and exited — leaving the old one
// still running and no new window, which is precisely what the PowerShell
// helper never did: it waited for the pid, however long it took.
cmd := exec.Command(exe, "--post-update", "--wait-pid", strconv.Itoa(os.Getpid()))
cmd.Dir = dir
hideConsole(cmd)
// Waiting on the mutex alone was not enough: shutting down is allowed thirty
// seconds here (armExitWatchdog), because it closes a remote logbook, a CAT
// session and sometimes a backup, while the new instance was only patient
// for twenty. On a station where that ran long, the new process gave up and
// exited — leaving the old one still running and no new window, which is
// precisely what the PowerShell helper never did: it waited for the pid,
// however long it took.
//
// And relaunchCmd rather than a command built here, because the OTHER half
// of the same report was this line calling hideConsole: SW_HIDE in the
// STARTUPINFO, which Windows applies to the new process's first window. The
// updated OpsLog started, took the mutex, and stayed invisible. See
// relaunch.go — the fact belongs in one place, since two self-relaunches
// differing by one line is how only one of them was broken.
cmd := relaunchCmd(exe, "--post-update", "--wait-pid", strconv.Itoa(os.Getpid()))
if err := cmd.Start(); err != nil {
return fmt.Errorf("schedule relaunch: %w", err)
}