|
|
|
|
@@ -1,8 +1,17 @@
|
|
|
|
|
# Runic Gateway Installer — plan
|
|
|
|
|
|
|
|
|
|
Status: **planning**. No installer code exists yet. 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)).
|
|
|
|
|
Status: **Phase 0 in progress.** No installer code exists yet. 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)).
|
|
|
|
|
|
|
|
|
|
| Phase 0 item | State |
|
|
|
|
|
|---|---|
|
|
|
|
|
| 0.1 `servuo-plugins` release workflow | ✅ Merged — [servuo-plugins#7](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/7) + [#8](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/8); first overlay release is [`v0.1.1`](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/releases/tag/v0.1.1) |
|
|
|
|
|
| 0.2 `link` installable (data paths + `--print-config`) | 🟨 In review — [link#24](https://gitea.whitlocktech.com/RunicGateway/link/pulls/24) |
|
|
|
|
|
| 0.3 Bundle CI in the installer repo | ⬜ Not started — **next**; both components it composes now exist |
|
|
|
|
|
| 0.4 This file + `INSTALL.md` | 🟦 This file exists; `INSTALL.md` waits on the shape settling |
|
|
|
|
|
| — Repo bootstrap (governance + CI) | ✅ [`RunicGateway/installer`](https://gitea.whitlocktech.com/RunicGateway/installer) created; workflows merged ([installer#1](https://gitea.whitlocktech.com/RunicGateway/installer/pulls/1), [#2](https://gitea.whitlocktech.com/RunicGateway/installer/pulls/2)) |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
@@ -29,6 +38,7 @@ release/start scripts. The installer never writes a launcher.
|
|
|
|
|
| Code signing | **Unsigned.** `SHA256SUMS` is the trust anchor; SmartScreen/Gatekeeper warnings are expected and documented, as with most self-hosted tooling |
|
|
|
|
|
| Language | **Rust** — single static binary per OS, reuses the cross-compile pattern already proven in `link/.gitea/workflows/release.yml` |
|
|
|
|
|
| Plugin source | **Release tarball artifact** — no git and no Gitea credentials on the shard host |
|
|
|
|
|
| Composition | **Published bundle manifest** (§7.1). CI names an exact, protocol-checked combination of component versions; the installer fetches it at run time and `--bundle <tag>` pins one. Component releases regenerate JSON, not the installer binary |
|
|
|
|
|
| Token handoff | **Print token + prefilled admin URL** at the end of the run |
|
|
|
|
|
| Repo | **New repo**, `RunicGateway/installer`. It deploys *both* other components, so living inside `link/` would invert the dependency |
|
|
|
|
|
| ServUO version | **Warn and skip.** Patches are verified against stock 57.4 only; on anything else the base install proceeds and the patch tier is skipped with a warning. Forks are the norm in a public audience — refusing outright would block most operators |
|
|
|
|
|
@@ -104,9 +114,13 @@ Two files, two owners:
|
|
|
|
|
| `/etc/runicgateway/sidecar.toml` | uo-link | The sidecar's own schema, unchanged. Service sets `UOLINK_CONFIG` to this path |
|
|
|
|
|
| `/etc/runicgateway/install.json` | installer | Deployed versions, file hashes, applied patches, ServUO path, timestamps |
|
|
|
|
|
|
|
|
|
|
**Working-directory trap:** the sidecar writes both `sidecar.toml` and `uo-link.db` relative to CWD.
|
|
|
|
|
Under `C:\Program Files\` that fails or silently lands in VirtualStore. The service definitions must
|
|
|
|
|
pin `UOLINK_CONFIG` and `UOLINK_DB_PATH` explicitly:
|
|
|
|
|
**Working-directory trap:** the sidecar wrote both `sidecar.toml` and `uo-link.db` relative to CWD.
|
|
|
|
|
Under `C:\Program Files\` that fails or silently lands in VirtualStore. Phase 0.2 fixed the second
|
|
|
|
|
half in the sidecar — a relative `[store].path` now resolves against the directory holding
|
|
|
|
|
`sidecar.toml`, so pinning the config alone is enough to put the database somewhere deterministic —
|
|
|
|
|
but the config path itself is still CWD-relative by default, and "deterministic" is not the same as
|
|
|
|
|
"where this install wants it". The service definitions therefore still pin `UOLINK_CONFIG` and
|
|
|
|
|
`UOLINK_DB_PATH` explicitly:
|
|
|
|
|
|
|
|
|
|
- Linux: config `/etc/runicgateway/sidecar.toml`, db `/var/lib/runicgateway/uo-link.db`, dedicated
|
|
|
|
|
service user
|
|
|
|
|
@@ -121,6 +135,12 @@ becomes write-only. This is the largest "I installed it and nothing happened" fa
|
|
|
|
|
|
|
|
|
|
The installer closes it by printing a copy-paste block at the end of a successful run — see §6.
|
|
|
|
|
|
|
|
|
|
Phase 0.2 supplied the missing half of that: `uo-link-sidecar --print-config` provisions the config
|
|
|
|
|
if absent and prints the resolved settings — token, both binds, `ws_path`, protocol version, db
|
|
|
|
|
path — as JSON. The installer reads the block it prints out of that one call. **It never parses the
|
|
|
|
|
log**, which was the alternative and would have made the handoff depend on a log format that is not
|
|
|
|
|
a contract.
|
|
|
|
|
|
|
|
|
|
### 2.5 `deploy.ps1` cannot be the cross-platform deployer
|
|
|
|
|
|
|
|
|
|
It is PowerShell-only; a Linux ServUO host running .NET typically has no `pwsh`. It also hard-throws
|
|
|
|
|
@@ -133,15 +153,16 @@ operators.
|
|
|
|
|
|
|
|
|
|
### 2.6 Prerequisites the overview assumed away
|
|
|
|
|
|
|
|
|
|
- **`servuo-plugins` has no release workflow.** Only `link` does. "Pull latest repository" is
|
|
|
|
|
replaced by a release tarball, which has to be built first (Phase 0).
|
|
|
|
|
- **`servuo-plugins` had no release workflow.** Only `link` did. "Pull latest repository" is replaced
|
|
|
|
|
by a release tarball, which had to be built first — Phase 0 item 1, now in review
|
|
|
|
|
([servuo-plugins#7](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/7)).
|
|
|
|
|
- **arm64 is not buildable today.** `link/release.yml` cross-compiles only
|
|
|
|
|
`x86_64-unknown-linux-gnu` and `x86_64-pc-windows-gnu`. An arm64 `.deb` needs another cross
|
|
|
|
|
toolchain.
|
|
|
|
|
- **The compat matrix has no home.** `PROTOCOL_VERSION` currently lives only in
|
|
|
|
|
`link/sidecar/src/main.rs`. The sidecar publishes it via `X-UOLink-Version` and `/health`, and the
|
|
|
|
|
website stores an expected value — but the *plugin's* protocol version is not queryable before
|
|
|
|
|
boot. See §7.
|
|
|
|
|
- **The compat matrix has no home.** `PROTOCOL_VERSION` lives in `link/sidecar/src/main.rs`. The
|
|
|
|
|
sidecar publishes it via `X-UOLink-Version` and `/health`, and the website stores an expected
|
|
|
|
|
value — but the *plugin's* protocol version is not queryable before boot. Phase 0 item 1 gives it
|
|
|
|
|
a home: `servuo-plugins/overlay.toml`, declared into the overlay manifest. See §7.0 / §7.1.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
@@ -156,7 +177,7 @@ Runic Gateway Installer v1.0.0
|
|
|
|
|
├── runicgateway-installer-linux-x86_64
|
|
|
|
|
└── SHA256SUMS
|
|
|
|
|
|
|
|
|
|
uo-link v3.x.y (existing release, extended)
|
|
|
|
|
uo-link v0.x.y (existing release, extended)
|
|
|
|
|
├── uo-link-sidecar-windows-x86_64.exe
|
|
|
|
|
├── uo-link-sidecar-linux-x86_64
|
|
|
|
|
├── runicgateway-link_<ver>_amd64.deb (Phase 5)
|
|
|
|
|
@@ -167,6 +188,9 @@ servuo-plugins v<ver> (new release, Phase 0)
|
|
|
|
|
└── SHA256SUMS
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Binding those together is the **bundle manifest** (§7.1) — published by the installer repo's CI, not
|
|
|
|
|
by any component, and the thing the installer actually resolves against.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
scp runicgateway-installer-linux-x86_64 user@server:/tmp/
|
|
|
|
|
chmod +x runicgateway-installer-linux-x86_64
|
|
|
|
|
@@ -218,10 +242,62 @@ Repo work that must land before an installer can exist.
|
|
|
|
|
consume only `{version, changelog, artifacts}`). The adapter half produces
|
|
|
|
|
`runicgateway-overlay-<ver>.tar.gz` containing `overlay/`, `patches/`, and a `manifest.json`
|
|
|
|
|
(version, commit, per-file SHA256, declared protocol version, minimum ServUO version).
|
|
|
|
|
|
|
|
|
|
As built ([servuo-plugins#7](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/7)),
|
|
|
|
|
with three deviations from `link`'s copy that each fell out of the repo rather than being chosen:
|
|
|
|
|
|
|
|
|
|
- **No build gates, structural gates instead.** Nothing in that repo can be compiled without
|
|
|
|
|
ServUO reference assemblies, so CI asserts what it honestly can: `Bridge.cfg` and the Bridge
|
|
|
|
|
scripts present, `Scripts.csproj` present (its absence ships code that never compiles while
|
|
|
|
|
ServUO reports success — §2.1), every `.patch` parseable via `git apply --stat`, and each
|
|
|
|
|
patch's companion `.cs` present.
|
|
|
|
|
- **No bump commit, so no push to `main`.** `link` writes the version into `Cargo.toml` because
|
|
|
|
|
the binary embeds it; the tarball embeds nothing but the generated manifest, so the tag *is*
|
|
|
|
|
the version. That workflow needs no branch-protection exception.
|
|
|
|
|
- **`overlay.toml` at the repo root** holds the declared `protocol` and the ServUO compatibility
|
|
|
|
|
values, read by CI into the manifest. It exists because the number needs one maintained home —
|
|
|
|
|
see §7 for why the plugin cannot simply be asked.
|
|
|
|
|
|
|
|
|
|
The tarball uses a **fixed** top-level directory, `runicgateway-overlay/`, not a versioned one:
|
|
|
|
|
the installer looks for `overlay/`, `patches/` and `manifest.json` at known paths rather than
|
|
|
|
|
parsing the version it is trying to read. Member order, mtime and ownership are pinned, so a
|
|
|
|
|
given tree yields a byte-identical tarball and its checksum moves only when its contents do.
|
|
|
|
|
2. **`link`: make the sidecar installable.** Confirm/settle default data paths, and add a way to
|
|
|
|
|
read back config non-interactively (e.g. `--print-config` emitting JSON: bind addresses, token,
|
|
|
|
|
protocol version, db path) so the installer does not have to scrape logs for the token.
|
|
|
|
|
3. **Decide and document the compat matrix format** (§7).
|
|
|
|
|
|
|
|
|
|
As built ([link#24](https://gitea.whitlocktech.com/RunicGateway/link/pulls/24)) — the sidecar
|
|
|
|
|
had **no CLI at all** before this, so the shape was chosen rather than inherited:
|
|
|
|
|
|
|
|
|
|
- **Four flags, hand-rolled:** `--print-config`, `--config <PATH>`, `--version`, `--help`. No
|
|
|
|
|
argument-parsing crate — it would be larger than the code it replaced — and deliberately no
|
|
|
|
|
flags that duplicate a config key, so `sidecar.toml` stays the single place settings live.
|
|
|
|
|
An unrecognized argument exits `2`; silently ignoring a typo'd flag would start a sidecar that
|
|
|
|
|
is not the one the installer asked for.
|
|
|
|
|
- **`--print-config` performs first-run setup rather than only reporting.** It runs the same
|
|
|
|
|
load path a normal start does, so a missing config file is written and a blank token is
|
|
|
|
|
generated and saved. That collapses "provision the sidecar" and "find out its token" into one
|
|
|
|
|
non-interactive call — which is exactly the sequence §6 needs. `config_created` and
|
|
|
|
|
`token_generated` say whether *this* run did either, because the values alone cannot
|
|
|
|
|
distinguish a fresh install from a re-read of an existing one, and a re-run must not report a
|
|
|
|
|
token as newly minted.
|
|
|
|
|
- **The document is the whole of stdout.** The log subscriber writes to stdout, so it is not
|
|
|
|
|
started in this mode. `ws_path` is emitted from the same constant the route is registered
|
|
|
|
|
with, so the installer's WebSocket URL cannot drift from the server's.
|
|
|
|
|
- **Relative `[store].path` now anchors to the config file's directory, not the CWD** — see
|
|
|
|
|
§2.3, which this half-closes on the sidecar side. Absolute paths are used as written; parent
|
|
|
|
|
directories are created; `:memory:` and `file:` URIs are left alone.
|
|
|
|
|
- **The db path is handed to sqlx as a path, not a `sqlite://` URL.** The URL spelling is
|
|
|
|
|
parsed as one: it percent-decodes the path and splits it on `?`, so an installed path
|
|
|
|
|
containing `%20` opened a different file than the operator named.
|
|
|
|
|
- **No platform data directories are compiled in.** That is the "settle" half of this item, and
|
|
|
|
|
the answer is that the *installer* owns layout (§2.3) and pins `UOLINK_CONFIG` /
|
|
|
|
|
`UOLINK_DB_PATH` in the service definition. Baking `/etc` and `%ProgramData%` defaults into
|
|
|
|
|
the binary would give the same paths two owners and break `cargo run` in a working tree.
|
|
|
|
|
3. **Bundle CI in the installer repo** (§7). Compose job (read both repos' latest releases → run the
|
|
|
|
|
two gates → publish `bundle.json`), the nightly cron, and the dispatch step appended to each
|
|
|
|
|
component's release workflow. This must exist before Phase 1 is useful, since the installer
|
|
|
|
|
resolves what to install *from* the bundle.
|
|
|
|
|
4. **`docs`: this file, plus `docs/installer/INSTALL.md`** (the operator-facing guide) once the
|
|
|
|
|
shape is settled.
|
|
|
|
|
|
|
|
|
|
@@ -242,7 +318,10 @@ Repo work that must land before an installer can exist.
|
|
|
|
|
- Windows: `%ProgramFiles%\RunicGateway\`, data in `%ProgramData%\RunicGateway\`, service
|
|
|
|
|
registration with automatic start and restart-on-failure.
|
|
|
|
|
- Both: `UOLINK_CONFIG` and `UOLINK_DB_PATH` pinned in the service definition (§2.3).
|
|
|
|
|
- Token surfacing (§6).
|
|
|
|
|
- Token surfacing (§6): run the installed binary once as
|
|
|
|
|
`uo-link-sidecar --print-config --config <the pinned path>` **before** registering the service.
|
|
|
|
|
That both writes the config the service will read and returns the token to print, so the service
|
|
|
|
|
never starts against a config that does not exist yet.
|
|
|
|
|
|
|
|
|
|
### Phase 3 — patch tier (opt-in)
|
|
|
|
|
|
|
|
|
|
@@ -255,9 +334,9 @@ degrade loudly rather than silently.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
✓ ServUO found /opt/ServUO (57.4)
|
|
|
|
|
✓ Overlay in sync 23 files, all hashes match install.json
|
|
|
|
|
✓ Overlay in sync 30 files, all hashes match install.json
|
|
|
|
|
⚠ Patch tier 1 of 3 applied — vendor.sale unavailable
|
|
|
|
|
✓ uo-link installed 3.0.1
|
|
|
|
|
✓ uo-link installed 0.3.0
|
|
|
|
|
✓ Service running, enabled
|
|
|
|
|
✓ Sidecar reachable 127.0.0.1:8080 /health ok
|
|
|
|
|
✓ Protocol sidecar 3 = overlay manifest 3
|
|
|
|
|
@@ -267,12 +346,22 @@ degrade loudly rather than silently.
|
|
|
|
|
The last check matters most: it is the only thing that distinguishes "files copied" from "the bridge
|
|
|
|
|
actually works" (§2.1).
|
|
|
|
|
|
|
|
|
|
`runicgateway update` — asymmetric by component, deliberately:
|
|
|
|
|
Three of those rows are answered by the sidecar's own CLI rather than by inspecting the filesystem:
|
|
|
|
|
`--version` prints `uo-link-sidecar <ver> (protocol <n>)`, and `--print-config` gives the config and
|
|
|
|
|
db paths the *installed service* resolves — so `doctor` reports what the binary would actually do,
|
|
|
|
|
not what `install.json` believes it was told to do. The protocol row compares that number against
|
|
|
|
|
the overlay manifest's declared one (§7.0).
|
|
|
|
|
|
|
|
|
|
- **uo-link**: query the Gitea releases API → compare versions → download → verify checksum →
|
|
|
|
|
`runicgateway update` — resolves the current bundle (§7.1), then acts asymmetrically by component,
|
|
|
|
|
deliberately:
|
|
|
|
|
|
|
|
|
|
- **uo-link**: compare the bundle's version against what is installed → download → verify checksum →
|
|
|
|
|
replace binary → restart service.
|
|
|
|
|
- **plugin overlay**: download the newer overlay tarball → verify → re-sync → record commit → tell
|
|
|
|
|
the operator ServUO must restart (the installer does not restart the shard).
|
|
|
|
|
- **plugin overlay**: download the bundle's overlay tarball → verify → re-sync → record commit →
|
|
|
|
|
tell the operator ServUO must restart (the installer does not restart the shard).
|
|
|
|
|
|
|
|
|
|
Because both come from one bundle, an update always moves to a combination whose protocol versions
|
|
|
|
|
were checked together, rather than to two independently-latest artifacts that may disagree.
|
|
|
|
|
|
|
|
|
|
`runicgateway uninstall` — **removes only what it exclusively owns, and never edits the ServUO
|
|
|
|
|
tree.** The installer cannot know what the operator has changed in those files since deployment, so
|
|
|
|
|
@@ -314,14 +403,23 @@ Paste these into Admin → Shard on your Runic Gateway site:
|
|
|
|
|
The token is write-only once saved — the site will never show it back to you.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Every value in that block except the host and the site URL comes from one
|
|
|
|
|
`uo-link-sidecar --print-config` call (§2.4): `web.auth_token`, `protocol`, and `web.bind` +
|
|
|
|
|
`web.ws_path` for the two URLs. Only the **host** is substituted — `web.bind` is frequently
|
|
|
|
|
`0.0.0.0`, which is not something to hand a website — so the installer composes the URLs from the
|
|
|
|
|
host it detects or prompts for, rather than echoing the bind address.
|
|
|
|
|
|
|
|
|
|
The installer prompts for the site URL only to build that link; it never contacts the website. A
|
|
|
|
|
future "installer registers itself with the website" flow (claim code + authenticated endpoint) is
|
|
|
|
|
explicitly **out of scope** — it is real backend work in a security-sensitive area and can be added
|
|
|
|
|
later without changing anything here.
|
|
|
|
|
|
|
|
|
|
The printed token is a secret in transit: `--print-config` output must go to the operator's
|
|
|
|
|
terminal and the config file, never into an installer log file or a support bundle.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 7. Version tracking and the compat matrix
|
|
|
|
|
## 7. Version tracking, the bundle, and release orchestration
|
|
|
|
|
|
|
|
|
|
Three components version independently, bound by a protocol contract:
|
|
|
|
|
|
|
|
|
|
@@ -329,13 +427,108 @@ Three components version independently, bound by a protocol contract:
|
|
|
|
|
`X-UOLink-Version` on every response; a mismatch is rejected `409`.
|
|
|
|
|
- **website** — stores an expected protocol version in `uoLinkConfig` (admin-managed).
|
|
|
|
|
- **plugin overlay** — has no queryable version before ServUO boots. The overlay release
|
|
|
|
|
`manifest.json` declares it, and `install.json` records what was deployed. `doctor` compares the
|
|
|
|
|
recorded overlay protocol version against the sidecar's live one.
|
|
|
|
|
`manifest.json` declares it, and `install.json` records what was deployed.
|
|
|
|
|
|
|
|
|
|
**Open risk:** the v3 cutover is mid-flight — protocol work landed on `edge` branches with the
|
|
|
|
|
`edge → main` cutover still open across four repos. Until that lands, `main` and `edge` disagree
|
|
|
|
|
about `PROTOCOL_VERSION`, so the installer must not hardcode a version anywhere; it reads what the
|
|
|
|
|
artifacts declare. See `docs/link/v3.md`.
|
|
|
|
|
### 7.0 The overlay manifest
|
|
|
|
|
|
|
|
|
|
Shipped inside every `runicgateway-overlay-<ver>.tar.gz`, generated by that repo's release workflow:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"component": "servuo-plugins-overlay",
|
|
|
|
|
"version": "0.1.0",
|
|
|
|
|
"commit": "968b526…",
|
|
|
|
|
"repo": "RunicGateway/servuo-plugins",
|
|
|
|
|
"protocol": 3,
|
|
|
|
|
"servuo": { "min_version": "57.4", "patches_verified_against": "57.4" },
|
|
|
|
|
"files": { "overlay/Config/Bridge.cfg": "32718424…", "patches/…": "…" }
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`version` and `commit` come from the release engine; `protocol` and the `servuo` block are read from
|
|
|
|
|
`servuo-plugins/overlay.toml`; `files` is a SHA256 per shipped file.
|
|
|
|
|
|
|
|
|
|
Two of these carry weight beyond documentation:
|
|
|
|
|
|
|
|
|
|
- **`protocol` is a hand-maintained declaration, and has to be.** The plugin announces no version on
|
|
|
|
|
the wire and none is queryable before ServUO boots, so nothing in CI can derive it — which makes
|
|
|
|
|
this line the only thing §7.1's gate 1 has to compare the sidecar against. The duty is stated in
|
|
|
|
|
`overlay.toml` and in that repo's README: **bump it in the same PR that changes the emitters**, the
|
|
|
|
|
way `link` bumps `PROTOCOL_VERSION`.
|
|
|
|
|
- **`files` is what makes `doctor` able to tell "the operator edited a deployed file" from "the
|
|
|
|
|
overlay moved on"** (§5, Phase 4). The installer copies these hashes into `install.json` at deploy
|
|
|
|
|
time; a later mismatch against *both* the manifest and `install.json` means upstream changed, a
|
|
|
|
|
mismatch against `install.json` alone means local edits.
|
|
|
|
|
|
|
|
|
|
`min_version` and `patches_verified_against` are separate on purpose. The base overlay only *adds*
|
|
|
|
|
files and is expected to work broadly; the patch tier diffs stock ServUO files and is verified
|
|
|
|
|
against exactly one version (§2.2).
|
|
|
|
|
|
|
|
|
|
### 7.1 The bundle manifest
|
|
|
|
|
|
|
|
|
|
**The bundle is the compat matrix.** Rather than the installer hardcoding versions or blindly
|
|
|
|
|
resolving "latest", CI publishes a small manifest naming an exact, checked combination:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"bundle": "2026.08.01",
|
|
|
|
|
"protocol": 3,
|
|
|
|
|
"link": { "version": "0.3.0", "sha256": "a91f..." },
|
|
|
|
|
"overlay": { "version": "0.1.0", "commit": "968b526", "sha256": "7c3e..." }
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The installer fetches the current bundle at run time; `--bundle <tag>` pins an older one for a
|
|
|
|
|
reproducible install. Because the bundle is data, **a new `link` release regenerates ~20 lines of
|
|
|
|
|
JSON and leaves the installer binary untouched** — operators do not re-download the installer to
|
|
|
|
|
pick up a sidecar patch, and the installer does not accumulate releases whose code is byte-identical.
|
|
|
|
|
|
|
|
|
|
Two gates run at compose time, both cheap and both worth it:
|
|
|
|
|
|
|
|
|
|
1. The sidecar's `PROTOCOL_VERSION` must equal the overlay manifest's declared protocol version.
|
|
|
|
|
This is the check that catches an `edge`/`main` protocol mismatch before it reaches an operator.
|
|
|
|
|
2. Every referenced asset must exist and its SHA256 must match the publishing repo's `SHA256SUMS`.
|
|
|
|
|
|
|
|
|
|
### 7.2 What triggers a bundle
|
|
|
|
|
|
|
|
|
|
| Trigger | Why |
|
|
|
|
|
|---|---|
|
|
|
|
|
| `link` publishes a release | Its release job `POST`s to the installer repo's workflow-dispatch endpoint as its final step. `link/.gitea/workflows/release.yml` already declares `workflow_dispatch: {}` and already holds a `write:repository` token |
|
|
|
|
|
| `servuo-plugins` publishes a release | Same. Phase 0 item 1 gave it the release workflow; the dispatch step is marked as a TODO in that workflow's header and lands with the bundle CI it would call (item 3) — a step that `404`s on every release is worse than no step |
|
|
|
|
|
| Nightly cron on the installer repo | Recomputes from whatever the latest releases actually are, so a missed or failed dispatch self-heals instead of silently pinning operators to a stale sidecar |
|
|
|
|
|
|
|
|
|
|
`repository_dispatch` is deliberately avoided — support for it is uncertain on this Gitea version,
|
|
|
|
|
whereas dispatching an existing `workflow_dispatch` workflow via the API works today.
|
|
|
|
|
|
|
|
|
|
### 7.3 Stale-overlay handling: dispatch, don't wait
|
|
|
|
|
|
|
|
|
|
Each component **self-releases on merge to its own `main`**, using the same conventional-commit
|
|
|
|
|
engine. Note that "updated since the last release" must mean *releasable* commits — the engine sets
|
|
|
|
|
`RELEASE=false` when nothing but `docs:`/`chore:` has landed, so a docs typo correctly does **not**
|
|
|
|
|
cut an overlay release, and the bundle keeps using the existing one.
|
|
|
|
|
|
|
|
|
|
So by the time the installer's CI looks, the release normally already exists. If it finds
|
|
|
|
|
`servuo-plugins` main ahead of its latest release *with* releasable commits, it:
|
|
|
|
|
|
|
|
|
|
1. fires that repo's release workflow via workflow-dispatch and **does not wait for it**,
|
|
|
|
|
2. composes this bundle from the assets that exist right now,
|
|
|
|
|
3. writes a loud warning into the job summary.
|
|
|
|
|
|
|
|
|
|
The new overlay release lands minutes later on its own and the nightly cron folds it into the next
|
|
|
|
|
bundle. This gets the automation without the flaky part: dispatching another repo's workflow is
|
|
|
|
|
fine — that workflow still runs its own gates — but *polling* it is not, because Gitea's dispatch
|
|
|
|
|
endpoint returns no run handle, so the job would have to guess which run is its own and hold a
|
|
|
|
|
runner idle meanwhile. The warning exists so a genuinely broken release workflow surfaces once
|
|
|
|
|
rather than being silently retriggered every night forever.
|
|
|
|
|
|
|
|
|
|
### 7.4 Open risk
|
|
|
|
|
|
|
|
|
|
**Settled as of the v3 cutover.** Protocol work landed on `edge` branches and the `edge → main`
|
|
|
|
|
cutover has now merged, so `main` speaks protocol 3 consistently across the repos. The rule it
|
|
|
|
|
motivated stands regardless and is not a temporary measure: **the installer hardcodes no protocol
|
|
|
|
|
version anywhere.** It reads what the artifacts declare, and §7.1's gate 1 is what stops a
|
|
|
|
|
mismatched pair from being published as a bundle — which is the mechanism that will matter at the
|
|
|
|
|
*next* protocol bump, not just this one. See `docs/link/v3.md`.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
@@ -351,12 +544,14 @@ artifacts declare. See `docs/link/v3.md`.
|
|
|
|
|
3. **Co-location assumption** — the shard dials out to the sidecar on loopback `127.0.0.1:7788`, so
|
|
|
|
|
sidecar and ServUO must share a host. Should the installer support installing only uo-link on a
|
|
|
|
|
different host, or hard-assume co-location?
|
|
|
|
|
4. **Branch targeting for the new repo** — `link`, `website`, `servuo-plugins` and `docs` are
|
|
|
|
|
mid-cutover between `edge` and `main`. The installer repo starts clean on `main`; the Phase 0
|
|
|
|
|
`servuo-plugins` release workflow needs a target branch decision.
|
|
|
|
|
|
|
|
|
|
Resolved and moved into §1 / §2.2 / §5: uninstall scope, and minimum ServUO version.
|
|
|
|
|
|
|
|
|
|
**Resolved — branch targeting for the new repo** (was question 4). The v3 cutover landed:
|
|
|
|
|
`servuo-plugins#6` merged, so that repo's `main` and `edge` agree at protocol 3. The release
|
|
|
|
|
workflow targets `main`, and the installer repo starts clean on `main`. §7.4's caution still applies
|
|
|
|
|
in principle — the installer hardcodes no protocol version, it reads what the artifacts declare —
|
|
|
|
|
but the specific `edge`/`main` disagreement that motivated it is gone.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 9. Administrator experience
|
|
|
|
|
|