Updater heals itself: PM2 cron revived, Update now, failure reasons

PM2's cron for autofirmer-update dies inside a live daemon (six Windows
machines in October), and nothing on the machine noticed. The app now
watches scripts/.last-check and, past 10 minutes, runs
update-check.mjs --heal: a checker PM2 still lists is restarted (which
re-registers its cron), one it lost is registered again, and one whose
errored entry still holds a live pid is left alone, since PM2 would kill
that pid. The heal scrubs PM2's per-app fields from its environment first.

The master's new 'update-now' command runs --heal --now, skipping a
failed update's retry wait. A failed pull, npm install or build records
one line of reason (or the chown to run when a file isn't ours), which
rides on .last-check to the master. A package-lock.json rewritten by npm
is put back before the pull. The fetch timeout drops under the cron's
minute. scripts/.no-auto-update opts a checkout out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-19 00:41:04 -05:00
co-authored by Claude Opus 5.5
parent c0899620d4
commit 13e03c3d0f
6 changed files with 396 additions and 23 deletions
+5
View File
@@ -24,6 +24,7 @@ import { queueRun } from './queue-run';
import { pushBackup } from './backup-sync';
import { getBuildVersion } from './version';
import { updaterStatus } from './updater-status';
import { runUpdateNow } from './updater-watchdog';
import type {
Command,
InstanceCapabilities,
@@ -87,6 +88,7 @@ export function collectCapabilities(): InstanceCapabilities {
}))
),
runnerOnline,
updateNow: true,
};
}
@@ -131,6 +133,9 @@ async function execute(command: Command): Promise<unknown> {
case 'backup-now':
return pushBackup(true);
case 'update-now':
return runUpdateNow();
default: {
// A newer master may know commands this build does not. Say so
// rather than failing silently.