ci(docker): skip relay image publishes for main pushes outside the build context (#9310)

## Summary

Every push to `main` published a new relay and push gateway image, even
when it only changed `desktop/`, `mobile/`, `docs/` or other paths
`.dockerignore` keeps out of the Docker build context. Deployments that
track the sortable `main-<commit UTC>-<7>` tag then rolled their relays
onto an image built from identical inputs.

The `push` trigger now skips the directories excluded from the build
context (`desktop/`, `mobile/`, `docs/`, `bin/`, `sidecars/`,
`.github/`) and re-includes `.github/workflows/docker.yml`, which
defines the build. Tag pushes (`relay-v*`) still always publish, because
GitHub does not evaluate path filters for tag pushes. Pull request
builds keep their existing allowlist.

## Guard

`scripts/test-relay-image-eligibility-workflow.sh`, which `ci.yml` runs,
now requires the push filter to be exactly, in order:
1. `"**"`,
2. only `"!<dir>/**"` exclusions, each also listed in `.dockerignore`,
3. the `.github/workflows/docker.yml` re-inclusion.

Comment and blank lines inside the list are skipped rather than ending
it. So nobody can add a file exclusion, exclude a directory Docker sees,
or reorder the re-inclusion in a way that hides a change to the image.

## Testing

- `scripts/test-relay-image-eligibility-workflow.sh` passes.
- The guard fails with a specific message for each of these:
`"!crates/**"` added, `"!Cargo.lock"` added, single-quoted
`'!Cargo.lock'`, the re-inclusion moved before `"!.github/**"`, `"**"`
removed, and `"!crates/**"` appended after a comment or a blank line.
- `actionlint` passes on `docker.yml`.

## Simpler

The image is published only when its inputs can change. This removes
no-op staging rollouts, plus the cancelled or queued Docker runs that
each merge to main caused. No new concepts: the filter reuses
`.dockerignore` as the only definition of what the image sees.

Identified follow-up: root-level Markdown (for example `README.md`) is
in the build context, so root-docs-only merges still publish. Skipping
them would need a `.dockerignore` entry and a file-level filter rule,
which the guard currently rejects on purpose.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Luke Tornquist <tornquist@squareup.com>
Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Luke Tornquist
2026-11-09 19:41:13 -05:00
committed by GitHub
parent 3e9d2e90ce
commit e8a4bc1591
3 changed files with 49 additions and 3 deletions

View File

@@ -30,6 +30,32 @@ require_literal '- "scripts/create-deployment-eligibility-predicate.jq"'
require_literal '- "scripts/select-qualified-ci-run.jq"'
require_literal '- "scripts/test-relay-image-eligibility-workflow.sh"'
# Main pushes may skip only paths Docker never sees. The push filter must be, in
# order: "**", then directory exclusions that .dockerignore also excludes, then
# the re-included image workflow. Any other entry, or another order, could stop
# a change that alters the published image from publishing it.
push_trigger=$(awk '/^ push:$/{f=1;next} f&&/^ [a-z_]+:/{exit} f' "$workflow")
push_paths=()
while IFS= read -r entry; do
push_paths+=("$entry")
done < <(awk '/^ paths:$/{f=1;next} f&&/^[[:space:]]*(#.*)?$/{next} f&&/^ - /{print;next} f{exit}' <<<"$push_trigger")
fail_push_paths() {
echo "main push path filter: $1" >&2
exit 1
}
(( ${#push_paths[@]} >= 3 )) || fail_push_paths "expected \"**\", directory exclusions, then the workflow"
[[ "${push_paths[0]}" == ' - "**"' ]] || fail_push_paths 'first entry must be "**"'
last=$(( ${#push_paths[@]} - 1 ))
[[ "${push_paths[$last]}" == ' - ".github/workflows/docker.yml"' ]] ||
fail_push_paths "last entry must re-include .github/workflows/docker.yml"
for entry in "${push_paths[@]:1:last-1}"; do
[[ "$entry" =~ ^\ {6}-\ \"!([A-Za-z0-9._-]+)/\*\*\"$ ]] ||
fail_push_paths "only \"!<dir>/**\" exclusions may sit between \"**\" and the workflow, found: ${entry# - }"
excluded=${BASH_REMATCH[1]}
grep -Fxq "$excluded/" "$repo_root/.dockerignore" ||
fail_push_paths "skips $excluded/, which .dockerignore keeps in the image build context"
done
if grep -Fq "buzz-staging-dev" "$workflow"; then
echo "canonical relay image workflow references the preview-only image package" >&2
exit 1