From f8d80c07dba02a64f5b26da6956c529e636e59f8 Mon Sep 17 00:00:00 2001 From: wtclaude Date: Tue, 4 Aug 2026 11:13:51 -0500 Subject: [PATCH] ci(release): recompose the installer bundle after publishing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 sidecar 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. The bundle job re-reads PROTOCOL_VERSION from sidecar/src/main.rs at the new release tag and checks it against the overlay's declared protocol before publishing anything (gate 1), so a bump that lands without its plugin half is caught at compose time instead of on an operator's shard. 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 --- .gitea/workflows/release.yml | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index b59d401..5e72aae 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -28,6 +28,11 @@ # write:package; THIS workflow additionally needs # `write:repository` so it can push the bump commit + tag # and create the release. Grant that scope to the token. +# The final step also dispatches RunicGateway/installer's +# bundle workflow, so the token ideally has write there too +# — but that is a nicety, not a requirement: without it the +# step warns and the installer's nightly cron picks the +# release up instead. # Also: `main` must accept a direct push from that user (disable branch # protection for it, or add it as an exception) — the bump commit lands on main. # @@ -51,6 +56,9 @@ env: BIN: uo-link-sidecar LINUX_TARGET: x86_64-unknown-linux-gnu WINDOWS_TARGET: x86_64-pc-windows-gnu + # Notified after a release so the installer's compat matrix picks up this + # version immediately rather than at its next nightly run (PLAN.md §7.2). + INSTALLER_REPO: RunicGateway/installer jobs: release: @@ -256,3 +264,45 @@ jobs: -F "attachment=@dist/${f}" >/dev/null echo " uploaded ${f}" done + + # ── Recompose the installer's bundle manifest ──────────────────────── + # The installer does not resolve "latest" at run time — it installs the + # exact combination named by a published bundle (docs/installer/PLAN.md + # §7.1). So a sidecar release that nobody recomposes around is a release + # no operator will ever be offered. This step tells the installer repo to + # rebuild that manifest now, instead of leaving the new version invisible + # until its nightly cron. + # + # DISPATCH, DON'T WAIT (PLAN.md §7.3). Gitea's workflow-dispatch endpoint + # returns no run handle, so there is nothing to poll: a waiting step would + # have to guess which run is its own and hold a runner idle to do it. The + # bundle job runs its own gates regardless of who started it. + # + # A failure here is a WARNING, never a failure of this job. The release is + # already published and correct by this point; failing the run would + # misreport that. The installer's nightly cron recomposes from whatever + # the latest releases actually are, so a dropped dispatch self-heals — it + # costs latency, not correctness. + # + # `repository_dispatch` is deliberately not used: support for it is + # uncertain on this Gitea version, while dispatching an existing + # workflow_dispatch workflow via the API works today. + - name: Ask the installer repo to recompose its bundle + if: ${{ steps.plan.outputs.release == 'true' }} + env: + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: | + set -euo pipefail + CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')" + HTTP="$(curl -s -o /dev/null -w '%{http_code}' -X POST \ + -H "Authorization: token ${CI_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"ref":"main"}' \ + "https://${GITEA_HOST}/api/v1/repos/${INSTALLER_REPO}/actions/workflows/bundle.yml/dispatches" || echo 000)" + case "$HTTP" in + 20*) echo "Dispatched ${INSTALLER_REPO} bundle.yml (HTTP ${HTTP}) — not waiting for it." ;; + 403|404) + echo "::warning::Could not dispatch ${INSTALLER_REPO} bundle.yml (HTTP ${HTTP}). REGISTRY_TOKEN likely lacks write:repository on that repo. Release ${{ steps.plan.outputs.tag }} is published and fine; its bundle will be composed by the installer's nightly cron instead." ;; + *) + echo "::warning::Dispatching ${INSTALLER_REPO} bundle.yml returned HTTP ${HTTP}. Release ${{ steps.plan.outputs.tag }} is published and fine; the nightly cron will recompose the bundle." ;; + esac -- 2.49.1