From 2e45e0db66ed99c84be36f5d6e27b48a883e649e Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 8 Aug 2026 12:57:29 -0700 Subject: [PATCH 1/2] ci: verify what we publish from the consumer side Nothing in this repo has ever checked the artifact a user downloads. ci.yml proves the code builds and its tests pass; release.yml's verify-assets proves the upload step believed itself, asserted from inside the run that did the uploading. Neither is evidence about the published thing. That gap shipped twice in this fleet. webrequest-hook v1.0.4 published as a zero-asset phantom: tag, Release object, nothing to download. headroom-hook's published Docker bundle could not boot the gateway, because its shipped docker/bundle/config.yaml still used config shapes busbar 1.5.3 retired, so the container exits 1 during config load and never binds a port. Both were green everywhere. Adds consumer-verify.yml, which calls the fleet's shared GetBusbar/busbar/.github/workflows/plugin-consumer-verify.yml. It checks out nothing, so a fix that is committed but never published still fails it. It asserts the Release is published rather than a still-draft, that every platform archive it owes is present and downloadable THROUGH /releases/latest/download/ (a count can never see a missing platform, only a name can), that one archive really is a plugin busbar would accept - manifest name/alias/kind/version, the sha256 binding the cdylib beside it, a real shared library, a non-empty signature - and, where a runnable bundle is published, that the image BOOTS AND SERVES. The same job is appended to release.yml as its final step, so a broken publish turns the RELEASE red rather than leaving a green release beside a red run nobody correlates. On failure the shared workflow opens or updates ONE labelled issue naming the failing check, its expected and observed values and the run URL, and closes it again when a run passes. The inputs are read off the PUBLISHED manifest rather than guessed from the crate name: this fleet genuinely disagrees with itself about naming (the store repos drop the trailing -plugin the auth repos keep, and store-valkey publishes as busbar-store-redis). --- .github/workflows/consumer-verify.yml | 55 +++++++++++++++++++++++++++ .github/workflows/release.yml | 37 ++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 .github/workflows/consumer-verify.yml diff --git a/.github/workflows/consumer-verify.yml b/.github/workflows/consumer-verify.yml new file mode 100644 index 0000000..f490bae --- /dev/null +++ b/.github/workflows/consumer-verify.yml @@ -0,0 +1,55 @@ +name: consumer-verify + +# Does what this repo PUBLISHED actually work when a user gets it? +# +# Every other workflow here reports on itself. ci.yml proves the code builds and its tests pass. +# release.yml's verify-assets proves the upload step believed it succeeded, asserted from inside the +# run that did the uploading. None of that is evidence about the artifact a user downloads, and the +# gap is not theoretical: webrequest-hook v1.0.4 published as a zero-asset phantom, and +# headroom-hook's published bundle could not boot the gateway because its shipped config used shapes +# busbar 1.5.3 retired. Both were green everywhere. Nothing anywhere noticed. +# +# The logic lives in ONE place for the whole fleet, exactly like plugin-ci.yml, so a fix reaches +# every plugin at once instead of being copied ten times and drifting nine. +# +# WHY BOTH TRIGGERS. release: published catches a broken publish immediately, and it fires whether or +# not the release workflow itself finished happy - which matters, because a verifier that only runs +# when everything already worked is not a verifier. The daily schedule catches ROT: an artifact that +# published fine can stop working later when nothing about it changed (a bundle that no longer boots +# against a newer engine, an asset deleted by hand, a release un-flagged as latest). A +# publish-time-only check structurally cannot see that class. +on: + release: + types: [published] + schedule: + - cron: " * * *" + workflow_dispatch: + inputs: + version: + description: "Version to verify (e.g. 1.0.4). Empty means the newest published release." + required: false + type: string + +permissions: + contents: read + issues: write + actions: read + +jobs: + consumer: + uses: GetBusbar/busbar/.github/workflows/plugin-consumer-verify.yml@dev + with: + version: ${{ inputs.version || '' }} + # Read off the PUBLISHED artifact, not guessed from the crate name: the filename prefix and the + # manifest name genuinely differ across this fleet (the store repos drop the trailing -plugin + # that the auth repos keep, and store-valkey publishes as busbar-store-redis). + asset_prefix: busbar-store-postgres + plugin_name: busbar-store-postgres-plugin + plugin_alias: postgres + plugin_kind: store + # This repo publishes a runnable bundle, so the check does not stop at 'the tarball exists': + # the image is pulled fresh and the container must BOOT and answer /healthz. That is the + # assertion that was missing when the published bundle shipped a config busbar had retired. + bundle_image: 43 9 + bundle_env: "" + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a016719..cc402da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -249,3 +249,40 @@ jobs: -f event_type=upstream-release \ -f "client_payload[repo]=${GITHUB_REPOSITORY}" \ -f "client_payload[tag]=${GITHUB_REF_NAME}" + + # THE LAST STEP: does what we just published work for a user? + # + # Everything above verifies what THIS RUN produced, from inside this run. verify-assets asserts the + # assets it uploaded are present, which proves the run believed itself. It cannot prove the tarball + # downloads for a stranger, unpacks into a plugin busbar will load, or (where a bundle is + # published) boots. Those are the failures that shipped: a zero-asset phantom release, and a + # published bundle that exits 1 on startup. + # + # Consumer verification is POST-PUBLICATION by nature - you cannot download an asset that was never + # uploaded - so this cannot block the publish and does not pretend to. What it does is make the + # verdict impossible to miss: a failure is THIS release run's failure, and the shared workflow also + # opens or updates one labelled issue naming the failing check, expected, observed and the run URL. + # + # !cancelled() because a needs: on a FAILED job skips its dependent by default - the exact shape + # that skips a release's own guard precisely when the release is broken. + consumer-verification: + name: consumer verification (LAST STEP) + needs: [verify-assets] + if: ${{ !cancelled() }} + uses: GetBusbar/busbar/.github/workflows/plugin-consumer-verify.yml@dev + with: + version: ${{ github.ref_name }} + asset_prefix: busbar-store-postgres + plugin_name: busbar-store-postgres-plugin + plugin_alias: postgres + plugin_kind: store + # This repo publishes a runnable bundle, so the check does not stop at 'the tarball exists': + # the image is pulled fresh and the container must BOOT and answer /healthz. That is the + # assertion that was missing when the published bundle shipped a config busbar had retired. + bundle_image: 43 9 + bundle_env: "" + permissions: + contents: read + issues: write + actions: read + secrets: inherit From 5ea1ff407d49ff82359621c8f559d1b85c865d72 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 8 Aug 2026 13:16:17 -0700 Subject: [PATCH 2/2] ci: say which busbar branch we mean, instead of '/merge' On a pull_request event `github.ref_name` is '/merge', not a branch name, so plugin-ci asked GetBusbar/busbar for a branch called '5/merge' and the sibling checkout died with 'The process /usr/bin/git failed with exit code 1', naming neither the ref nor the reason. Every pull request to this repo has been red on that before running a line of the change it was testing. `github.base_ref` is the branch the PR targets and is empty on a push, so this means dev-against-dev and main-against-main as intended on both events. plugin-ci.yml also now falls back with a loud warning when a ref cannot be resolved, so this is belt and braces - but a caller should say what it means rather than lean on a fallback. --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 138fa55..770cd30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,10 @@ jobs: plugin_kind: store plugin_alias: postgres service: postgres - busbar_ref: ${{ github.ref_name }} + busbar_ref: ${{ github.base_ref || github.ref_name }} + # base_ref FIRST: on a pull_request `github.ref_name` is '/merge', not a branch + # name, so this asked busbar for a branch called '5/merge' and the sibling checkout died + # with an unreadable git error on EVERY pull request to this repo. # NOTE: no extra_sibling_test_command here (unlike before this repo's 2-crate-workspace # restructure). `extra_sibling_test_command: cargo test -p busbar-store-postgres` used to # exercise the monorepo's own copy of the store logic against the sibling busbarAI checkout's