Skip to content

ci(construct): add PR-time publish-manifest-rehearsal to catch buildx env-var leaks#273

Merged
donbeave merged 1 commit into
mainfrom
ci/construct-publish-manifest-rehearsal
May 9, 2026
Merged

ci(construct): add PR-time publish-manifest-rehearsal to catch buildx env-var leaks#273
donbeave merged 1 commit into
mainfrom
ci/construct-publish-manifest-rehearsal

Conversation

@donbeave

@donbeave donbeave commented May 9, 2026

Copy link
Copy Markdown
Member

Summary

Tier 3 follow-up to the post-#266 hardening. Adds a PR-time publish-manifest-rehearsal job to construct.yml that runs the same docker buildx CLI plumbing publish-manifest depends on, without the side-effect steps (Docker Hub login, imagetools create, imagetools inspect). The rehearsal closes the gap that let #266 reach main: workflow-level env vars consumed by third-party CLIs (in #266's case BUILDX_BUILDER) were never exercised on PR because every job that calls docker buildx is gated to push-to-main.

How the rehearsal catches the #266 class of break

docker buildx ls evaluates BUILDX_BUILDER (and any other buildx-controlling env var) at startup and exits non-zero on a missing-builder reference. That's the exact failure shape publish-manifest emitted post-#256-merge:

ERROR: no builder "jackin-construct" found
error: Recipe `construct-publish-manifest` failed with exit code 1

Running the same call without registry credentials surfaces the same error before any network write. A second cheap step — docker buildx imagetools --help >/dev/null — confirms the buildx CLI plugin is bundled and loadable on the runner.

The rehearsal runs on the events publish-manifest itself skips: PR and feature-branch workflow_dispatch that touch construct paths. The construct-required aggregator now lists publish-manifest-rehearsal in needs: so its result is rolled up into the same single required-check name branch protection enforces.

What's deferred

Similar rehearsals for build-validator (CI), deploy (Docs), and publish-preview (Homebrew preview) are not included. None of those side-effect surfaces — release artifacts in GitHub-side storage, GitHub Pages publish, Homebrew tap PR-create — has the same network-free reproducibility shape buildx ls provides for docker. Adding rehearsals for them would either require synthesizing fake side effects (high false-confidence risk) or re-architecting the workflow trigger (preview.yml fires on workflow_run from CI completion on main, not PR). Reopen if a #266-class break surfaces in any of them.

Verify locally

Checkout

export TIRITH=0
mkdir -p "$HOME/Projects/jackin-project/test"
cd "$HOME/Projects/jackin-project/test"

if [ ! -d jackin/.git ]; then
  git clone https://github.com/jackin-project/jackin.git
fi

cd jackin
mise trust
git fetch -f origin ci/construct-publish-manifest-rehearsal:refs/remotes/origin/ci/construct-publish-manifest-rehearsal
git checkout -B ci/construct-publish-manifest-rehearsal refs/remotes/origin/ci/construct-publish-manifest-rehearsal

Static checks

yq . .github/workflows/construct.yml >/dev/null

Smoke (CI runner only — local docker may not bundle buildx)

docker buildx ls
docker buildx imagetools --help >/dev/null && echo ok

The CI run on this PR is the actual fidelity test — publish-manifest-rehearsal should run, report green, and be visible in the construct-required rollup. Push to main on merge will skip the rehearsal (because is_publish == 'true') and run the real publish-manifest, exactly as before this PR.

… env-var leaks

Tier 3 follow-up to #266. The previous
hardening rounds (#267 docs rules + #14/#15 terraform branch
protection) are process- and gate-side. This one is the
workflow-side companion: a PR-time rehearsal job that runs the same
docker buildx CLI plumbing `publish-manifest` depends on, without
the side-effect steps (Docker Hub login, `imagetools create`,
`imagetools inspect`).

The #266 break (`ERROR: no builder "jackin-construct" found`) was
caused by hoisting BUILDX_BUILDER into workflow-level env, which
docker buildx reads as the default-builder selection. The error
surfaces at `docker buildx` startup — before any network write —
so a network-free `docker buildx ls` invocation reproduces the
exact failure shape. The rehearsal runs on the events
publish-manifest itself skips: PR and feature-branch dispatch that
touch construct paths.

Two checks:
- `docker buildx ls` exercises BUILDX_BUILDER (and any future
  buildx-controlling env var) without writing to a registry. A
  workflow-level env leak fails this step loudly.
- `docker buildx imagetools --help` smoke-tests that the buildx
  CLI plugin is bundled and loadable on the runner. Cheap.

The `construct-required` aggregator now includes
publish-manifest-rehearsal in its `needs:` list so the rehearsal's
result is rolled up into the same single check name branch
protection requires.

Skipping similar rehearsals for `build-validator`, `deploy`, and
`publish-preview` for now: those side-effect surfaces (release
artifacts, GitHub Pages, public Homebrew tap) don't have the
same network-free reproducibility shape `buildx ls` provides for
the docker case. Revisit if a #266-class break surfaces in any of
them.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Claude <noreply@anthropic.com>
@donbeave donbeave merged commit d0d9962 into main May 9, 2026
19 checks passed
@donbeave donbeave deleted the ci/construct-publish-manifest-rehearsal branch May 9, 2026 10:59
donbeave added a commit that referenced this pull request May 18, 2026
… env-var leaks (#273)

Tier 3 follow-up to the post-#266 hardening. Add a PR-time `publish-manifest-rehearsal` job to `construct.yml` that runs the same docker buildx CLI plumbing `publish-manifest` depends on, without the side-effect steps (Docker Hub login, `imagetools create`, `imagetools inspect`).

The previous hardening rounds were process- (#267 docs rules) and gate-side (#14 / #15 terraform branch protection). This is the workflow-side companion. The #266 break (`ERROR: no builder "jackin-construct" found`) was caused by hoisting `BUILDX_BUILDER` into workflow-level env, which docker buildx reads as the default-builder selection — surfacing only post-merge on main because `publish-manifest` is push-only and never runs on a `pull_request` event. `docker buildx ls` evaluates `BUILDX_BUILDER` at startup and exits non-zero on a missing-builder reference, regardless of whether the rest of the command would have hit the network. Running it on PR + feature-branch dispatch reproduces the failure shape without registry credentials.

Two checks: `docker buildx ls` (catches workflow-level env-var leaks for any current or future buildx-controlling env var) and `docker buildx imagetools --help >/dev/null` (smoke-test that the buildx CLI plugin is bundled and loadable on the runner). The `construct-required` aggregator now lists `publish-manifest-rehearsal` in `needs:` so the rehearsal's result is rolled up into the same single check name branch protection requires.

Equivalent rehearsals for `build-validator`, `deploy`, and `publish-preview` are deferred — those side-effect surfaces don't have the same network-free reproducibility shape `buildx ls` provides for docker, so the fidelity-vs-cost trade is worse. Reopen if a #266-class break surfaces in any of them.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant