ci(release): publish the overlay as a release tarball with a manifest

Phase 0 item 1 of the installer plan (docs/installer/PLAN.md §5). The
installer deploys the plugin from a release tarball rather than from git,
because the shard host gets neither git nor Gitea credentials — but this
repo published no releases at all, so there was nothing for it to fetch.
`link` was the only repo with a release workflow.

Reuses link/.gitea/workflows/release.yml's conventional-commit engine, as
that file's own header anticipated: the plan and release steps consume only
{version, changelog, artifacts}. Three things had to change, each forced by
this repo rather than chosen:

  No build. The plugin ships as C# source and ServUO compiles it at boot;
  it needs ServUO reference assemblies, so nothing here can be compiled in
  CI. The build gates are replaced by structural ones that assert what can
  honestly be asserted without a ServUO tree: Bridge.cfg and the Bridge
  scripts are present, Scripts.csproj (the silent-build-bug fix) is present,
  every .patch parses as a unified diff via `git apply --stat`, and each
  patch's companion .cs exists. Each of those has a way of shipping broken
  and only failing on an operator's live shard.

  No bump commit, so no push to main. link writes the version into
  Cargo.toml because the binary embeds it; a tarball embeds nothing but the
  manifest CI generates, so the git tag is the version. This workflow
  therefore never needs main to accept a direct push — no branch-protection
  exception for it.

  A manifest. The tarball carries manifest.json: version, commit, declared
  protocol version, ServUO compatibility, and a SHA256 per shipped file.

The manifest matters more than it looks. The plugin announces no version on
the wire and none is queryable before ServUO boots (PLAN.md §2.6), so its
declared protocol version is the ONLY thing that lets the installer's bundle
CI verify sidecar/overlay agreement before an operator installs the pair
(PLAN.md §7.1 gate 1). That declaration lives in the new overlay.toml
alongside the ServUO compatibility values, so it is one commented line to
maintain rather than a literal buried in a workflow — currently protocol 3,
per docs/link/v3.md.

Tarball layout uses a FIXED top-level directory (runicgateway-overlay/)
rather than a versioned one, so the installer can find overlay/, patches/
and manifest.json at known paths instead of parsing the version it is trying
to read. tar's member order, mtime and ownership are pinned, so a given tree
produces a byte-identical tarball and its checksum changes only when the
contents do.

Verified locally against the real tree before pushing: YAML parses, all six
run blocks pass bash -n, the plan step produces v0.1.0 from actual history,
the gates pass (22 bridge scripts, all three patches parse), the manifest
renders with protocol=3 and 30 file hashes, and two consecutive builds of
the tarball produce the same SHA256.

One real bug caught by running it rather than reading it: sha256sum marks
binary mode by prefixing the path with `*` instead of the two-space
text-mode separator, which would have put a leading `*` on every key in the
manifest. The capture now tolerates both.

Not included: the workflow-dispatch call into the installer's bundle CI
(PLAN.md §7.2). That is Phase 0 item 3 and there is nothing to dispatch yet;
the insertion point is marked in the header. A step that 404s on every
release is worse than no step.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-04 09:23:56 -05:00
parent 968b526fac
commit ebbfab51fc
4 changed files with 433 additions and 2 deletions

View File

@@ -27,6 +27,8 @@ integration guide, protocol spec, research — with full history preserved).
| `patches/` | Unified diffs against stock ServUO for files we must modify rather than add. |
| `tools/` | Never deployed. Test scaffolding (C# probes + PowerShell stub sidecars) and anything else that must not reach a server. |
| `deploy.ps1` | Copies `overlay/` into a server root. `-Verify` diffs instead of writing. |
| `overlay.toml` | Release metadata: the wire-protocol version this overlay speaks, and its ServUO compatibility. Read by CI into the release manifest — see [Releases](#releases). |
| `.gitea/workflows/release.yml` | Publishes `runicgateway-overlay-<ver>.tar.gz` on every merge to `main`. |
| [INTEGRATION.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/INTEGRATION.md) | **Website integration guide** — the WebSocket feed, REST endpoints, auth, event catalog, and examples. |
| [PLAN.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/PLAN.md) | Implementation plan, measured performance budget, and the full data catalog. |
| [RESEARCH.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/RESEARCH.md) | Original source-level research. Partly superseded — see the corrections table in `PLAN.md` §8. |
@@ -41,8 +43,9 @@ The two are deployed **together** but built **independently**:
- **This plugin** is deployed as *source*`deploy.ps1` copies `overlay/` into the ServUO server
root, and ServUO compiles it at boot (`Scripts.csproj`; see [Phase 0](#phase-0--what-it-fixes)).
There is no separate build artifact and no CI build — it cannot be compiled standalone without the
ServUO reference assemblies.
There is **no CI build** — it cannot be compiled standalone without the ServUO reference
assemblies. CI does publish a *source* tarball for the installer to fetch; see
[Releases](#releases).
- **The sidecar** is a standalone Rust binary, released from its own repo.
The **only** coupling is the loopback JSON protocol (the shard dials out to the sidecar on
@@ -59,6 +62,54 @@ without the sidecar running.
.\deploy.ps1 -ServerPath <servuo> # write
```
`deploy.ps1` is the **developer-facing** tool and stays that way. Operators get the
[Runic Gateway installer](https://gitea.whitlocktech.com/RunicGateway/installer), which does the
same sync cross-platform from the release tarball below.
## Releases
Every merge to `main` that carries a releasable conventional commit (`feat:`, `fix:`, `perf:`, or a
breaking change — a `docs:`/`chore:`-only merge deliberately cuts nothing) publishes a Gitea release:
```
runicgateway-overlay-<ver>.tar.gz
└── runicgateway-overlay/
├── manifest.json
├── overlay/ # exactly what deploy.ps1 would copy
└── patches/ # the opt-in stock-file diffs + their companion sources
SHA256SUMS
```
This is a **source** tarball, not a build — nothing here is compiled. It exists so the installer can
deploy the plugin onto a shard host that has no git and no Gitea credentials.
`manifest.json` is what makes the tarball self-describing:
```json
{
"component": "servuo-plugins-overlay",
"version": "0.1.0",
"commit": "968b526…",
"protocol": 3,
"servuo": { "min_version": "57.4", "patches_verified_against": "57.4" },
"files": { "overlay/Config/Bridge.cfg": "32718424…", }
}
```
- **`protocol`** comes from `overlay.toml` and is the plugin half of the compatibility contract. The
plugin announces no version on the wire and none is queryable before ServUO boots, so this
declaration is the only way the installer can check it against the sidecar's `PROTOCOL_VERSION`
*before* an operator installs the pair. **When the protocol changes, bump it in the same PR that
changes the emitters.**
- **`files`** carries a SHA256 per shipped file, so a deployment can later tell "an operator edited
this" from "the overlay moved on".
The version is derived from git tags — there is no version to maintain by hand and no bump commit,
so this workflow never pushes to `main`.
The tarball is byte-reproducible for a given tree (`tar --sort=name`, pinned mtime and ownership), so
its checksum changes only when its contents do.
## Status
| Phase | State |