From 415826f0a623fe8b01bd7c489ff7bca472c6a88f Mon Sep 17 00:00:00 2001 From: archipelago Date: Mon, 5 Oct 2026 16:02:48 -0400 Subject: [PATCH] fix: package the qualified UI in release ISOs --- docs/release-1.9.0-acceptance.md | 42 +++++++++++++++++++ .../_archived/build-auto-installer-iso.sh | 18 +++++++- scripts/build-iso-release.sh | 1 + tests/regression/iso-qualified-web-ui.py | 38 +++++++++++++++++ tests/release/run.sh | 1 + 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 tests/regression/iso-qualified-web-ui.py diff --git a/docs/release-1.9.0-acceptance.md b/docs/release-1.9.0-acceptance.md index 042d6963..e6ec8a9a 100644 --- a/docs/release-1.9.0-acceptance.md +++ b/docs/release-1.9.0-acceptance.md @@ -717,3 +717,45 @@ track AIUI changes and pass the checkout revision. Actual image qualification and public demo deployment remain pending. The demo/release VPS currently has under1GiB free disk; capacity must be resolved before image/artifact publication. No running container, volume, release or repository was deleted. + +### Authorized release-storage cleanup — 2026-10-05 + +Operator approved removing only the old v1.8.13-alpha and v1.8.15-alpha ISO +attachments, then clarified that broader retention changes should be dropped if +that suffices. Both local ISO archives were independently hashed against their +published checksum files before removal. Gitea API deletion removed attachment +IDs219 and226 only; all other assets in both releases were verified unchanged. +Public server free space increased from887MiB to5.9GiB. Remaining historical +releases, OTA files, registry packages and application data were preserved. +Gitea's prior read-only storage doctor found no orphaned archives, attachments or +package blobs. No storage-doctor fix or package garbage collection was run. +Further removals are not planned; check exact final upload/image sizes first. + +### Corrected binary deployed and restart verified + +Final security backend SHA256 +`560aa6006cd9ef8be95b1f7831cf3b53854e911622b50022bb4402ce0f8b010a` +built from e0b2181a after the complete1,681-test isolated pass. Deployed dev, +yaya and Shorty: health200, both embedded helper files match reviewed source, +active HTTP/HTTPS defaults are guarded, and all existing container IDs/start +times are unchanged. Backup per node: `support/190-guard-560aa6006cd9`. +The first dev check incorrectly inspected sites-available rather than the actual +regular sites-enabled file; automatic backend rollback ran. Corrected check +uses the helper's active-dashboard resolution, and the second deployment passes. +This was a qualification-script path error; retain the first failed log. + +Shorty's final backend manager restart passed302 continuous public HTTP/HTTPS +RPC denial probes, followed by healthy management. Existing public32-case and +read-only Angor acceptance are rerunning against this exact deployed binary. +Logs: `/tmp/archy-190-guard-dev-deploy-2.log`, +`/tmp/archy-190-guard-yaya-deploy.log`, `/tmp/archy-190-guard-shorty-deploy.log`, +`/tmp/archy-190-final-shorty-restart-security.log`. + +ISO release packaging now explicitly selects the qualified dashboard/AIUI +payload rather than capturing a live node's cached runtime files or choosing +AIUI by timestamp. Three executable builder-branch fixtures pass qualified, +missing and partial payloads; missing inputs fail without a live-node fallback. +The release wrapper sets this path and the release harness includes the test. +Accepted companion54 APK/metadata replace the stale copies in the packaging +staging directory; exact SHA remains ceb58a7d…fab1a1. Final ISO and OTA package +acceptance/signatures remain pending. diff --git a/image-recipe/_archived/build-auto-installer-iso.sh b/image-recipe/_archived/build-auto-installer-iso.sh index 5d557392..81dde3a6 100755 --- a/image-recipe/_archived/build-auto-installer-iso.sh +++ b/image-recipe/_archived/build-auto-installer-iso.sh @@ -1448,7 +1448,19 @@ mkdir -p "$ARCH_DIR/web-ui" # Try to get from live server first (unless BUILD_FROM_SOURCE=1) WEBUI_CAPTURED=0 -if [ "$BUILD_FROM_SOURCE" != "1" ]; then +if [ -n "${ARCHIPELAGO_WEB_UI:-}" ]; then + # Release builds supply the qualified payload explicitly. Never substitute + # a running node's cached runtime files or an older installed dashboard. + if [ ! -f "$ARCHIPELAGO_WEB_UI/index.html" ] || \ + [ ! -f "$ARCHIPELAGO_WEB_UI/aiui/index.html" ] || \ + [ ! -f "$ARCHIPELAGO_WEB_UI/aiui/BUILD-INFO" ]; then + echo "ERROR: ARCHIPELAGO_WEB_UI must contain the qualified dashboard and AIUI" + exit 1 + fi + cp -r "$ARCHIPELAGO_WEB_UI/." "$ARCH_DIR/web-ui/" + WEBUI_CAPTURED=1 +fi +if [ "$WEBUI_CAPTURED" = "0" ] && [ "$BUILD_FROM_SOURCE" != "1" ]; then # Direct copy from local filesystem (when running on target with sudo) if [ -d "/opt/archipelago/web-ui" ] && [ "$(ls -A /opt/archipelago/web-ui 2>/dev/null)" ]; then cp -r /opt/archipelago/web-ui/* "$ARCH_DIR/web-ui/" @@ -1504,6 +1516,9 @@ AIUI_INCLUDED=0 # or yesterday's hashed assets linger next to today's forever. AIUI_SRC="" AIUI_NEWEST=0 +if [ -n "${ARCHIPELAGO_WEB_UI:-}" ]; then + AIUI_SRC="$ARCHIPELAGO_WEB_UI/aiui" +else for AIUI_DIR in \ "$SCRIPT_DIR/../../aiui/packages/app/dist" \ "$SCRIPT_DIR/../../AIUI/packages/app/dist" \ @@ -1520,6 +1535,7 @@ for AIUI_DIR in \ fi fi done +fi if [ -n "$AIUI_SRC" ]; then echo " Including AIUI from $AIUI_SRC (newest of the candidates)..." mkdir -p "$ARCH_DIR/web-ui/aiui" diff --git a/scripts/build-iso-release.sh b/scripts/build-iso-release.sh index 634d2067..11a91f89 100755 --- a/scripts/build-iso-release.sh +++ b/scripts/build-iso-release.sh @@ -147,6 +147,7 @@ build_iso() { BUILD_FROM_SOURCE=0 DEV_SERVER=localhost ARCHIPELAGO_BIN="$REPO/core/target/release/archipelago" + ARCHIPELAGO_WEB_UI="$REPO/web/dist/neode-ui" ) [ -n "$RC_OVERRIDE" ] && env_args+=(RC="$RC_OVERRIDE") sudo -E env "${env_args[@]}" nice -n 5 bash image-recipe/build-debian-iso.sh diff --git a/tests/regression/iso-qualified-web-ui.py b/tests/regression/iso-qualified-web-ui.py new file mode 100644 index 00000000..639b30a9 --- /dev/null +++ b/tests/regression/iso-qualified-web-ui.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Exercise the ISO builder's actual payload-selection branches, without a build.""" +import os +import pathlib +import subprocess +import tempfile + +ROOT = pathlib.Path(__file__).resolve().parents[2] +source = (ROOT / 'image-recipe/_archived/build-auto-installer-iso.sh').read_text() +capture = source.split('WEBUI_CAPTURED=0', 1)[1].split('# Include AIUI web app', 1)[0] +select = source.split('AIUI_SRC=""', 1)[1].split('if [ -n "$AIUI_SRC" ]; then', 1)[0] +program = 'set -eu\nWEBUI_CAPTURED=0\n' + capture + '\nAIUI_SRC=""\n' + select + '\nprintf "%s\\n" "$AIUI_SRC"\n' +with tempfile.TemporaryDirectory(prefix='archy-iso-qualified-ui-') as tmp: + base = pathlib.Path(tmp) + qualified = base / 'qualified' + (qualified / 'aiui').mkdir(parents=True) + (qualified / 'index.html').write_text('qualified-dashboard') + (qualified / 'aiui/index.html').write_text('qualified-aiui') + (qualified / 'aiui/BUILD-INFO').write_text('commit=fixture') + for label, path, success in [('qualified', qualified, True), ('missing', base / 'absent', False), ('partial', base / 'partial', False)]: + out = base / 'outputs' / label / 'installer' + (out / 'web-ui').mkdir(parents=True) + if label == 'partial': + path.mkdir(exist_ok=True) + (path / 'index.html').write_text('incomplete') + result = subprocess.run(['bash', '-c', program], env=os.environ | { + 'ARCHIPELAGO_WEB_UI': str(path), 'ARCH_DIR': str(out), + 'BUILD_FROM_SOURCE': '0', 'DEV_SERVER': 'localhost', 'SCRIPT_DIR': str(base), + }, capture_output=True, text=True) + assert (result.returncode == 0) == success, (label, result.stdout, result.stderr) + if success: + assert (out / 'web-ui/index.html').read_text() == 'qualified-dashboard' + assert (out / 'web-ui/aiui/index.html').read_text() == 'qualified-aiui' + assert result.stdout.strip().splitlines()[-1] == str(qualified / 'aiui') + assert not (out / 'web-ui/archipelago-runtime').exists() + else: + assert not list((out / 'web-ui').iterdir()), 'invalid payload silently fell back to a live node' + print('PASS ISO payload selection:', label) diff --git a/tests/release/run.sh b/tests/release/run.sh index 49211ab0..9d76fa03 100755 --- a/tests/release/run.sh +++ b/tests/release/run.sh @@ -72,6 +72,7 @@ summary() { stage "git-diff-check" git diff --check stage "mirror-gate-regression" python3 scripts/tests/test_git_mirrors.py stage "iso-boot-runner-regression" python3 scripts/tests/test_iso_qemu_runner.py +stage "iso-qualified-web-ui" python3 tests/regression/iso-qualified-web-ui.py stage "demo-rpc-parity" timeout 120 node neode-ui/scripts/mock-rpc-parity.mjs stage "demo-resumable-uploads" timeout 120 node --test neode-ui/scripts/demo-upload-test.mjs stage "companion-signature-regression" python3 scripts/tests/test_companion_apk_verification.py