fix(flink): route dead-letter via yield (PyFlink has no ctx.output) — R4 follow-up #157
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Container Attestation | |
| "on": | |
| # No `paths:` filter on pull_request: build-smoke is a required | |
| # branch-protection check, so the job must COMPLETE on every PR. | |
| # A paths filter would leave docker-free PRs stuck forever on | |
| # "Expected - waiting for status". Path-gating happens inside the | |
| # job via the `changes` step instead. | |
| pull_request: | |
| workflow_dispatch: | |
| inputs: | |
| mode: | |
| description: Build a fresh GHCR image or sign an existing digest | |
| required: true | |
| default: build-and-sign | |
| type: choice | |
| options: | |
| - build-and-sign | |
| - sign-existing-digest | |
| image_ref: | |
| description: Container image repository/name without a digest | |
| required: false | |
| type: string | |
| image_digest: | |
| description: Immutable image digest, for example sha256:... | |
| required: false | |
| type: string | |
| confirm: | |
| description: Type SIGN to build/sign or sign an existing digest | |
| required: true | |
| type: string | |
| # Top level stays read-only; the write scopes (packages/id-token/attestations) | |
| # are granted only to the two operator-dispatched signing jobs below, so the | |
| # every-PR build-smoke job runs with a read-only token. | |
| permissions: | |
| contents: read | |
| env: | |
| IMAGE_REF: ghcr.io/${{ github.repository_owner }}/agentflow-api | |
| jobs: | |
| build-smoke: | |
| name: build-smoke | |
| # Runs on EVERY PR (required check). The `changes` step inspects the | |
| # diff against the PR base: when the Dockerfile / pip surface or this | |
| # workflow changed, the image is built without pushing or signing so a | |
| # broken Dockerfile fails the PR instead of landing silently; otherwise | |
| # the job completes immediately as a skip-success so the required check | |
| # never blocks docker-free PRs. | |
| if: ${{ github.event_name == 'pull_request' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect container-relevant changes | |
| id: changes | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| base="${{ github.event.pull_request.base.sha }}" | |
| changed="$(git diff --name-only "${base}...HEAD")" | |
| printf 'Changed files:\n%s\n' "${changed}" | |
| if printf '%s\n' "${changed}" | grep -E -q \ | |
| '^(Dockerfile|pyproject\.toml$|requirements\.txt$|\.github/workflows/container-attestation\.yml$)'; then | |
| echo "docker=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "docker=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Skip note (no container-relevant changes) | |
| if: steps.changes.outputs.docker == 'false' | |
| run: echo "No Dockerfile*/pyproject.toml/requirements.txt/workflow changes - smoke build skipped, required check passes." | |
| - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 | |
| if: steps.changes.outputs.docker == 'true' | |
| - name: Build API image (no push) | |
| if: steps.changes.outputs.docker == 'true' | |
| id: smoke | |
| uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 | |
| with: | |
| context: . | |
| file: Dockerfile.api | |
| push: false | |
| load: true | |
| tags: agentflow-api:pr-${{ github.event.pull_request.number }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| build-push-sign-attest: | |
| if: ${{ github.event_name == 'workflow_dispatch' && inputs.confirm == 'SIGN' && inputs.mode == 'build-and-sign' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| attestations: write | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 | |
| - name: Build and push API image | |
| id: build | |
| uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 | |
| with: | |
| context: . | |
| file: Dockerfile.api | |
| push: true | |
| tags: | | |
| ${{ env.IMAGE_REF }}:${{ github.sha }} | |
| ${{ env.IMAGE_REF }}:audit-${{ github.run_id }} | |
| - uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1 | |
| - name: Sign pushed image digest with cosign keyless | |
| env: | |
| IMAGE_REF: ${{ env.IMAGE_REF }} | |
| IMAGE_DIGEST: ${{ steps.build.outputs.digest }} | |
| run: cosign sign --yes ${IMAGE_REF}@${IMAGE_DIGEST} | |
| - uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-name: ${{ env.IMAGE_REF }} | |
| subject-digest: ${{ steps.build.outputs.digest }} | |
| push-to-registry: true | |
| attest-and-sign: | |
| if: ${{ github.event_name == 'workflow_dispatch' && inputs.confirm == 'SIGN' && inputs.mode == 'sign-existing-digest' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| attestations: write | |
| steps: | |
| - name: Validate digest inputs | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| case "${{ inputs.image_digest }}" in | |
| sha256:*) ;; | |
| *) echo "image_digest must be sha256:<digest>" >&2; exit 1 ;; | |
| esac | |
| if [ -z "${{ inputs.image_ref }}" ]; then | |
| echo "image_ref is required for sign-existing-digest mode" >&2 | |
| exit 1 | |
| fi | |
| case "${{ inputs.image_ref }}" in | |
| *@*) echo "image_ref must not include a digest; provide image_digest separately" >&2; exit 1 ;; | |
| *:latest) echo "image_ref must not be a mutable :latest tag" >&2; exit 1 ;; | |
| esac | |
| echo "IMAGE_REF=${{ inputs.image_ref }}" >> "$GITHUB_ENV" | |
| echo "IMAGE_DIGEST=${{ inputs.image_digest }}" >> "$GITHUB_ENV" | |
| - uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1 | |
| - name: Sign image digest with cosign keyless | |
| env: | |
| IMAGE_REF: ${{ env.IMAGE_REF }} | |
| IMAGE_DIGEST: ${{ env.IMAGE_DIGEST }} | |
| run: cosign sign --yes ${IMAGE_REF}@${IMAGE_DIGEST} | |
| - uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-name: ${{ inputs.image_ref }} | |
| subject-digest: ${{ inputs.image_digest }} | |
| push-to-registry: false |