7fa8953ffadb6114582946df676e09c6ebce366a
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 4720a214a2 |
ci(release): recompose the installer bundle after publishing
Phase 0 item 3 of docs/installer/PLAN.md wired up from this side. The installer does not resolve "latest" at run time — it installs the exact combination named by a published bundle manifest (PLAN.md §7.1), so until now a new overlay release was invisible to operators until the installer repo's nightly cron noticed it. Adds a final step that POSTs to RunicGateway/installer's bundle workflow-dispatch endpoint. That job re-reads this tarball's manifest.json and checks its declared `protocol` against the sidecar's PROTOCOL_VERSION before publishing anything (gate 1) — the check this repo cannot perform for itself, since the C# plugin announces no version on the wire. It replaces the TODO the header has carried since #7, which was deliberately left unimplemented while there was nothing to dispatch. Dispatch, don't wait (PLAN.md §7.3): Gitea's dispatch endpoint returns no run handle, so there is nothing to poll — a waiting step would have to guess which run is its own while holding a runner idle. The bundle job runs its own gates regardless of who started it. A dispatch failure is a warning, never a failure of this job. By the time this step runs the release is published and correct, so failing the run would misreport that; the installer's nightly cron recomposes from whatever the latest releases actually are, making a dropped dispatch cost latency rather than correctness. That also means REGISTRY_TOKEN having write on the installer repo is a nicety, not a new hard requirement — noted in the header. Verified the workflow still parses and that the new step is last, gated on release=='true', and contains no path that can exit non-zero. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| eebc74ac8d |
fix(release): preflight credentials and recover the orphaned v0.1.0 tag
The first release run tagged the repo and then failed, leaving v0.1.0 with no release behind it and no way to ever get one. REGISTRY_USER and REGISTRY_TOKEN are not configured on this repo, but the tag push SUCCEEDED anyway: actions/checkout leaves an `http.<host>.extraheader` credential in the local git config, so `git remote set-url` to a URL with empty credentials still authenticated through that leftover header. The release API call had no such fallback and returned 401 (visible in the run log as `REGISTRY_USER:` / `REGISTRY_TOKEN:` with empty values). So the run got exactly far enough to do the one thing that is hard to undo. Worse, that state was self-perpetuating. The plan step treated any existing tag as "nothing to release", so every subsequent push to main would see v0.1.0, set RELEASE=false, and stand down — the release would never appear, and no amount of re-running would fix it. Two fixes: A credential preflight, before anything is built or pushed, gated on the run intending to publish so a docs:/chore:-only merge still passes on a repo without secrets. It names the missing secrets and the scope they need, rather than failing at whichever step happens to use them first. Orphan-tag recovery. The plan step now asks the API whether a release exists for the tag: 200 means stand down, 404 means an earlier run died after tagging, so reuse the tag and publish the release it is missing. This deliberately overrides the RELEASE=false the bump logic just decided — with the tag already in place there are no releasable commits after it, which is precisely why the stuck state could not clear itself. Anything other than 200/404 (network failure, bad token) is refused rather than guessed, because assuming "no release" would republish over a good one. The tag step reuses an existing tag instead of failing on `git tag`, and a recovery run's changelog summarizes what the tag contains (previous-tag..this-tag) instead of the empty range after it. Once REGISTRY_USER / REGISTRY_TOKEN are set, the next push to main will finish the release that the first run started — v0.1.0, from the same commit it already points at. Verified against the live repo state: the plan step now reports release=true reuse_tag=true for the orphaned v0.1.0 and renders the correct changelog; a tag that does have a release (checked against link's v0.3.0) still stands down; a fresh repo still takes the seed path; and the preflight fails loudly on empty secrets and passes on populated ones. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| ebbfab51fc |
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>
|