Cutover: promote the installer from edge to main #17
Reference in New Issue
Block a user
No description provided.
Delete Branch "edge"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this is
The
edge → maincutover. Merging this firesrelease.ymland cuts the first public installer release.edgehas been the integration branch since Phase 1, becauserelease.ymlpublishes on every push tomainand merging the crate there early would have shipped a binary that syncs the overlay but cannot install the sidecar. That reason is now spent: all five phases are built and both entry criteria are met.24 commits — Phases 1 through 5, plus the merge that brings
main's publishing fixes ontoedge.Both cutover gates are met
.deb, no MSI, Linux aarch64, backup-before-overwrite) and built — installer#9–#12, link#26, docs#96The Windows half is worth spelling out, since it was the long-standing unknown.
sc startfailed with 1053 on the first real run: the SCM waited 30 seconds for a handshake the sidecar could not perform, because it was a plain console program. Fixed inlink#29(v1.2.0) and verified against a live SCM service — 13/13 checks, start in 1s,RUNNING,/healthserved, service log written, clean stop.installer#16in this range makes the installer diagnose that error correctly instead of blaming the config file.systemd registration was verified separately on 2026-08-05 against a real privileged systemd container.
The bundle is ready
bundles/current.jsonon thebundlesbranch now names:v1.2.1 is v1.2.0 plus link#28 (a CI-only change), so the shipped sidecar carries the Windows service fix. Both halves declare protocol 3.
The merge commit in this range
edgehad diverged frommainby 7 commits (the publishing fixes:bundle.ymlpublishing to thebundlesbranch,release.ymlgoing tag-only, andbundles/*.jsonremoved frommain).84c1106merges those ontoedgeso this PR is conflict-free.It had one conflict, resolved in favour of
edge:maindeletedbundles/bundle-2026.08.04.jsonwhileedgehad renamed it totests/fixtures/published-bundle.json. Both changes say the same thing — published bundles no longer live onmain— so the fixture is kept. It stays frozen at 2026.08.04 deliberately: it is the crate's test input, not a mirror of what is published..gitea/workflows/release.ymlauto-merged cleanly.What merging this does
release.ymlonmain— tag-only as of installer#14, so it tags and publishes without pushing a version-bump commit to a protected branch.SHA256SUMS:linux-x86_64,linux-aarch64,windows-x86_64.exe.How it was tested
The merge result was built and gated on both platforms before this PR was opened:
rust:1-slim-bookworm, matching the CI runner):cargo fmt --checkOK,cargo clippy --locked --all-targets -- -D warningsclean,cargo test --locked140 unit + 9 integration passed. This is the one that matters — half the crate iscfg(unix), so a Windows-only pass proves nothing about CI.Merged-tree sanity checks:
bundles/retains onlyREADME.mdas the pointer, andtests/fixtures/published-bundle.jsonis present and parses.Checklist
AI-assisted contributions (required)
Claude Code (Opus 5). I have reviewed and understandevery change, and take responsibility for it. AI-authored commits are
marked with a
Co-Authored-Bytrailer.License
(GNU GPL v3.0 or later), and I have the right to contribute it.
Completes the command surface INSTALL.md §2 published before the binary existed. With this, `edge` cuts a binary that does everything that guide describes. doctor (src/doctor.rs) Reads only. Every row is answered by asking the thing itself — the installed binary (--version, --print-config), the service manager, and the sidecar's /health — because the record says what `install` did, which is a different question from what is true now. --print-config is run ONLY when the config already exists: that flag provisions, and a diagnosis must not create the state it reports on. It is also run under the environment the service pins (UOLINK_DB_PATH), so the config and database it names are the ones the service opens, not the ones the binary would pick on its own. Exit 1 when any row failed, so a monitoring script can read it; a ⚠ never does that. A stopped shard is therefore a ⚠, not a ✗ — "you have not started it" and "it is running and the bridge is dead" are different problems and only the second is broken. Offline is a ⚠ too: a shard host with no route to Gitea is a supported way to run this. The patch row re-resolves each recorded patch against the tree from the cached .patch, so a core upgrade or a restored backup that silently removed the tier's edits is caught — nothing else here would notice. update (src/update.rs, install.rs::Mode) The same pipeline as install, not a second one: PLAN.md describes it as "re-resolve the bundle, then move both components to it", which is what an install over an existing deployment already does. Writing it twice would give the sync rules and the protocol cross-checks two places to disagree. What differs is small and lives in Mode — a prior record is required, the tree comes from that record rather than detection, the patch tier's scope narrows, and the close is a diff instead of a handoff. The token is not reprinted: it has not changed and the website has it. A changed protocol number IS called out, because a stale value in Admin → Shard is answered with 409 and looks like the shard going offline. Tier scope: features an earlier run recorded are re-resolved without asking again (the record is the evidence of consent, including on an unsupported ServUO); anything new the release offers is named but not applied without --patches. A shard that declined stays declined. uninstall (src/uninstall.rs, service::remove) Removes the binary, the service and install.json; prints the overlay files and the exact hunks, rendered from the cached patches with the rung each landed at. Files edited since deployment are flagged so nobody deletes their own work blind. The report is also written to a file in the working directory — it is the only thing still needed after the command exits, and it arrives at the end of the longest output this tool produces. Two deviations from PLAN.md §5, both deliberate: - The cached patch set and patches/originals/ SURVIVE. That table put them under "removed", but the report tells the operator to diff against those originals — advice the same command would have made impossible to follow. --purge removes them, with the config and the database. - --yes means yes here, not "take the default". The prompt defaults to no (destructive), but the operator typed the verb; reading --yes as "no" would leave an unattended uninstall unable to express itself, and a script that appears to succeed while removing nothing is the worse failure. Exit 1 if a step could not be carried out — everything else still was. Verified on this machine against a scratch tree built from the real ServUO 57.4 files: a healthy doctor (exit 0), one with a deleted overlay file, an edited one and a reverted patch (all three found, exit 1), a --verify update that wrote nothing, a real update that repaired all three and left install.json byte-identical, uninstall with and without --purge, a second uninstall, and doctor/update on a host with no record. Linux fmt/clippy/tests run in Docker as well as the Windows host. Co-Authored-By: Claude <noreply@anthropic.com>Step 4 of PLAN.md §5.2, and the half that faces the operator: the release now cross-compiles aarch64-unknown-linux-gnu, and platform_key() resolves ("linux","aarch64") to the bundle key link publishes under instead of refusing the host by name. Same toolchain shape as the Windows step -- a linker plus a CC/AR pair, because ring (under ureq's rustls) compiles C and assembly. And the same packaging trap named in the sums comment: an artifact missing from SHA256SUMS is one `sha256sum -c` passes over silently, so the new binary is added to both the sums and the upload list. Two test changes fall out of the asset map growing a key: - The exact `assets.len() == 2` assertion is replaced by a check that each key CI requires is present and well-formed. An exact count would fail on the first bundle that adds arm64 -- reporting correct behaviour as a regression. - The host-binary lookup now accepts either outcome, and says why. Bundles are kept unchanged forever so `--bundle` stays reproducible, which means one published before arm64 existed can never gain that key. On such a host the run must fail with the reason rather than something that reads like a corrupt document, so sidecar_asset()'s error now says so and the test asserts it. Verified by cross-building this crate for aarch64 in a rust:1-slim-bookworm container -- ELF 64-bit LSB pie executable, ARM aarch64 -- and by running fmt, clippy -D warnings and the tests on both Linux and the Windows host, since only half of service.rs compiles on either. Co-Authored-By: Claude <noreply@anthropic.com>