docs(installer): record Phase 4 — doctor, update and uninstall as built
The installer crate now implements the whole command surface INSTALL.md
published before the binary existed, so this records what Phase 4 turned
out to be and corrects two places where the plan and the guide had drifted
apart.
PLAN.md
- Status header: Phases 1–4 are on `edge`; the edge → main cutover now
cuts a binary that does everything INSTALL.md describes, with Phase 5
being packaging polish rather than capability.
- A Phase 4 "as built" section: why `update` is the install pipeline in
a different mode rather than a second implementation, why it neither
reprints the token nor stays quiet about a protocol change, the tier's
scope under `update` (re-resolve what was applied, without re-asking;
name what is new), how `doctor` asks the binary the way the service
does, the exit-code rule and why a stopped shard is a ⚠ while a
running one that has not dialed in is a ✗.
- §5's uninstall table: the cached patch set and patches/originals/ move
from "removed" to "kept". The report that command prints tells the
operator to diff against those originals — deleting them made the
advice impossible to follow within one command's output. `--purge`
removes them.
INSTALL.md
- §2: exit codes stated (`doctor` and `uninstall` use 1 for a completed
run that found something wrong), `--patches` now applies to `update`,
`--yes` means yes on `uninstall`, `--purge` covers the patch cache.
- §7 doctor: the real row set, what ✓/⚠/✗ mean, that it writes nothing
and is safe to run with the shard up.
- §7 update: it updates the tree install.json names, needs the shard
stopped, does not reprint the token, calls out a protocol change, and
what it does and does not do with the patch tier.
- §7 uninstall: what survives, that edited files are flagged in the
listing, the confirmation's default, and where the report file lands.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
# Runic Gateway Installer — plan
|
||||
|
||||
Status: **Phases 1, 2 and 3 built, on `edge`.** Phase 0's prerequisites all landed, the installer
|
||||
repo publishes the bundle manifest, and [`INSTALL.md`](INSTALL.md) specified the operator-facing run
|
||||
Status: **Phases 1 to 4 built, on `edge`.** Phase 0's prerequisites all landed, the installer repo
|
||||
publishes the bundle manifest, and [`INSTALL.md`](INSTALL.md) specified the operator-facing run
|
||||
before the binary existed. The crate now implements the installer core (bundle resolution, ServUO
|
||||
detection and validation, the overlay sync, `install.json` — [Phase 1 as
|
||||
built](#phase-1--installer-core)), the sidecar half (binary, config, service, token handoff —
|
||||
[Phase 2 as built](#phase-2--uo-link-install-and-service)), and the patch tier (the rung ladder, the
|
||||
unsupported-version path, the cached patch set — [Phase 3 as built](#phase-3--patch-tier-opt-in)).
|
||||
All three are on the `edge` branch, not `main`, so no half-capable binary is released. **The
|
||||
`edge → main` cutover is next**, and it now cuts a binary that does everything `INSTALL.md`
|
||||
describes except `doctor`/`update`/`uninstall`, each of which says which phase it arrives in.
|
||||
[Phase 2 as built](#phase-2--uo-link-install-and-service)), the patch tier (the rung ladder, the
|
||||
unsupported-version path, the cached patch set — [Phase 3 as built](#phase-3--patch-tier-opt-in)),
|
||||
and the day-two commands `doctor`, `update` and `uninstall` ([Phase 4 as
|
||||
built](#phase-4--diagnostics-and-updates)). All four are on the `edge` branch, not `main`, so no
|
||||
half-capable binary is released. **The `edge → main` cutover is next**, and it now cuts a binary
|
||||
that does everything `INSTALL.md` describes — Phase 5 is packaging polish, not capability.
|
||||
This document is the design of record; it supersedes the informal overview it grew out of, which
|
||||
described a ServUO integration that does not match how `servuo-plugins` actually ships (see
|
||||
[Corrections](#corrections-to-the-original-overview)).
|
||||
@@ -106,7 +107,9 @@ most real shards are hand-modified. Therefore:
|
||||
(`/etc/runicgateway/patches/`, `%ProgramData%\RunicGateway\patches\`). Re-runs stay idempotent,
|
||||
and uninstall can print the exact hunks offline long after the release tarball is gone (§5,
|
||||
Phase 4). As built this caches every patch the tier *evaluated*, not only those that applied,
|
||||
because the refusal message names that path as the file to apply by hand.
|
||||
because the refusal message names that path as the file to apply by hand. **The cache outlives an
|
||||
uninstall** — see Phase 4, where the report that would have been left pointing at a deleted
|
||||
directory is what settled it.
|
||||
- **Cache the pre-image of every file the tier edits**, under `patches/originals/`, mirroring its
|
||||
path in the ServUO tree. It is written before the first edit and never overwritten, so a revert
|
||||
can be verified byte-for-byte rather than reconstructed from a printed diff — which matters most
|
||||
@@ -754,14 +757,87 @@ a clever automatic revert risks silently eating their work. It removes and it re
|
||||
|
||||
| Action | Scope |
|
||||
|---|---|
|
||||
| Removed | uo-link binary, its service entry (systemd unit / Windows service), `install.json` and the cached patch set |
|
||||
| Kept | `sidecar.toml` and `uo-link.db` (config and history survive; `--purge` to drop them) |
|
||||
| Removed | uo-link binary, its service entry (systemd unit / Windows service), `install.json` |
|
||||
| Kept | `sidecar.toml`, `uo-link.db`, and the cached patch set with its pre-patch originals (`--purge` to drop them) |
|
||||
| **Printed, not done** | Every overlay file deployed into the ServUO tree, listed by path, for the operator to delete |
|
||||
| **Printed, not done** | The exact hunks each applied patch added to `EventSink.cs`, `PlayerVendorGumps.cs`, `Logging.cs`, rendered from the cached `.patch` files — with the rung that applied each one (§2.2.1), since a `region-match` apply means the surrounding file was already the operator's — for them to revert by hand |
|
||||
|
||||
The printed report is also written to a file, so it survives the terminal scrollback of a long
|
||||
uninstall.
|
||||
|
||||
**As built** ([installer#7](https://gitea.whitlocktech.com/RunicGateway/installer/pulls/7)) —
|
||||
`src/doctor.rs`, `src/update.rs` and `src/uninstall.rs`, plus `service::observe`/`service::remove`
|
||||
and a `Mode` on the install pipeline. The decisions that were not already settled above:
|
||||
|
||||
- **`update` is the `install` pipeline in a different mode, not a second implementation.** This
|
||||
section describes it as "re-resolve the bundle, then move both components to it" — which is what
|
||||
an `install` over an existing deployment already does, down to keeping a modified `Bridge.cfg`
|
||||
and restarting the service after replacing its binary. A separate implementation would have given
|
||||
the sync rules, the two protocol cross-checks and the record-carrying logic a second place to
|
||||
disagree. What actually differs is four things: a prior record is **required** (an `update` on an
|
||||
uninstalled host is a typo or a state directory the run cannot see — never a first install under
|
||||
a verb that promises to preserve), the tree comes from that record rather than from detection (a
|
||||
host with two shards must not have an update silently move to the other one), the tier's scope
|
||||
narrows, and the close is a diff instead of a handoff.
|
||||
- **`update` does not reprint the token, and does call out a protocol change.** The token has not
|
||||
changed and the website already holds it; reprinting a secret nobody has to act on just puts it
|
||||
in another scrollback. The protocol number is the one thing an update *can* change that the
|
||||
website has to be told about — a stale value in Admin → Shard is answered `409` and looks to an
|
||||
operator exactly like the shard going offline.
|
||||
- **The tier under `update` re-resolves only what an earlier run applied, without asking again.**
|
||||
Not a fresh offer: a shard that declined stays unpatched through every update, which is what
|
||||
opt-in has to mean. Consent is not re-sought for what is already in the tree — including on an
|
||||
unsupported ServUO, where `install` demands a second flag — because the record *is* the evidence
|
||||
that the operator opted in, and re-prompting would make an unattended update impossible on
|
||||
precisely the hosts that most need their patches re-checked when an overlay moves. New features
|
||||
the release offers are named but not applied; `--patches` is how they are taken up. A feature the
|
||||
record shows as applied that the release no longer declares keeps its record rather than being
|
||||
dropped: its edits are still in the tree, and a record that forgot them would stop `uninstall`
|
||||
printing hunks that are really there.
|
||||
- **`doctor` asks the thing itself, and asks it the way the service does.** `--print-config` is run
|
||||
under the same `UOLINK_DB_PATH` the unit pins, so the config and database it names are the ones
|
||||
the *service* opens rather than the ones the binary would pick on its own — which is what §5's
|
||||
sketch promised and a bare call would have got wrong on Linux. It is also run **only when the
|
||||
config already exists**, because that flag provisions: a diagnosis must not create the state it
|
||||
is reporting on.
|
||||
- **`doctor` exits `1` when a row failed, and a `⚠` never causes that.** The rule makes it readable
|
||||
from a monitoring script, and the split is what keeps the report worth reading: a stopped shard
|
||||
is a `⚠` with the reason ("you have not started it"), while a *running* shard that has not dialed
|
||||
in is the `✗` (§2.1's silent failure). Being offline is a `⚠` too — a shard host with no route to
|
||||
Gitea is a supported way to run this, and failing a health check over it would report a working
|
||||
deployment as broken. Both network calls take short timeouts for the same reason.
|
||||
- **The patch row re-resolves each recorded patch against the tree.** The cached `.patch` makes it
|
||||
possible offline, and the expected answer is rung 0. A core upgrade, a hand revert or a restored
|
||||
backup silently removes the tier's edits, and nothing else in the report would notice.
|
||||
- **The cached patch set and `patches/originals/` survive an uninstall** — a deviation from the
|
||||
table above, which listed them as removed. The report that same command prints tells the operator
|
||||
to diff their stock files against those originals; deleting them would have made the advice
|
||||
impossible to follow within one command's output. They are the only offline record of what the
|
||||
tier changed once the release tarball is gone, so `--purge` is what removes them, alongside the
|
||||
config and the database. The report names every path it left behind.
|
||||
- **`--yes` means yes on `uninstall`, not "take the default".** Everywhere else that flag answers an
|
||||
offer the *run* made, so taking the safe default is right. Here the operator typed the destructive
|
||||
verb; reading `--yes` as "no" would leave an unattended uninstall unable to express itself at all,
|
||||
and a script that appears to succeed while removing nothing is the worse of the two failures. The
|
||||
interactive prompt still defaults to **no**, after listing exactly what will and will not be
|
||||
touched.
|
||||
- **`uninstall` exits `1` for a step it could not carry out**, having done everything else. The
|
||||
common case is a binary still locked by a sidecar somebody started by hand, so a permission error
|
||||
on that file says so rather than sending the operator to look at ACLs. The Linux service account
|
||||
is removed only when the record says this installer created it; Windows' virtual account goes with
|
||||
the service.
|
||||
- **The overlay listing flags files edited since deployment.** An operator deleting that list file
|
||||
by file must not lose their own `Bridge.cfg` settings or a script edit without being told which
|
||||
ones those are.
|
||||
|
||||
Verified on this machine against a scratch ServUO 57.4 tree built from the real files: a healthy
|
||||
`doctor` (exit 0), one against a tree with a deleted overlay file, an edited one and a reverted
|
||||
patch (all three found, exit 1), an `update --verify` that wrote nothing, a real `update` that
|
||||
repaired all three and left `install.json` byte-identical, `uninstall` with and without `--purge`,
|
||||
a second `uninstall`, a locked binary reported as a problem with exit 1, and `doctor`/`update` on a
|
||||
host with no record. `fmt`/`clippy -D warnings`/tests were run for Linux in Docker as well as on the
|
||||
Windows host, since only half of `service.rs` compiles on either.
|
||||
|
||||
### Phase 5 — packaging polish
|
||||
|
||||
`.deb` packaging, Windows MSI, arm64 cross build, and optional automated backup before upgrade.
|
||||
|
||||
Reference in New Issue
Block a user