6.1 KiB
Release OTA and ISO from latest main
This is the operator handoff for producing a release from whatever commit is
the reviewed tip of main at the time. A release is not ready merely because
the artifacts build. Every active regression, live-node acceptance, mirror and
publication gate below must pass.
1. Freeze an exact reviewed source revision
Use a fresh clone or clean worktree. Do not build from an UAT node or a dirty tree.
git switch main
git pull --ff-only ngit main
git status --short
git rev-parse HEAD
python3 scripts/check-git-mirrors.py --local --all
git status --short must be empty. The full mirror audit must show matching
advertised branches/tags on ngit and Gitea. Inventory and resolve any unrelated
drift; never force-push, delete refs, or rewrite published history merely to
make the check green. Record the accepted ngit proposal and merge commit in the
release acceptance ledger.
Read and retain every unchecked item in:
docs/post-1.8.22-regressions-20261001.md- the current release acceptance ledger and UAT checklist
Run the source gates and the documented live-node matrix. On a node with installed apps, backend tests must run only through the isolation wrapper:
scripts/test-backend-isolated.sh
tests/release/run.sh
python3 scripts/check-app-catalog-drift.py --release --strict
Keep source/unit results separate from actual-node acceptance. In particular, prove that paid-file recovery never makes a second payment and that app cleanup preserves wallets, persistent data, and uninstall decisions.
2. Prepare and sign the OTA release
Choose a new SemVer that has never been published. Do not move or replace an
existing release tag. Curate the new top entry in CHANGELOG.md, then preview:
release_version=X.Y.Z-alpha
scripts/create-release.sh "$release_version" --dry-run
When all gates and review are complete, run the real preparation from clean
main in an interactive terminal:
scripts/create-release.sh "$release_version"
The script builds the backend, frontend, AIUI and radio tools, creates a signed
pending manifest and staged OTA artifacts, commits the release preparation, and
creates annotated tag v$release_version. During the signing prompt, paste the
24-word release master mnemonic once, press Enter, then Ctrl-D on the next
line. Never put the mnemonic in a command, file, chat, log, or Git. Do not use
RELEASE_MASTER_MNEMONIC for the normal operator ceremony.
If an already-prepared pending manifest needs signing on the offline/operator terminal, first ensure the release binary was built from the exact frozen commit, then run:
bash scripts/sign-manifest.sh \
"releases/pending/v${release_version}/manifest.json"
The script cryptographically verifies the result against the public release root pinned in the binary. A failed verification is a hard stop.
Review the preparation commit, tag target, staged artifacts and signature. Push the exact reviewed commit/tag to both publication mirrors according to the ngit-first workflow; do not create independent merges on each platform.
3. Build and sign the installer ISO
The ISO builder requires clean main, matching versions, the annotated tag and
the signed live manifest. After OTA publication has safely promoted the live
manifest (next section), return to the exact tagged clean tree and run:
scripts/build-iso-release.sh
For a normal release, do not pass --skip-gates or --no-qemu. The command
runs the release harness, strict catalog check, artifact checks, ISO build,
mount-level smoke test and headless QEMU boot. Keep its final PASS summary as
release evidence.
Sign the generated ISO checksum document in the operator ceremony:
iso_path=/absolute/path/to/archipelago-${release_version}.iso
bash scripts/sign-iso-checksums.sh "$iso_path"
core/target/release/archipelago ceremony verify \
"$iso_path.sha256.json"
sha256sum -c "$iso_path.sha256"
The signing script again expects one mnemonic paste, Enter, then Ctrl-D. It does not place the mnemonic on disk.
4. Publish without exposing incomplete updates
Configure the Gitea remote with a public HTTPS URL and keep its credentials in Git's credential helper, never in the remote URL. Then run:
scripts/publish-release-assets.sh "$release_version" gitea-vps2
This script deliberately pushes the tag first, creates the release, uploads and
byte-verifies the OTA assets, and pushes the manifest-bearing main last. When
the signed ISO files exist, the same command attaches and verifies them. Never
manually publish releases/manifest.json before its referenced assets are
downloadable and verified.
After publishing, mirror the exact accepted main commit and annotated tag to
ngit and Gitea, then verify both:
python3 scripts/check-git-mirrors.py --local \
--ref "refs/tags/v${release_version}"
python3 scripts/check-git-mirrors.py --local --all
scripts/check-release-assets.sh releases/manifest.json
A failed push, missing ref, tag-object mismatch, unavailable mirror, incomplete asset, or mismatched byte hash blocks publication. A main-only check is not full historical mirror parity.
5. Canary and final acceptance
Before announcing the release:
- Apply OTA to one non-critical canary from the signed manifest.
- Reboot it and verify management health, signer/native identity, installed and deliberately removed apps, wallet state, persistent app data, terminal, networking/FIPS, and the active regression checklist.
- Exercise rollback/recovery without changing wallets or app data.
- Boot the ISO in QEMU and on representative physical hardware; perform a clean install and an upgrade-path check, then repeat the same acceptance matrix.
- Verify public release downloads and both Git mirrors once more. Record exact commit, annotated tag object, artifact hashes, signer verification, node evidence, ngit proposal/merge, known deferrals and operator acceptance in the release ledger.
Do not call the release complete if any required live acceptance, signature, mirror, asset, OTA reboot/rollback, or ISO boot/install result is missing.