84c1106d58a3d58b2851f4ede8be4dca17d93af9
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 82900da939 |
feat(installer): back up what a run is about to overwrite
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 1m59s
PLAN.md §5.3. Before anything is written, every file this run will replace is copied into <state>/backups/<utc-stamp>/ with a manifest naming where each came from. --no-backup opts out; --verify takes none. Scoped by what cannot be fetched again. The sidecar binary and the overlay files are re-downloadable and hash-named in the bundle, and the database is a cache with a schema -- link's store.rs creates every table IF NOT EXISTS over shard state the sweeps repopulate. What a run can destroy for good is an operator's edits to a deployed .cs file, which Phase 1 overwrites unconditionally and by design, and sidecar.toml, whose token the website already holds. Two deviations from §5.3 as written, both found by building it: - The trigger is "this run is about to overwrite something", not "an update, or an install over an existing record". §5.3 justified the latter with "a first install overwrites nothing" -- which is not true of a tree deployed by hand per INSTALL.md Appendix A2, a documented path. There the first install finds .cs files that differ, plans them as Change, and overwrites them with no record anywhere. The direct test covers that case and still writes nothing for a genuine first install, because there is nothing to copy. - sidecar.toml joins a backup that is already being taken and is never the reason for one. Nothing here rewrites it, so making it a trigger would put a dated directory on disk after every no-op update; it is copied so a restored set of files comes with the token that matches them. The directory is created lazily and the manifest is written last, so a directory carrying one is a complete backup -- and pruning only considers those, so a run interrupted mid-copy cannot evict a good backup by being newer than it. Three are kept. uninstall keeps them and names them in its report; --purge removes them, alongside the config, the database and the cached patch set. doctor reports the newest. Restoring stays printed rather than done, as the uninstall report is: the installer cannot know what has changed since, and putting an old .cs file back over a newer overlay eats work rather than saving it. Verified live against two scratch ServUO trees built from the real 57.4 files: a clean first install leaving no backups directory at all, an update after editing a deployed .cs (copy holds the edit, tree gets the release's file, manifest lists both it and sidecar.toml), a no-op update taking none, --no-backup and --verify each taking none, a fourth backup pruning the oldest, doctor's row, uninstall keeping three and listing them, --purge removing them, and a --patches run capturing the pre-patch Logging.cs while the two rung-0 patches correctly captured nothing. fmt, clippy -D warnings and 144 tests on both Linux and Windows. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| 80b1c0da24 |
feat(installer): implement Phase 4 — doctor, update and uninstall
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 59s
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>
|