From f4ce516dbef40e86f97f58b1ef973e26e2da9f8e Mon Sep 17 00:00:00 2001 From: coder 0 Date: Mon, 24 Aug 2026 12:53:35 -0400 Subject: [PATCH 1/4] Add staging dev relay image workflow Co-authored-by: Brad Seiler Signed-off-by: Brad Seiler --- .github/workflows/staging-dev-relay-image.yml | 294 ++++++++++++++++++ docs/staging-dev-relay-images.md | 22 ++ 2 files changed, 316 insertions(+) create mode 100644 .github/workflows/staging-dev-relay-image.yml create mode 100644 docs/staging-dev-relay-images.md diff --git a/.github/workflows/staging-dev-relay-image.yml b/.github/workflows/staging-dev-relay-image.yml new file mode 100644 index 00000000000..3023acdd7c1 --- /dev/null +++ b/.github/workflows/staging-dev-relay-image.yml @@ -0,0 +1,294 @@ +name: Staging dev relay image + +# Publishes pre-merge relay runtime images for bb-block staging only. +# +# Operators run this workflow from the default branch and provide a target ref in +# this repository. GitHub's workflow_dispatch permission model limits triggering +# to collaborators who can run repository workflows; no actor allowlist is kept +# here. The target ref is resolved to an immutable commit SHA before checkout, +# and the published tag is derived from that full SHA. + +on: + workflow_dispatch: + inputs: + target_ref: + description: "Branch, tag, refs/heads/*, or refs/tags/* in block/buzz to publish for bb-block staging" + required: true + type: string + +concurrency: + group: staging-dev-relay-image-${{ github.workflow }}-${{ github.run_id }} + cancel-in-progress: false + +permissions: {} + +env: + IMAGE_NAME: ghcr.io/block/buzz-staging-dev + ECR_REPOSITORY: 929862310821.dkr.ecr.us-west-2.amazonaws.com/ghcr.io/block/buzz-staging-dev + +jobs: + resolve: + name: Resolve target ref + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: read + outputs: + target_sha: ${{ steps.resolve.outputs.target_sha }} + image_tag: ${{ steps.resolve.outputs.image_tag }} + steps: + - name: Require reviewed workflow from main in canonical repository + env: + DISPATCH_REF: ${{ github.ref }} + REPOSITORY: ${{ github.repository }} + run: | + set -euo pipefail + if [ "$REPOSITORY" != "block/buzz" ]; then + echo "::error::Staging dev relay image publication is restricted to block/buzz" + exit 1 + fi + + if [ "$DISPATCH_REF" != "refs/heads/main" ]; then + echo "::error::Dispatch this workflow from main, not $DISPATCH_REF" + exit 1 + fi + + - name: Resolve target ref in this repository + id: resolve + env: + TARGET_REF_INPUT: ${{ inputs.target_ref }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + set -euo pipefail + + target_ref=$(printf '%s' "$TARGET_REF_INPUT" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + + if [ -z "$target_ref" ]; then + echo "target_ref must not be empty" >&2 + exit 1 + fi + + if [ ${#target_ref} -gt 255 ]; then + echo "target_ref is too long" >&2 + exit 1 + fi + + if [[ ! "$target_ref" =~ ^[A-Za-z0-9._/@-]+$ ]]; then + echo "target_ref contains unsupported characters; use a branch, tag, or full refs/heads/* or refs/tags/* name" >&2 + exit 1 + fi + + case "$target_ref" in + -*|*..*|*.lock|refs/pull/*|pull/*|*/pull/*) + echo "target_ref is not an allowed repository branch/tag/ref" >&2 + exit 1 + ;; + esac + + workdir=$(mktemp -d) + trap 'rm -rf "$workdir"' EXIT + git -C "$workdir" init --quiet + git -C "$workdir" remote add origin "https://github.com/${GITHUB_REPOSITORY}.git" + + fetch_commit() { + local ref=$1 + git -C "$workdir" fetch --no-tags --depth=1 origin "$ref" >/dev/null 2>&1 + git -C "$workdir" rev-parse --verify "FETCH_HEAD^{commit}" + } + + target_sha="" + if [[ "$target_ref" == refs/heads/* || "$target_ref" == refs/tags/* ]]; then + target_sha=$(fetch_commit "$target_ref") || { + echo "target_ref did not resolve in ${GITHUB_REPOSITORY}: $target_ref" >&2 + exit 1 + } + else + if target_sha=$(fetch_commit "refs/heads/${target_ref}"); then + : + elif target_sha=$(fetch_commit "refs/tags/${target_ref}"); then + : + else + echo "target_ref did not resolve as a branch or tag in ${GITHUB_REPOSITORY}: $target_ref" >&2 + exit 1 + fi + fi + + target_sha=$(printf '%s' "$target_sha" | tr '[:upper:]' '[:lower:]') + if [[ ! "$target_sha" =~ ^[0-9a-f]{40}$ ]]; then + echo "resolved target SHA is invalid: $target_sha" >&2 + exit 1 + fi + + image_tag="dev-sha-${target_sha}" + { + echo "target_sha=${target_sha}" + echo "image_tag=${image_tag}" + } >> "$GITHUB_OUTPUT" + + printf "Resolved \`%s\` to \`%s\`; image tag \`%s\`.\n" "$target_ref" "$target_sha" "$image_tag" >> "$GITHUB_STEP_SUMMARY" + + build: + name: Build staging relay runtime (${{ matrix.platform }}) + runs-on: ${{ matrix.runner }} + needs: resolve + timeout-minutes: 60 + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-24.04 + arch: amd64 + - platform: linux/arm64 + runner: ubuntu-24.04-arm + arch: arm64 + steps: + - name: Checkout resolved target + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: ${{ needs.resolve.outputs.target_sha }} + fetch-depth: 1 + persist-credentials: false + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + with: + buildkitd-config-inline: | + [worker.oci] + max-parallelism = 2 + + - name: Log in to GHCR + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 + with: + images: ${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.title=Buzz staging dev relay + org.opencontainers.image.description=Pre-merge Buzz relay runtime image for bb-block staging only + org.opencontainers.image.licenses=Apache-2.0 + org.opencontainers.image.revision=${{ needs.resolve.outputs.target_sha }} + + - name: Build and push runtime image by digest + id: build + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + with: + context: . + file: ./Dockerfile + target: runtime + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true + cache-from: | + type=registry,ref=${{ env.IMAGE_NAME }}-buildcache:${{ matrix.arch }} + cache-to: | + type=registry,ref=${{ env.IMAGE_NAME }}-buildcache:${{ matrix.arch }},mode=max,compression=zstd + + - name: Export digest + env: + DIGEST: ${{ steps.build.outputs.digest }} + run: | + set -euo pipefail + mkdir -p /tmp/digests-release + touch "/tmp/digests-release/${DIGEST#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: staging-dev-relay-digests-${{ matrix.arch }} + path: /tmp/digests-release/* + if-no-files-found: error + retention-days: 1 + + merge: + name: Publish staging relay runtime manifest + runs-on: ubuntu-24.04 + needs: + - resolve + - build + timeout-minutes: 15 + permissions: + contents: read + packages: write + id-token: write + attestations: write + steps: + - name: Download per-arch digests + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: /tmp/digests + pattern: staging-dev-relay-digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + + - name: Log in to GHCR + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push manifest list + id: manifest + working-directory: /tmp/digests + env: + IMAGE_NAME: ${{ env.IMAGE_NAME }} + IMAGE_TAG: ${{ needs.resolve.outputs.image_tag }} + run: | + set -euo pipefail + digests=() + for digest in *; do + digests+=("${IMAGE_NAME}@sha256:${digest}") + done + + docker buildx imagetools create -t "${IMAGE_NAME}:${IMAGE_TAG}" "${digests[@]}" + merged_digest=$(docker buildx imagetools inspect "${IMAGE_NAME}:${IMAGE_TAG}" \ + --format '{{json .Manifest}}' | jq -r '.digest') + echo "digest=${merged_digest}" >> "$GITHUB_OUTPUT" + + - name: Attest provenance for the merged image + uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 + with: + subject-name: ${{ env.IMAGE_NAME }} + subject-digest: ${{ steps.manifest.outputs.digest }} + push-to-registry: true + + - name: Deployment summary + env: + IMAGE_NAME: ${{ env.IMAGE_NAME }} + ECR_REPOSITORY: ${{ env.ECR_REPOSITORY }} + IMAGE_TAG: ${{ needs.resolve.outputs.image_tag }} + TARGET_SHA: ${{ needs.resolve.outputs.target_sha }} + MERGED_DIGEST: ${{ steps.manifest.outputs.digest }} + run: | + { + echo "### Published bb-block staging dev relay image" + echo + echo "**Source commit:** \`${TARGET_SHA}\`" + echo "**GHCR image:** \`${IMAGE_NAME}:${IMAGE_TAG}\`" + echo "**Manifest digest:** \`${MERGED_DIGEST}\`" + echo + echo "Set bb-block staging BPCI values to:" + echo '```yaml' + echo "buzz:" + echo " image:" + echo " repository: ${ECR_REPOSITORY}" + echo " tag: ${IMAGE_TAG}" + echo '```' + echo + echo "Verify provenance:" + echo '```' + echo "gh attestation verify oci://${IMAGE_NAME}@${MERGED_DIGEST} --owner block" + echo '```' + } >> "$GITHUB_STEP_SUMMARY" diff --git a/docs/staging-dev-relay-images.md b/docs/staging-dev-relay-images.md new file mode 100644 index 00000000000..d8c3cb0122a --- /dev/null +++ b/docs/staging-dev-relay-images.md @@ -0,0 +1,22 @@ +# Staging dev relay images + +Use the **Staging dev relay image** GitHub Actions workflow to publish a pre-merge Buzz relay runtime image for bb-block staging. + +1. Run `.github/workflows/staging-dev-relay-image.yml` from the default branch. +2. Enter a `target_ref` from `block/buzz` (`my-branch`, `refs/heads/my-branch`, `my-tag`, or `refs/tags/my-tag`). +3. Wait for the workflow summary. It resolves that ref to a commit and publishes only the relay `runtime` image to: + + ```text + ghcr.io/block/buzz-staging-dev:dev-sha-<40-character-commit-sha> + ``` + +4. In `squareup/builderbot-platform-core-infrastructure`, set bb-block staging values to the distinct pull-through ECR path and immutable tag from the workflow summary: + + ```yaml + buzz: + image: + repository: 929862310821.dkr.ecr.us-west-2.amazonaws.com/ghcr.io/block/buzz-staging-dev + tag: dev-sha-<40-character-commit-sha> + ``` + +This path is intentionally separate from the production/main relay image path (`ghcr.io/block/buzz`) so a staging-only branch deployment is obvious in BPCI. From 388364b59da6185d99b0a1a9d19fd77a65fa5b0e Mon Sep 17 00:00:00 2001 From: Duncan Date: Tue, 25 Aug 2026 10:25:13 -0400 Subject: [PATCH 2/4] fix(ci): correct staging dev relay image provenance and tag uniqueness The stock attest-build-provenance predicate signs the workflow's OIDC main sha/ref, not the separately checked-out target_sha, so the attestation named the wrong source commit. Remove the attestation step and its id-token/attestations permissions rather than ship a false provenance claim. The reused dev-sha-${target_sha} tag is unsafe against the utility ECR pull-through cache, which uses immutable tags: rebuilds of the same SHA aren't byte-identical (mutable base tags + apt-get update), so a second dispatch could wedge or skew the mirror. Make every publication unique with the run id/attempt suffix; the per-run_id concurrency block then guards nothing and is removed. Co-authored-by: Will Pfleger Signed-off-by: Will Pfleger --- .github/workflows/staging-dev-relay-image.yml | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/.github/workflows/staging-dev-relay-image.yml b/.github/workflows/staging-dev-relay-image.yml index 3023acdd7c1..c8a746a0b94 100644 --- a/.github/workflows/staging-dev-relay-image.yml +++ b/.github/workflows/staging-dev-relay-image.yml @@ -16,10 +16,6 @@ on: required: true type: string -concurrency: - group: staging-dev-relay-image-${{ github.workflow }}-${{ github.run_id }} - cancel-in-progress: false - permissions: {} env: @@ -119,7 +115,7 @@ jobs: exit 1 fi - image_tag="dev-sha-${target_sha}" + image_tag="dev-sha-${target_sha}-run-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" { echo "target_sha=${target_sha}" echo "image_tag=${image_tag}" @@ -219,8 +215,6 @@ jobs: permissions: contents: read packages: write - id-token: write - attestations: write steps: - name: Download per-arch digests uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -257,13 +251,6 @@ jobs: --format '{{json .Manifest}}' | jq -r '.digest') echo "digest=${merged_digest}" >> "$GITHUB_OUTPUT" - - name: Attest provenance for the merged image - uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 - with: - subject-name: ${{ env.IMAGE_NAME }} - subject-digest: ${{ steps.manifest.outputs.digest }} - push-to-registry: true - - name: Deployment summary env: IMAGE_NAME: ${{ env.IMAGE_NAME }} @@ -286,9 +273,4 @@ jobs: echo " repository: ${ECR_REPOSITORY}" echo " tag: ${IMAGE_TAG}" echo '```' - echo - echo "Verify provenance:" - echo '```' - echo "gh attestation verify oci://${IMAGE_NAME}@${MERGED_DIGEST} --owner block" - echo '```' } >> "$GITHUB_STEP_SUMMARY" From acbd5b937e1c2fa1f93ae236d0c707368c48f8c6 Mon Sep 17 00:00:00 2001 From: tornquist Date: Tue, 25 Aug 2026 14:41:00 +0000 Subject: [PATCH 3/4] docs: correct staging dev image tag Signed-off-by: tornquist --- docs/staging-dev-relay-images.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/staging-dev-relay-images.md b/docs/staging-dev-relay-images.md index d8c3cb0122a..833e53fe8d9 100644 --- a/docs/staging-dev-relay-images.md +++ b/docs/staging-dev-relay-images.md @@ -7,7 +7,7 @@ Use the **Staging dev relay image** GitHub Actions workflow to publish a pre-mer 3. Wait for the workflow summary. It resolves that ref to a commit and publishes only the relay `runtime` image to: ```text - ghcr.io/block/buzz-staging-dev:dev-sha-<40-character-commit-sha> + ghcr.io/block/buzz-staging-dev:dev-sha-<40-character-commit-sha>-run-- ``` 4. In `squareup/builderbot-platform-core-infrastructure`, set bb-block staging values to the distinct pull-through ECR path and immutable tag from the workflow summary: @@ -16,7 +16,7 @@ Use the **Staging dev relay image** GitHub Actions workflow to publish a pre-mer buzz: image: repository: 929862310821.dkr.ecr.us-west-2.amazonaws.com/ghcr.io/block/buzz-staging-dev - tag: dev-sha-<40-character-commit-sha> + tag: dev-sha-<40-character-commit-sha>-run-- ``` This path is intentionally separate from the production/main relay image path (`ghcr.io/block/buzz`) so a staging-only branch deployment is obvious in BPCI. From 15f496c6352bd4046845461b2365ad95d0cb0249 Mon Sep 17 00:00:00 2001 From: tornquist Date: Tue, 25 Aug 2026 14:42:00 +0000 Subject: [PATCH 4/4] docs: define staging dev image limits Signed-off-by: tornquist --- docs/staging-dev-relay-images.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/staging-dev-relay-images.md b/docs/staging-dev-relay-images.md index 833e53fe8d9..1fdfdd7e026 100644 --- a/docs/staging-dev-relay-images.md +++ b/docs/staging-dev-relay-images.md @@ -20,3 +20,7 @@ Use the **Staging dev relay image** GitHub Actions workflow to publish a pre-mer ``` This path is intentionally separate from the production/main relay image path (`ghcr.io/block/buzz`) so a staging-only branch deployment is obvious in BPCI. + +These images are for manual, pre-merge staging evaluation only. They are not release-qualified and must not be promoted to production or used by the canonical Kargo promotion path. + +While bb-block remains a shared staging environment, do not deploy a target ref through this runtime-only path if it changes `migrations/` or requires changes to `deploy/charts/buzz/`. Backwards-compatible migrations can still leave the shared database ahead of the restored `main` image. Use an isolated environment or deployment-time enforcement for those changes.