Console: every update line the app logs sets the OTA state, newest wins (the Mac's card said '0.3.3 is current' with 0.3.4 staged); test
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
88572621ed
commit
1375c0a6b5
3 changed files with 36 additions and 13 deletions
|
|
@ -20,7 +20,7 @@ shard run reported as exit 0, 7a7e873).
|
|||
| 4 Oct 2026 | the Mac card read 0.0 MH/s for a minute while its blocks were still accepted (after the stale mark shipped) | STALE_S 120 s left no margin: one missed 60 s upload (a 120 s gap at 19:45:59 UTC) plus a 30 s STATUS age plus the 10 s cache | STALE_S 180 s (one missed upload is not stale; PC 2's real case was a 1,860 s gap) | test: 150 s is fresh, 240 s stale |
|
||||
| 4 Oct 2026 | a machine stopped on purpose (Sam's Mac, 14:47 UTC) read "silent 4h" on the console, the same as a crash or a lost network | the app logs `quit: stopping the miners, then the node` and `stopped` and uploads once more before it exits (so it does report), but the console never read those lines | 4d208c9: `parseAppTail` (now in `relay/lib/parse.mjs`) sets `stopped {at, reason quit\|update}` when a quit or OTA-install line has no status line after it; the card says "stopped (quit) N ago" with a grey stripe, `tools/console.mjs` says STOPPED | unit test: quit, update, running, and a quit followed by a later status line; live: five running machines show no false stop; a live stopped case is pending the next real quit |
|
||||
| 4 Oct 2026 | `publish-manifest.sh --deploy` called the live manifest "verified" after one signature check: any validly signed manifest, including the previous version still at the edge, passed; a failed deploy was hidden by `\|\| true` | one-shot check, signature only, no byte compare | `verify_live_manifest`: up to `--tries` checks 5 s apart, byte-identical to the folder's file, then the signature, each failure named; `--verify-only` runs just the check; a failed deploy stops first (the ship tool's own verify step already compared bytes and version, so a cut through `tools/ship-app.mjs` was covered; a hand publish was not) | finished: `--verify-only --tries 2` against the live 0.3.3 (try 1); failed: a local server with an altered copy ("differs", both stamps named) and a closed port ("is not reachable") |
|
||||
| 4 Oct 2026 | PC 2 came back on 0.3.4 (the canary) and its card's OTA state read "none" | the OTA state parsed only `OTA: …` lines, and the install lines sit in the previous run's log file; the new run only says `update check: 0.3.4 is current (manifest 0.3.4)` | that line is the OTA state (`current`, coloured as good) unless a newer `OTA:` line follows | test: current alone, a newer OTA line wins, an older one does not; live card after the deploy |
|
||||
| 4 Oct 2026 | PC 2 came back on 0.3.4 (the canary) and its card's OTA state read "none" | the OTA state parsed only `OTA: …` lines, and the install lines sit in the previous run's log file; the new run only says `update check: 0.3.4 is current (manifest 0.3.4)` | 797a844 then 2nd commit: every update line the app logs (`is available: downloading`, `downloaded and verified`, `is ready; it installs at the next safe moment`, `installing`, `update to X complete`, `is marked failed`, `has no build yet`, `is current`) is parsed and the newest decides the state; the Mac's card had read "0.3.3 is current" from a 70-minute-old check while 0.3.4 was downloaded, verified and staged | test: staged beats the older check, a finished run reads current, a newer OTA line wins, failed and updated; live cards after the deploy |
|
||||
|
||||
## Open
|
||||
|
||||
|
|
|
|||
|
|
@ -81,10 +81,26 @@ export function parseAppTail(tail) {
|
|||
// OTA: the newest line decides the state shown on the card
|
||||
const ota = lastMatch(lines, /^\d+(?:\.\d+)? (?:\[\w+\] )?(OTA: .+)$/);
|
||||
if (ota) { const t = ota[1]; a.ota = { text: t.slice(0, 220), at: stampOf(ota[0]), state: /waiting for administrator approval|approval not given/.test(t) ? 'waiting for approval' : /deferred/.test(t) ? 'deferred' : /updated to|installed|applied/.test(t) ? 'updated' : /download/.test(t) ? 'downloading' : 'note' }; }
|
||||
// After an update the new run's log has no "OTA:" line, only the check that found itself current (4 October 2026,
|
||||
// 20:33: PC 2 came back on 0.3.4 and its card said nothing about it); that check is the OTA state until a newer OTA line
|
||||
const current = lastMatch(lines, /^(\d+)(?:\.\d+)? update check: (\S+) is current \(manifest (\S+)\)$/);
|
||||
if (current && (!ota || Number(current[1]) >= Number((/^(\d+)/.exec(ota[0]) || [])[1]))) a.ota = { text: `update check: ${current[2]} is current (manifest ${current[3]})`, at: stampOf(current[0]), state: 'current', version: current[2] };
|
||||
// The app's other update lines (app/igneum-app/src/ota.rs): the newest of them, the OTA line above and the plain
|
||||
// "update check" decides the state shown on the card. 4 October 2026, 20:25: the Mac had downloaded, verified and
|
||||
// staged 0.3.4 ("update: ... downloaded and verified", "[ok] ... is ready; it installs at the next safe moment") and
|
||||
// its card said "0.3.3 is current" from a check 70 minutes older; a finished update's new run says only "is current".
|
||||
const SHAPES = [
|
||||
[/^(\d+)(?:\.\d+)? \[info\] Igneum Miner (\S+) is available: downloading/, 'downloading'],
|
||||
[/^(\d+)(?:\.\d+)? update: .*Igneum-Miner-(\d+\.\d+\.\d+)\S* downloaded and verified/, 'downloaded'],
|
||||
[/^(\d+)(?:\.\d+)? \[ok\] Igneum Miner (\S+) is ready; it installs at the next safe moment/, 'staged'],
|
||||
[/^(\d+)(?:\.\d+)? \[info\] installing Igneum Miner (\S+)/, 'installing'],
|
||||
[/^(\d+)(?:\.\d+)? update to (\S+) complete/, 'updated'],
|
||||
[/^(\d+)(?:\.\d+)? update: (\S+) is marked failed/, 'failed'],
|
||||
[/^(\d+)(?:\.\d+)? update check: (\S+) is published but has no \S+ build yet/, 'no build'],
|
||||
[/^(\d+)(?:\.\d+)? update check: (\S+) is current \(manifest \S+\)/, 'current'],
|
||||
];
|
||||
let best = a.ota ? { at: Number((/^(\d+)/.exec(ota[0]) || [])[1]) } : null;
|
||||
for (const [re, state] of SHAPES) {
|
||||
const m = lastMatch(lines, re);
|
||||
if (m && (!best || Number(m[1]) >= best.at)) best = { at: Number(m[1]), ota: { text: strip(m[0]).replace(/^\[\w+\] /, '').slice(0, 220), at: stampOf(m[0]), state, version: m[2] } };
|
||||
}
|
||||
if (best && best.ota) a.ota = best.ota;
|
||||
const pc = lastMatch(lines, /^\d+(?:\.\d+)? (?:\[\w+\] )?(?:GPU )?(power cap.*)$/i);
|
||||
if (pc) a.power_cap = { text: pc[1].slice(0, 220), at: stampOf(pc[0]), ok: !/NOT applied|nothing to read back/.test(pc[1]) };
|
||||
const jf = lastMatch(lines, /^\d+(?:\.\d+)? jobs: (.+)$/);
|
||||
|
|
|
|||
|
|
@ -64,14 +64,21 @@ test('app tail: a clean quit after the last status line is "stopped (quit)", an
|
|||
assert.equal(parseAppTail(resumed).stopped, undefined);
|
||||
});
|
||||
|
||||
test('app tail: after an update the "update check: X is current" line is the OTA state; an OTA line that is newer wins', async () => {
|
||||
test('app tail: the newest update line decides the OTA state: staged beats an older check, a finished run says current, a newer OTA line wins', async () => {
|
||||
const { parseAppTail } = await import('../lib/parse.mjs');
|
||||
const after = ['1791146000 IGNEUM-APP version=0.3.4 machine=1ccfe586 platform=windows node=igneumd/2.1.0', '1791146426 update check: 0.3.4 is current (manifest 0.3.4)',
|
||||
'1791146430 status: accepted 45 blocks (45 this run), 122.00 MH/s, mining | node 44306 blocks, 2 peers, synced | up 00:08:00'].join('\n');
|
||||
const st = n => `${n} status: accepted 1 blocks (1 this run), 1.00 MH/s, mining | node 1 blocks, 1 peers, synced | up 00:01:00`;
|
||||
const mac = ['1791141355 update check: 0.3.3 is current (manifest 0.3.3)', st(1791145000),
|
||||
'1791145529 [info] Igneum Miner 0.3.4 is available: downloading (39 MB). Finality rule v3',
|
||||
'1791145533 update: /Users/x/Library/Application Support/Igneum/app/updates/Igneum-Miner-0.3.4.dmg downloaded and verified',
|
||||
'1791145562 update: staged bundle digest 2b640b8c', '1791145562 [ok] Igneum Miner 0.3.4 is ready; it installs at the next safe moment', st(1791146000)].join('\n');
|
||||
const m = parseAppTail(mac);
|
||||
assert.deepEqual([m.ota.state, m.ota.version, m.ota.at], ['staged', '0.3.4', new Date(1791145562000).toISOString()]);
|
||||
const after = ['1791146000 IGNEUM-APP version=0.3.4 machine=1ccfe586 platform=windows node=igneumd/2.1.0', '1791146426 update check: 0.3.4 is current (manifest 0.3.4)', st(1791146430)].join('\n');
|
||||
const a = parseAppTail(after);
|
||||
assert.deepEqual([a.ota.state, a.ota.version, a.ota.at], ['current', '0.3.4', new Date(1791146426000).toISOString()]);
|
||||
const newer = after + '\n1791146500 OTA: 0.3.5 downloaded, waiting for this machine\'s minute';
|
||||
assert.notEqual(parseAppTail(newer).ota.state, 'current');
|
||||
const older = '1791146400 OTA: 0.3.4 installed\n' + after;
|
||||
assert.equal(parseAppTail(older).ota.state, 'current');
|
||||
assert.deepEqual([a.ota.state, a.ota.version], ['current', '0.3.4']);
|
||||
assert.notEqual(parseAppTail(after + "\n1791146500 OTA: 0.3.5 downloaded, waiting for this machine's minute").ota.state, 'current');
|
||||
assert.equal(parseAppTail('1791146400 OTA: 0.3.4 installed\n' + after).ota.state, 'current');
|
||||
assert.equal(parseAppTail(mac + '\n1791146600 update: 0.3.4 is marked failed; it will not be applied again by itself').ota.state, 'failed');
|
||||
assert.equal(parseAppTail(mac + '\n1791146600 update to 0.3.4 complete (from 0.3.3); keeping the previous version for a rollback').ota.state, 'updated');
|
||||
assert.equal(parseAppTail(st(1)).ota, undefined);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue