docs(installer): add release orchestration and the bundle manifest #81

Merged
whitlocktech merged 1 commits from docs/installer-release-orchestration into main 2026-08-01 21:42:38 +00:00
Member

What & why

Follow-up to #80, which merged while this was still being written — so it lands as its own PR.

The installer needs CI that reacts when a component publishes a release. This adds it as §7, folded into version tracking rather than bolted on separately, because the bundle is the compat matrix — which closes the "where does the compat matrix live" gap §7 originally left open.

  • §7.1 Bundle manifest — CI publishes an exact, protocol-checked combination of component versions. The installer resolves against it at run time; --bundle <tag> pins one for a reproducible install. Because the bundle is data, a link release regenerates ~20 lines of JSON and leaves the installer binary untouched: operators don't re-download the installer to pick up a sidecar patch, and the repo doesn't accumulate releases whose code is byte-identical. Two compose-time gates — sidecar PROTOCOL_VERSION must equal the overlay manifest's declared version, and every referenced asset's SHA256 must match.
  • §7.2 Triggers — each component's release job POSTs to the installer repo's workflow-dispatch endpoint (link/.gitea/workflows/release.yml already declares workflow_dispatch: {} and already holds a write:repository token), plus a nightly cron so a missed or failed dispatch self-heals rather than silently pinning operators to a stale sidecar. repository_dispatch deliberately avoided — support is uncertain on this Gitea version, while dispatching an existing workflow via the API works today.
  • §7.3 Stale overlay: dispatch, don't wait — each component self-releases on merge to its own main, so by the time the installer's CI looks the release normally already exists. Note "updated since" must mean releasable commits: the engine sets RELEASE=false when only docs:/chore: has landed, so a docs typo correctly cuts nothing. If main is genuinely ahead with releasable commits, fire that repo's release workflow, don't wait, compose from what exists now, and warn loudly in the job summary; the nightly folds in the result minutes later.

The reasoning on that last point, since it's the non-obvious one: dispatching another repo's workflow is fine — it still runs that repo's own gates, and it's all one org. Polling it is the problem. Gitea's dispatch endpoint returns no run handle, so the job would have to guess which run is its own by filtering recent runs, hold a runner idle while another repo builds, and invent timeout behavior. Fire-and-forget plus cron reconciliation gets the same automation with none of that.

Knock-on edits: bundle CI becomes a Phase 0 deliverable (Phase 1 resolves what to install from the bundle), runicgateway update now moves between checked combinations rather than two independently-latest artifacts, §3 notes the bundle is published by the installer repo rather than by any component, and the composition decision joins §1's locked table.

How it was tested

Docs-only. Claims about existing CI verified against link/.gitea/workflows/release.yml — specifically the workflow_dispatch: {} trigger, the REGISTRY_TOKEN write:repository scope, and the BUMP=none → RELEASE=false behavior the "releasable commits" rule depends on.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense.
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

This project requires disclosure of AI tool usage. Please pick one:

  • No AI tools were used to produce this contribution.
  • AI tools were used. Tool(s): Claude Code (Opus 5). I have reviewed and understand
    every change, and take responsibility for it. AI-authored commits are
    marked with a Co-Authored-By / Assisted-By trailer.

License

  • I agree that my contribution is licensed under this project's license
    (GNU GPL v3.0 or later), and I have the right to contribute it.
## What & why Follow-up to #80, which merged while this was still being written — so it lands as its own PR. The installer needs CI that reacts when a component publishes a release. This adds it as §7, folded into version tracking rather than bolted on separately, because **the bundle *is* the compat matrix** — which closes the "where does the compat matrix live" gap §7 originally left open. - **§7.1 Bundle manifest** — CI publishes an exact, protocol-checked combination of component versions. The installer resolves against it at run time; `--bundle <tag>` pins one for a reproducible install. Because the bundle is data, a `link` release regenerates ~20 lines of JSON and leaves the installer binary untouched: operators don't re-download the installer to pick up a sidecar patch, and the repo doesn't accumulate releases whose code is byte-identical. Two compose-time gates — sidecar `PROTOCOL_VERSION` must equal the overlay manifest's declared version, and every referenced asset's SHA256 must match. - **§7.2 Triggers** — each component's release job `POST`s to the installer repo's workflow-dispatch endpoint (`link/.gitea/workflows/release.yml` already declares `workflow_dispatch: {}` and already holds a `write:repository` token), plus a nightly cron so a missed or failed dispatch self-heals rather than silently pinning operators to a stale sidecar. `repository_dispatch` deliberately avoided — support is uncertain on this Gitea version, while dispatching an existing workflow via the API works today. - **§7.3 Stale overlay: dispatch, don't wait** — each component self-releases on merge to its own `main`, so by the time the installer's CI looks the release normally already exists. Note "updated since" must mean *releasable* commits: the engine sets `RELEASE=false` when only `docs:`/`chore:` has landed, so a docs typo correctly cuts nothing. If main is genuinely ahead with releasable commits, fire that repo's release workflow, **don't wait**, compose from what exists now, and warn loudly in the job summary; the nightly folds in the result minutes later. The reasoning on that last point, since it's the non-obvious one: dispatching another repo's workflow is fine — it still runs that repo's own gates, and it's all one org. *Polling* it is the problem. Gitea's dispatch endpoint returns no run handle, so the job would have to guess which run is its own by filtering recent runs, hold a runner idle while another repo builds, and invent timeout behavior. Fire-and-forget plus cron reconciliation gets the same automation with none of that. Knock-on edits: bundle CI becomes a Phase 0 deliverable (Phase 1 resolves what to install *from* the bundle), `runicgateway update` now moves between checked combinations rather than two independently-latest artifacts, §3 notes the bundle is published by the installer repo rather than by any component, and the composition decision joins §1's locked table. ## How it was tested Docs-only. Claims about existing CI verified against `link/.gitea/workflows/release.yml` — specifically the `workflow_dispatch: {}` trigger, the `REGISTRY_TOKEN` `write:repository` scope, and the `BUMP=none → RELEASE=false` behavior the "releasable commits" rule depends on. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. - [x] I have added or updated tests/docs where it makes sense. - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) This project **requires disclosure of AI tool usage**. Please pick one: - [ ] No AI tools were used to produce this contribution. - [x] AI tools were used. Tool(s): `Claude Code (Opus 5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` / `Assisted-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it.
wtclaude added 1 commit 2026-08-01 11:11:48 +00:00
The installer needs CI that reacts when a component publishes a release. Adds
that as section 7, folded into version tracking because the bundle IS the compat
matrix -- which closes the "where does the compat matrix live" gap section 7
previously left open.

- 7.1 Bundle manifest: CI publishes an exact, protocol-checked combination of
  component versions; the installer resolves against it at run time and
  --bundle <tag> pins one. A link release regenerates JSON and leaves the
  installer binary untouched, so operators don't re-download the installer for a
  sidecar patch and the repo doesn't accumulate releases with identical code.
  Two compose-time gates: sidecar PROTOCOL_VERSION must equal the overlay
  manifest's declared version, and every asset's SHA256 must match.
- 7.2 Triggers: each component's release job POSTs to the installer's
  workflow-dispatch endpoint (link's release.yml already declares
  workflow_dispatch and already holds a write:repository token), plus a nightly
  cron so a missed dispatch self-heals. repository_dispatch avoided -- support
  is uncertain on this Gitea version.
- 7.3 Stale overlay: dispatch, don't wait. Components self-release on merge to
  their own main, so the release normally already exists. If main is ahead with
  *releasable* commits (docs:/chore: correctly cut nothing), fire that repo's
  workflow, compose from what exists now, warn loudly, and let the nightly fold
  in the result. Dispatching another repo's workflow is fine -- it still runs
  its own gates -- but polling it is not, since Gitea's dispatch endpoint
  returns no run handle.

Bundle CI becomes a Phase 0 deliverable, since Phase 1 resolves what to install
from the bundle. `update` now moves between checked combinations rather than two
independently-latest artifacts.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit 8322e8318c into main 2026-08-01 21:42:38 +00:00
whitlocktech deleted branch docs/installer-release-orchestration 2026-08-01 21:42:39 +00:00
Sign in to join this conversation.
No description provided.