Skip to content

v4.169.50 - Windows auto-update could not install a release

Choose a tag to compare

@IvanSkainet IvanSkainet released this 22 Aug 18:03
· 55 commits to master since this release
3655be7

Windows auto-update could not install a release (#158)

Installing v4.169.49 on a real Windows host failed twice, in two distinct ways. Both defects were in the shipped updater, and neither was caught by a test — both were found by reading .arena-update-apply.log on the live machine. Together they meant Windows auto-update could not install a release unattended.

1. A pre-existing rollback directory aborted the update

[22.08.2026 20:01:46] bridge exited, starting copy
[22.08.2026 20:03:16] rollback directory unavailable
[22.08.2026 20:03:16] ERROR copy failed, restoring rollback snapshot

Not one file was copied. The bridge came back on the old version with the new release sitting unused on disk.

mkdir "<backup>" 2>NUL
if not errorlevel 1 goto :rollback_dir_ready

mkdir sets errorlevel 1 when the directory already exists. The snapshot path is derived from the version being replaced, so it is identical on every retry of the same upgrade — and apply itself prepares it before the mover runs. Any retry was guaranteed to fail closed.

Readiness is now gated on the directory existing (if exist "<dir>\."), after clearing any stale snapshot. Same lesson as v4.169.21 and schtasks: the exit code of the call meant to produce a thing is not evidence the thing exists.

2. Two relaunch helpers raced, and the old build won

The retry copied correctly, yet /health still answered 4.169.48 — with 118 seconds of uptime. New files on disk, old code in memory.

.arena-update-apply.log   bridge exited, starting copy   20:11:58
.arena-restart.log        fired start_hidden.vbs         20:11:59
.arena-update-apply.log   copy done, launching relaunch  20:12:05

apply arms a mover that waits for the bridge PID, copies, then relaunches. A manual POST /v1/admin/update/restart armed a second detached waiter on the same PID; with nothing to copy it won by six seconds and started Python against a tree robocopy was still rewriting.

Detection deliberately does not look for the mover's files: .arena-update-apply.cmd is never deleted and the mover's own lock cleanup is best-effort, so on any host that has ever updated both can be present with no mover running. apply now publishes .arena-update-mover.pid naming the process the mover waits for, before spawning it — which also closes the window before the lock directory appears. A marker that does not name the running process is ignored, so every stale-artefact case degrades safely to arming our own helper.

3. The rollback purge was unverified

rmdir /S /Q suppresses its errors, so a locked file could leave the old snapshot in place while if exist still succeeded — producing a backup that mixes two attempts. The mover now confirms the directory is empty and aborts with a distinct log line if it is not.

Verification

Exercised against real cmd.exe on a Windows host, not only in pytest: a fresh directory, an existing one with stale content, an empty one, a path with parentheses and a space, an impossible volume, and a purge blocked by an open file handle (correctly reports dirty and stops).

Suite: 9361 passed, 36 skipped. Builds are byte-reproducible and attested with provenance + SPDX SBOM.