All checks were successful
PR Checks / rust-gates (pull_request) Successful in 6s
Three things, all found by the first compose run that ever had a bundle
to write.
1. `main` is protected, so the push was declined by the pre-receive
hook -- twice, since the retry rebases and pushes to the same place.
Every bundle since v1.1.1 has been composed correctly and thrown
away. Bundles now go to a `bundles` branch of their own, at its
root, which needs no protection exception and keeps everything the
original choice was for: a reviewable diff, a git history of the
compat matrix, plain anonymous raw URLs, no credentials on the shard
host. The header's claim that this push "needs no new
branch-protection exception" was simply false.
2. A `${{ }}` written literally in a shell comment silently disabled
the entire stale-component check. The runner scans a step's script
for template expressions before running it, fails to parse the empty
one, and skips the step WITHOUT failing the job -- so the dispatch
that is supposed to fire a component's release workflow has never
run once. Reworded, with a warning not to write that token in a
comment again. (link/release.yml and this repo's release.yml carry
the same bug in their bump-and-tag step; handled separately.)
3. linux-aarch64 is now a REQUIRED platform key, which was step 3 of
PLAN.md §5.2 and was waiting on link publishing one. v1.1.1 does, so
from here a dropped target reddens this job instead of vanishing
from every bundle.
The published bundles are materialized into a worktree at `published/`,
so the ".2 suffix" scan and the idempotence check read what is actually
published rather than a stale copy on main. The branch is created from
an empty-tree root commit on first use, so it carries no history that
has nothing to do with the compat matrix; it has been seeded already
with bundle 2026.08.04, because every bundle is kept forever and the
move must not lose the one that exists.
bundles/*.json is deleted from main -- it is now a stale copy of data
that lives elsewhere, and a wrong "current" is worse than none. The
README stays and documents the branch.
Verified by running the whole job in a container against a bare repo
standing in for the remote: first run creates the branch and publishes
both files with all three asset keys, second and third runs report
"identical to the published current.json -- nothing to publish" and
push nothing, and the stale check now runs and reports both components
as having nothing releasable.
Co-Authored-By: Claude <noreply@anthropic.com>
107 lines
5.0 KiB
Markdown
107 lines
5.0 KiB
Markdown
# Bundles
|
|
|
|
**These files are generated. Do not edit them by hand.**
|
|
|
|
A *bundle* names one exact, protocol-checked combination of the two components the installer
|
|
deploys — a `uo-link` sidecar release and a `servuo-plugins` overlay release. The installer does not
|
|
hardcode versions and does not resolve "latest" at run time; it fetches one of these documents and
|
|
installs what it names. **The bundle is the compat matrix.**
|
|
|
|
They are written by [`.gitea/workflows/bundle.yml`](../.gitea/workflows/bundle.yml), which composes
|
|
one whenever a component publishes a release (dispatched by that release's own workflow) and
|
|
nightly, so a missed dispatch self-heals. A run that finds nothing changed writes nothing.
|
|
|
|
See `docs/installer/PLAN.md` §7 for the design.
|
|
|
|
## Where they live: the `bundles` branch
|
|
|
|
**The JSON documents are not in this directory.** They are published to a branch of their own,
|
|
[`bundles`](https://gitea.whitlocktech.com/RunicGateway/installer/src/branch/bundles), at its root:
|
|
|
|
| File | What it is |
|
|
|---|---|
|
|
| `current.json` | The bundle the installer uses by default. Always a copy of the newest `bundle-*.json`. |
|
|
| `bundle-<tag>.json` | Every bundle ever published, kept forever so `--bundle <tag>` stays reproducible. |
|
|
|
|
Tags are UTC dates — `2026.08.04`. A second bundle on the same day (a sidecar release in the
|
|
morning, an overlay release in the afternoon) becomes `2026.08.04.2`, so one tag always names
|
|
exactly one matrix.
|
|
|
|
**Why a branch rather than `main`.** `main` is protected and this job is unattended: the pre-receive
|
|
hook declines a push from CI, which is not something a nightly cron can resolve. A branch of its own
|
|
keeps everything the original choice was for — a reviewable diff, a git history of the compat
|
|
matrix, plain anonymous raw URLs, no credentials on the shard host — and needs no protection
|
|
exception. Whitelisting a scheduled job for pushes to the default branch would buy nothing this does
|
|
not.
|
|
|
|
This directory keeps the documentation, because that is what belongs on `main`: the branch carries
|
|
data, and only data.
|
|
|
|
## How the installer fetches these
|
|
|
|
Plain anonymous `GET`s against a public repo. The shard host gets no git and no Gitea credentials
|
|
(`PLAN.md` §1), so nothing here may require auth:
|
|
|
|
```
|
|
https://gitea.whitlocktech.com/RunicGateway/installer/raw/branch/bundles/current.json
|
|
https://gitea.whitlocktech.com/RunicGateway/installer/raw/branch/bundles/bundle-2026.08.04.json
|
|
```
|
|
|
|
Bundles are committed rather than published as Gitea releases because this repo's *own* releases are
|
|
the installer binaries, and `/releases/latest` returns whichever release is newest regardless of
|
|
kind — interleaving the two would make "latest" intermittently resolve to a release containing no
|
|
installer binary.
|
|
|
|
## Schema
|
|
|
|
`schema` is the version of *this document's* shape, and is unrelated to `protocol` (the uo-link wire
|
|
protocol) or to either component's release version. All three move independently.
|
|
|
|
```jsonc
|
|
{
|
|
"schema": 1,
|
|
"bundle": "2026.08.04", // this bundle's tag; what --bundle takes
|
|
"generated": "2026-08-04T16:07:13Z",
|
|
"protocol": 3, // the wire protocol both halves speak (gate 1 proved it)
|
|
|
|
"link": {
|
|
"repo": "RunicGateway/link",
|
|
"tag": "v1.1.0",
|
|
"version": "1.1.0",
|
|
"protocol": 3,
|
|
"assets": { // per-platform: the installer runs on each
|
|
"linux-x86_64": { "name": "…", "url": "…", "sha256": "…" },
|
|
"linux-aarch64": { "name": "…", "url": "…", "sha256": "…" },
|
|
"windows-x86_64": { "name": "…", "url": "…", "sha256": "…" }
|
|
}
|
|
},
|
|
|
|
"overlay": {
|
|
"repo": "RunicGateway/servuo-plugins",
|
|
"tag": "v0.1.1",
|
|
"version": "0.1.1",
|
|
"commit": "3a52abb…", // recorded into install.json at deploy time
|
|
"protocol": 3,
|
|
"servuo": {
|
|
"min_version": "57.4", // base overlay: only adds files
|
|
"patches_verified_against": "57.4" // patch tier: skipped with a warning elsewhere
|
|
},
|
|
"asset": { "name": "runicgateway-overlay-0.1.1.tar.gz", "url": "…", "sha256": "…" }
|
|
}
|
|
}
|
|
```
|
|
|
|
### The two things worth knowing
|
|
|
|
**`sha256` is load-bearing, not decorative.** Every artifact Runic Gateway publishes is deliberately
|
|
**unsigned** (`PLAN.md` §3) — the checksum is the entire trust anchor. Each hash here was computed by
|
|
CI from the asset it actually downloaded, *after* verifying it against the `SHA256SUMS` the
|
|
publishing repo shipped beside it. The installer must verify every download against these values and
|
|
refuse on a mismatch. A bundle whose hashes are trusted but never checked buys nothing.
|
|
|
|
**`link.protocol` and `overlay.protocol` are always equal, and that is the point.** The sidecar
|
|
rejects a protocol mismatch with `409` rather than mis-parsing, so a mismatched pair is a shard
|
|
emitting into a void. CI refuses to publish one: it reads `PROTOCOL_VERSION` from the sidecar's
|
|
source at its release tag and the declared `protocol` from the overlay tarball's `manifest.json`, and
|
|
fails if they differ. The top-level `protocol` is that agreed value.
|