From ade40fbe3abc3e84a90a6fd46c86855bbc0aa272 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:33:15 +0000 Subject: [PATCH] 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 --- packaging/ota/publish-jobs.sh | 2 +- packaging/ota/publish-manifest.sh | 10 ++++++++++ packaging/ota/update-check.ps1 | 14 ++++++++++++++ relay/test/clients.test.mjs | 11 +++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 packaging/ota/update-check.ps1 diff --git a/packaging/ota/publish-jobs.sh b/packaging/ota/publish-jobs.sh index 90882a7b1..6c105b3cc 100755 --- a/packaging/ota/publish-jobs.sh +++ b/packaging/ota/publish-jobs.sh @@ -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() { # -> 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 diff --git a/packaging/ota/publish-manifest.sh b/packaging/ota/publish-manifest.sh index 6b185ffd5..a314a6f50 100755 --- a/packaging/ota/publish-manifest.sh +++ b/packaging/ota/publish-manifest.sh @@ -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 diff --git a/packaging/ota/update-check.ps1 b/packaging/ota/update-check.ps1 new file mode 100644 index 000000000..d30ce345b --- /dev/null +++ b/packaging/ota/update-check.ps1 @@ -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') diff --git a/relay/test/clients.test.mjs b/relay/test/clients.test.mjs index d993a8ffe..9c50fba81 100644 --- a/relay/test/clients.test.mjs +++ b/relay/test/clients.test.mjs @@ -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'); +});