2.0.2: a manifest publish nudges every relay machine to check at once (the shipper's order, 8 October 2026: the engine's manifest check is hourly today, ten minutes from 2.0.2, and the mini took the 2.0.1 entry only when its api/update/check was called by hand). publish-manifest.sh --deploy ends by posting packaging/ota/update-check.ps1 as a relay run to every machine under ~/.config/igneum/relay-machines (--no-nudge skips it; an unreachable machine is one line, never a failure); the script asks the local engine through its own api/update/check with the token from app.url, waits 45 s and reads back what it staged (RESULT UPDATE-CHECK asked version=... manifest=... state=...); it installs, quits, pauses or resumes nothing (the engine's own path installs at its next safe moment and the MINING-ON relay run reads the apply). Also: publish-jobs.sh's edition read is safe under set -e (the pre-push publish-jobs check's sandboxed add had failed on it). Test known-failed first in relay/test/clients.test.mjs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-08 19:33:15 +00:00
parent 0f54f1bdb5
commit ade40fbe3a
4 changed files with 36 additions and 1 deletions

View file

@ -140,7 +140,7 @@ case "$CMD" in add|list|remove|sign|verify) ;; *) sed -n '2,35p' "$0" | sed 's/^
# Pre-2.0.2 headers carry no edition and pass. Reads tools/logs.mjs --rotation's header parse when the Mac has DATABASE_URL.
target_edition() { # <machine id8> -> lab | public | '' (unknown or no DATABASE_URL)
[ -f "$HOME/.config/igneum/env" ] || { echo ""; return; }
node "$ROOT/tools/logs.mjs" --header "$1" 2>/dev/null | sed -n 's/.*edition=\([a-z]*\).*/\1/p' | head -1
{ node "$ROOT/tools/logs.mjs" --header "$1" 2>/dev/null || true; } | sed -n 's/.*edition=\([a-z]*\).*/\1/p' | head -1 || true
}
if [ "$CMD" = add ] && [ -n "${TARGET:-}" ] && [ "$TARGET" != all ]; then
for t in ${TARGET//,/ }; do

View file

@ -281,6 +281,16 @@ if [ "$DEPLOY" = 1 ]; then
if [ "$PUBLIC" = 1 ]; then "$HERE/publish-public.sh" --verify --no-prune || exit 1; fi
# the console's Builds tab (relay/): one build event and a fresh downloads listing; never fatal
node "$ROOT/tools/console.mjs" post --kind build --title "OTA manifest $PRODUCT $VERSION ($CHANNEL) live" --body "$NOTES" >/dev/null 2>&1 || true
# the nudge (2.0.2, 8 October 2026): every machine with a relay secret is asked once, through the relay, to run
# packaging/ota/update-check.ps1 (the engine's own api/update/check), so a published entry stages within a minute instead
# of at the engine's next hourly check; --no-nudge skips it; a machine the relay cannot reach is one line, never a failure
if [ "${NUDGE:-1}" = 1 ] && [ -d "$HOME/.config/igneum/relay-machines" ]; then
for sf in "$HOME/.config/igneum/relay-machines"/*; do
[ -f "$sf" ] || continue
m="$(basename "$sf")"
if node "$ROOT/tools/relay.mjs" run "$m" "update check after the $VERSION ($CHANNEL) publish: the engine's own api/update/check, then what it staged" "$ROOT/packaging/ota/update-check.ps1" >/dev/null 2>&1; then echo "nudged $m (api/update/check through the relay)"; else echo "could not nudge $m through the relay (it checks on its own clock)"; fi
done
fi
node "$ROOT/tools/console.mjs" sync-dl >/dev/null 2>&1 || true
else
if [ -n "$DLSITE" ]; then

View file

@ -0,0 +1,14 @@
# The nudge after a manifest publish (2.0.2, the shipper's order of 8 October 2026): the engine's own manifest check is hourly
# today (ten minutes from 2.0.2), so a published entry would wait up to an hour; this run, posted by publish-manifest.sh
# through the relay to every machine with a secret, asks the local engine once through its own API (api/update/check,
# the token in app.url) and reads what it staged. Reads only beyond that one POST; nothing installed here: the engine's
# own path installs at its next safe moment and the MINING-ON relay run reads the apply.
$ErrorActionPreference = 'Continue'
$urlFile = Join-Path $env:LOCALAPPDATA 'igneum\app\app.url'
if (-not (Test-Path -LiteralPath $urlFile)) { Write-Output 'RESULT UPDATE-CHECK no app.url (the app is not running)'; exit 1 }
$u = (Get-Content -LiteralPath $urlFile -Raw).Trim()
try { $before = Invoke-RestMethod -Uri ($u + 'api/state') -TimeoutSec 6 -UseBasicParsing } catch { Write-Output ("RESULT UPDATE-CHECK no engine answers: " + $_.Exception.Message); exit 1 }
try { Invoke-RestMethod -Method Post -Uri ($u + 'api/update/check') -TimeoutSec 20 -UseBasicParsing | Out-Null } catch { Write-Output ("RESULT UPDATE-CHECK api/update/check failed: " + $_.Exception.Message); exit 1 }
Start-Sleep -Seconds 45
try { $after = Invoke-RestMethod -Uri ($u + 'api/state') -TimeoutSec 6 -UseBasicParsing } catch { $after = $before }
Write-Output ('RESULT UPDATE-CHECK asked version=' + $after.version + ' manifest=' + $after.update.version + ' state=' + $after.update.state + ' note="' + $after.update.note + '" at ' + (Get-Date).ToUniversalTime().ToString('HH:mm:ss') + 'Z')

View file

@ -118,3 +118,14 @@ test('PowerShell shape: balanced braces and here-strings in the two .ps1 clients
assert.doesNotMatch(s, /\$[A-Za-z_]+:\s[a-z]/, `${f}: a "$name: text" drive-qualified reference (the 5.1 class of 4 October)`);
}
});
test('a manifest publish nudges every relay machine to check for the update at once (2.0.2; known-failed first)', () => {
const pm = read('packaging/ota/publish-manifest.sh');
assert.match(pm, /--no-nudge\) NUDGE=0/, 'the nudge can be skipped');
assert.match(pm, /relay\.mjs" run "\$m" "update check after the \$VERSION/, 'one relay run per machine with a secret');
assert.match(pm, /packaging\/ota\/update-check\.ps1/, 'the nudge script');
const uc = read('packaging/ota/update-check.ps1');
assert.match(uc, /api\/update\/check/, 'the engine is asked through its own API');
assert.match(uc, /RESULT UPDATE-CHECK asked version=/, 'the read-back names what it staged');
assert.doesNotMatch(uc, /api\/update\/install|api\/quit|api\/pause|api\/resume/, 'the nudge installs, quits, pauses or resumes nothing');
});