Skip to content

Extract all queries to drivers, nuke sqlc, pro-specific executable #738

Extract all queries to drivers, nuke sqlc, pro-specific executable

Extract all queries to drivers, nuke sqlc, pro-specific executable #738

Workflow file for this run

name: Docker
on:
push:
branches:
- "master"
- "*" # TEMPORARY FOR TESTING
tags:
- "v*"
pull_request:
branches:
- "master"
- "*"
workflow_dispatch:
inputs:
ref:
description: "Tag to build (e.g. v0.1.0)"
required: true
env:
ECR_REGION: us-east-2
jobs:
build-riverui:
name: "Build image: riverui"
runs-on: ubuntu-latest
env:
IMAGE_NAME: ${{ github.repository }}
REGISTRY: ghcr.io
strategy:
matrix:
docker_platform:
- linux/amd64
- linux/arm64
outputs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
permissions:
attestations: write
contents: read
id-token: write
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Prepare
run: |
platform=${{ matrix.docker_platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
labels: |
org.opencontainers.image.source=https://github.com/riverqueue/riverui
org.opencontainers.image.description="River UI is a web-based user interface for River, a fast and reliable background job system."
org.opencontainers.image.licenses=MPL-2.0
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Build and push to GitHub Container Registry
id: build
uses: docker/build-push-action@v6
with:
context: .
pull: true
platforms: ${{ matrix.docker_platform }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache-${{ env.PLATFORM_PAIR }}
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:cache-${{ env.PLATFORM_PAIR }},mode=max
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true,annotation-index.org.opencontainers.image.description=River UI
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v2
with:
push-to-registry: true
subject-digest: ${{ steps.build.outputs.digest }}
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-oss-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
build-riverproui:
name: "Build image: riverproui"
runs-on: ubuntu-latest
env:
ECR_ACCOUNT_ID: ${{ secrets.ECR_CACHE_AWS_ACCOUNT_ID }}
ECR_ROLE_ARN: ${{ secrets.ECR_CACHE_ROLE_ARN }}
strategy:
matrix:
docker_platform:
- linux/amd64
- linux/arm64
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.ECR_ROLE_ARN }}
aws-region: ${{ env.ECR_REGION }}
role-session-name: GitHubActions
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Prepare
run: |
platform=${{ matrix.docker_platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta for Pro
id: meta-pro
uses: docker/metadata-action@v5
with:
images: ${{ format('{0}.dkr.ecr.{1}.amazonaws.com/riverqueue/riverproui', env.ECR_ACCOUNT_ID, env.ECR_REGION) }}
labels: |
org.opencontainers.image.source=https://github.com/riverqueue/riverui
org.opencontainers.image.description=River UI Pro is a web-based user interface for River, with pro features.
org.opencontainers.image.licenses=commercial
- name: Build & push (by digest)
id: build-pro
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.pro
pull: true
platforms: ${{ matrix.docker_platform }}
provenance: true
labels: ${{ steps.meta-pro.outputs.labels }}
cache-from: type=registry,ref=${{ env.ECR_ACCOUNT_ID }}.dkr.ecr.${{ env.ECR_REGION }}.amazonaws.com/riverqueue/riverproui:cache-${{ env.PLATFORM_PAIR }}
cache-to: type=registry,ref=${{ env.ECR_ACCOUNT_ID }}.dkr.ecr.${{ env.ECR_REGION }}.amazonaws.com/riverqueue/riverproui:cache-${{ env.PLATFORM_PAIR }},mode=max
outputs: type=image,name=${{ env.ECR_ACCOUNT_ID }}.dkr.ecr.${{ env.ECR_REGION }}.amazonaws.com/riverqueue/riverproui,push-by-digest=true,name-canonical=true,push=true
secrets: |
"riverpro_credential=${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}"
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build-pro.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: pro-digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge-riverui:
name: "Merge manifests: riverui"
runs-on: ubuntu-latest
env:
IMAGE_NAME: ${{ github.repository }}
REGISTRY: ghcr.io
needs:
- build-riverui
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-oss-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
labels: |
org.opencontainers.image.source=https://github.com/riverqueue/riverui
org.opencontainers.image.description="River UI is a web-based user interface for River, a fast and reliable background job system."
org.opencontainers.image.licenses=MPL-2.0
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create \
--annotation "index:org.opencontainers.image.source=https://github.com/riverqueue/riverui" \
--annotation "index:org.opencontainers.image.description=River UI is a web-based user interface for River, a fast and reliable background job system." \
--annotation "index:org.opencontainers.image.licenses=MPL-2.0" \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
merge-riverproui:
name: "Merge manifests: riverproui"
runs-on: ubuntu-latest
needs:
- build-riverproui
permissions:
contents: read
id-token: write
attestations: write
env:
ECR_ACCOUNT_ID: ${{ secrets.ECR_CACHE_AWS_ACCOUNT_ID }}
ECR_ROLE_ARN: ${{ secrets.ECR_CACHE_ROLE_ARN }}
TAG: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}
SHA_TAG: ${{ format('sha-{0}', github.sha) }}
outputs:
manifest_digest: ${{ steps.manifest_digest.outputs.digest }}
tag: ${{ steps.compute_tag.outputs.tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-tags: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up ORAS CLI
uses: oras-project/setup-oras@v1
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.ECR_ROLE_ARN }}
aws-region: ${{ env.ECR_REGION }}
role-session-name: GitHubActions
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: pro-digests-*
merge-multiple: true
- name: Compute TAG
id: compute_tag
run: |
if [[ "${{ github.ref }}" =~ ^refs/tags/riverproui/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
TAG="${GITHUB_REF_NAME#riverproui/}"
elif [ "${{ github.event_name }}" = "pull_request" ]; then
TAG="pr-${{ github.event.pull_request.number }}"
else
TAG="${{ github.ref_name }}"
fi
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "TAG=$TAG" >> $GITHUB_ENV
- name: Prepare ECR vars
run: |
ECR_IMAGE="${ECR_ACCOUNT_ID}.dkr.ecr.${ECR_REGION}.amazonaws.com/riverqueue/riverproui"
echo "ECR_IMAGE=${ECR_IMAGE}" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.ECR_IMAGE }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,pattern=sha-{{sha}}
type=semver,pattern=v{{version}},match=riverproui/(v.*)
- name: Create manifest list and push immutable tags
working-directory: /tmp/digests
run: |
docker buildx imagetools create \
--annotation "index:org.opencontainers.image.source=https://github.com/riverqueue/riverui" \
--annotation "index:org.opencontainers.image.description=River UI Pro is a web-based user interface for River, with pro features." \
--annotation "index:org.opencontainers.image.licenses=commercial" \
`echo "${{ steps.meta.outputs.tags }}" | xargs -n1 echo -t` \
$(printf "$ECR_IMAGE@sha256:%s " *)
- name: Tag mutable on release if latest in series
if: startsWith(github.ref, 'refs/tags/riverproui/v')
working-directory: /tmp/digests
run: |
STRIPPED_VERSION="${{ github.ref_name }}"
STRIPPED_VERSION="${STRIPPED_VERSION#riverproui/}"
MAJOR="${STRIPPED_VERSION#v}"
MAJOR="${MAJOR%%.*}"
MINOR_PATCH="${STRIPPED_VERSION#v${MAJOR}.}"
MINOR="${MINOR_PATCH%%.*}"
declare -a mutable_tags=()
GLOBAL_LATEST=$(git tag --list 'riverproui/v*' --sort=-v:refname | grep -E '^riverproui/v[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
if [ "$GLOBAL_LATEST" = "${{ github.ref_name }}" ]; then
mutable_tags+=("latest")
fi
LATEST_IN_MAJOR=$(git tag --list "riverproui/v${MAJOR}.*" --sort=-v:refname | head -n1)
if [ "$LATEST_IN_MAJOR" = "${{ github.ref_name }}" ]; then
mutable_tags+=("v${MAJOR}")
fi
LATEST_IN_MINOR=$(git tag --list "riverproui/v${MAJOR}.${MINOR}.*" --sort=-v:refname | head -n1)
if [ "$LATEST_IN_MINOR" = "${{ github.ref_name }}" ]; then
mutable_tags+=("v${MAJOR}.${MINOR}")
fi
for tag in "${mutable_tags[@]}"; do
docker buildx imagetools create \
--annotation "index:org.opencontainers.image.source=https://github.com/riverqueue/riverui" \
--annotation "index:org.opencontainers.image.description=River UI Pro is a web-based user interface for River, with pro features." \
--annotation "index:org.opencontainers.image.licenses=commercial" \
-t "$ECR_IMAGE:$tag" \
$(printf "$ECR_IMAGE@sha256:%s " *)
done
- name: Install crane
uses: imjasonh/setup-crane@v0.4
- name: Compute manifest digest for attestation
id: manifest_digest
run: |
DIGEST=$(crane digest "$ECR_IMAGE:$TAG")
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
- name: Generate build provenance attestation (not pushed)
id: attest
uses: actions/attest-build-provenance@v2
with:
push-to-registry: false
subject-digest: ${{ steps.manifest_digest.outputs.digest }}
subject-name: riverqueue.com/riverproui
- name: Resolve platform manifest digests
id: platform_digests
run: |
crane manifest "$ECR_IMAGE:$TAG" > /tmp/index.json
AMD64_DIGEST=$(jq -r '.manifests[] | select(.platform.os=="linux" and .platform.architecture=="amd64") .digest' /tmp/index.json)
ARM64_DIGEST=$(jq -r '.manifests[] | select(.platform.os=="linux" and .platform.architecture=="arm64") .digest' /tmp/index.json)
echo "amd64=$AMD64_DIGEST" >> "$GITHUB_OUTPUT"
echo "arm64=$ARM64_DIGEST" >> "$GITHUB_OUTPUT"
- name: Extract DSSE envelope from bundle
run: |
jq '.dsseEnvelope' "${{ steps.attest.outputs.bundle-path }}" > /tmp/dsse.json
- name: Decode DSSE payload to predicate (SLSA v1 statement)
run: |
jq -r '.payload' /tmp/dsse.json | base64 -d > /tmp/predicate.json
test "$(jq -r '.predicateType' /tmp/predicate.json)" = "https://slsa.dev/provenance/v1"
- name: Push Cosign attestations (index + per-platform)
env:
COSIGN_EXPERIMENTAL: "true"
COSIGN_REPOSITORY: ${{ env.ECR_ACCOUNT_ID }}.dkr.ecr.${{ env.ECR_REGION }}.amazonaws.com/riverqueue/riverproui
ECR_IMAGE: ${{ env.ECR_ACCOUNT_ID }}.dkr.ecr.${{ env.ECR_REGION }}.amazonaws.com/riverqueue/riverproui
run: |
for subject in "${{ steps.manifest_digest.outputs.digest }}" "${{ steps.platform_digests.outputs.amd64 }}" "${{ steps.platform_digests.outputs.arm64 }}"; do
cosign attest \
--predicate /tmp/predicate.json \
--type https://slsa.dev/provenance/v1 \
--yes \
--new-bundle-format \
"${ECR_IMAGE}@${subject}"
done
prefetch-riverproui-through-live-registry:
# Prefetch only for release semver tags
name: "Prefetch riverproui via live registry"
runs-on: ubuntu-latest
needs:
- merge-riverproui
env:
IMAGE_NAME: riverqueue.com/riverproui
MANIFEST_DIGEST: ${{ needs.merge-riverproui.outputs.manifest_digest }}
TAG: ${{ needs.merge-riverproui.outputs.tag }}
if: startsWith(github.ref, 'refs/tags/riverproui/v')
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-tags: true
- name: Force refresh mutable tags
if: startsWith(github.ref, 'refs/tags/riverproui/v')
run: |
STRIPPED_VERSION="${{ github.ref_name }}"
STRIPPED_VERSION="${STRIPPED_VERSION#riverproui/}"
MAJOR="${STRIPPED_VERSION#v}"
MAJOR="${MAJOR%%.*}"
MINOR_PATCH="${STRIPPED_VERSION#v${MAJOR}.}"
MINOR="${MINOR_PATCH%%.*}"
declare -a mutable_tags=()
GLOBAL_LATEST=$(git tag --list 'riverproui/v*' --sort=-v:refname | grep -E '^riverproui/v[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
if [ "$GLOBAL_LATEST" = "riverproui/${STRIPPED_VERSION}" ]; then
mutable_tags+=("latest")
fi
LATEST_IN_MAJOR=$(git tag --list "riverproui/v${MAJOR}.*" --sort=-v:refname | head -n1)
if [ "$LATEST_IN_MAJOR" = "riverproui/${STRIPPED_VERSION}" ]; then
mutable_tags+=("v${MAJOR}")
fi
LATEST_IN_MINOR=$(git tag --list "riverproui/v${MAJOR}.${MINOR}.*" --sort=-v:refname | head -n1)
if [ "$LATEST_IN_MINOR" = "riverproui/${STRIPPED_VERSION}" ]; then
mutable_tags+=("v${MAJOR}.${MINOR}")
fi
for tag in "${mutable_tags[@]}"; do
echo "Force refreshing mutable tag: $tag"
curl -f -u river:"${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}" \
-H "X-Force-Fetch-From-Upstream: ${{ secrets.FORCE_FETCH_SECRET }}" \
-H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
"https://riverqueue.com/v2/riverproui/manifests/${tag}" -o /dev/null
curl -f -u river:"${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}" \
-H "X-Force-Fetch-From-Upstream: ${{ secrets.FORCE_FETCH_SECRET }}" \
-H "Accept: application/vnd.oci.image.index.v1+json" \
"https://riverqueue.com/v2/riverproui/manifests/${tag}" -o /dev/null
done
- name: Login to live registry
uses: docker/login-action@v3
with:
registry: riverqueue.com
username: river
password: ${{ secrets.RIVERPRO_GO_MOD_CREDENTIAL }}
- name: Install crane
uses: imjasonh/setup-crane@v0.4
- name: Set up ORAS CLI
uses: oras-project/setup-oras@v1
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Resolve manifest digest
id: resolve-digest
run: |
DIGEST=$(crane digest "$IMAGE_NAME:$TAG")
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
- name: Fetch index manifest with crane (Docker preferred)
run: crane manifest "$IMAGE_NAME:$TAG" > /tmp/index-manifest-docker.json
- name: Fetch index manifest with oras (OCI preferred)
run: oras manifest fetch --output /tmp/index-manifest-oci.json "$IMAGE_NAME:$TAG"
- name: Extract platform manifest digests
id: platform-digests
run: |
AMD64_DIGEST=$(jq -r '.manifests[] | select(.platform.architecture == "amd64" and .platform.os == "linux") | .digest' /tmp/index-manifest-docker.json)
ARM64_DIGEST=$(jq -r '.manifests[] | select(.platform.architecture == "arm64" and .platform.os == "linux") | .digest' /tmp/index-manifest-docker.json)
echo "amd64_digest=$AMD64_DIGEST" >> $GITHUB_OUTPUT
echo "arm64_digest=$ARM64_DIGEST" >> $GITHUB_OUTPUT
- name: Fetch amd64 manifest with crane (Docker media type)
run: crane manifest "$IMAGE_NAME@${{ steps.platform-digests.outputs.amd64_digest }}" > /tmp/amd64-manifest-docker.json
- name: Fetch amd64 manifest with oras (OCI media type)
run: oras manifest fetch --output /tmp/amd64-manifest-oci.json "$IMAGE_NAME@${{ steps.platform-digests.outputs.amd64_digest }}"
- name: Fetch arm64 manifest with crane (Docker media type)
run: crane manifest "$IMAGE_NAME@${{ steps.platform-digests.outputs.arm64_digest }}" > /tmp/arm64-manifest-docker.json
- name: Fetch arm64 manifest with oras (OCI media type)
run: oras manifest fetch --output /tmp/arm64-manifest-oci.json "$IMAGE_NAME@${{ steps.platform-digests.outputs.arm64_digest }}"
- name: Prefetch image blobs (configs + layers)
run: |
# Blobs are identical across media types; fetch once per platform.
for m in /tmp/amd64-manifest-docker.json /tmp/arm64-manifest-docker.json; do
cfg=$(jq -r '.config.digest // empty' "$m")
if [ -n "$cfg" ]; then
oras blob fetch --output /dev/null "$IMAGE_NAME@$cfg"
fi
jq -r '.layers[]?.digest // empty' "$m" | while read -r ld; do
[ -n "$ld" ] && oras blob fetch --output /dev/null "$IMAGE_NAME@$ld"
done
done
- name: Prefetch all referrers content
run: |
for subject in "${{ steps.resolve-digest.outputs.digest }}" "${{ steps.platform-digests.outputs.amd64_digest }}" "${{ steps.platform-digests.outputs.arm64_digest }}"; do
oras discover --format json "$IMAGE_NAME@$subject" | tee /tmp/referrers.json
digests=$(jq -r '.manifests[]?.digest // empty' /tmp/referrers.json)
for d in $digests; do
tmpfile=$(mktemp)
# Fetch the full referrer manifest to a file so we can parse layers + config
oras manifest fetch --output "$tmpfile" "$IMAGE_NAME@$d"
# Prefetch config blob (often empty but warms cache)
cfg=$(jq -r '.config.digest // empty' "$tmpfile")
if [ -n "$cfg" ]; then
oras blob fetch --output /dev/null "$IMAGE_NAME@$cfg"
fi
# Prefetch any layer blobs (DSSE envelope, bundle, etc.)
jq -r '.layers[]?.digest // empty' "$tmpfile" | while read -r ld; do
[ -n "$ld" ] && oras blob fetch --output /dev/null "$IMAGE_NAME@$ld"
done
done
done
- name: Verify image attestation via Workers (registry-based)
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign verify-attestation \
--type https://slsa.dev/provenance/v1 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/riverqueue/riverui/.*' \
--new-bundle-format \
"$IMAGE_NAME@${{ steps.resolve-digest.outputs.digest }}" \
| tee /tmp/cosign-verify-registry-index.txt
cosign verify-attestation \
--type https://slsa.dev/provenance/v1 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/riverqueue/riverui/.*' \
--new-bundle-format \
"$IMAGE_NAME@${{ steps.platform-digests.outputs.amd64_digest }}" \
| tee /tmp/cosign-verify-registry-amd64.txt
cosign verify-attestation \
--type https://slsa.dev/provenance/v1 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/riverqueue/riverui/.*' \
--new-bundle-format \
"$IMAGE_NAME@${{ steps.platform-digests.outputs.arm64_digest }}" \
| tee /tmp/cosign-verify-registry-arm64.txt
- name: Verify Sigstore bundle against subject bytes (offline crypto)
run: |
verify_bundle () {
subject="$1"
# Exact subject bytes
oras manifest fetch --output /tmp/subject.json "$IMAGE_NAME@$subject"
# Find *all* bundle candidates for this subject (both media-type spellings)
refjson=$(mktemp)
oras discover --format json "$IMAGE_NAME@$subject" > "$refjson"
mapfile -t bundle_mfs < <(jq -r '.manifests[]
| select(.artifactType=="application/vnd.dev.sigstore.bundle.v0.3+json"
or .artifactType=="application/vnd.dev.sigstore.bundle+json;version=0.3")
| .digest' "$refjson")
ok=0
for bmf in "${bundle_mfs[@]}"; do
[ -z "$bmf" ] && continue
mf=$(mktemp)
oras manifest fetch --output "$mf" "$IMAGE_NAME@$bmf"
bundle_blob=$(jq -r '.layers[0].digest' "$mf")
oras blob fetch --output /tmp/bundle.json "$IMAGE_NAME@$bundle_blob"
if cosign verify-blob-attestation \
--bundle /tmp/bundle.json \
--new-bundle-format \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--certificate-identity-regexp '^https://github.com/riverqueue/riverui/.*' \
/tmp/subject.json > /tmp/verify.out 2>&1; then
echo "Verified OK with bundle $bmf"
ok=1
break
fi
done
if [ "$ok" -ne 1 ]; then
echo "No matching bundle verified for subject $subject"
cat /tmp/verify.out || true
exit 1
fi
}
verify_bundle "${{ steps.resolve-digest.outputs.digest }}"
verify_bundle "${{ steps.platform-digests.outputs.amd64_digest }}"
verify_bundle "${{ steps.platform-digests.outputs.arm64_digest }}"
- name: Upload debug artifacts
if: failure() || cancelled()
uses: actions/upload-artifact@v4
with:
name: prefetch-debug
path: |
/tmp/index-manifest-docker.json
/tmp/index-manifest-oci.json
/tmp/subject.json
/tmp/bundle.json
/tmp/cosign-verify-registry-index.txt
/tmp/cosign-verify-registry-amd64.txt
/tmp/cosign-verify-registry-arm64.txt
retention-days: 1