ci: buildx cache in same-region Artifact Registry (keyless)

Swap type=gha buildx cache for type=registry against
us-central1-docker.pkg.dev/.../ci-cache — same region as the ARC
runners, so layer blobs stop round-tripping to GitHub's cache CDN on
every build/rerun.

Auth is keyless both ways: reads ride the runner pod's GKE Workload
Identity (no login needed for cache-from); writes exchange the
workflow's GitHub OIDC token via WIF (google-github-actions/auth,
fork-guarded — fork PRs build cache-cold exactly like type=gha).
publish keeps its own unconditional WIF auth (trusted contexts only).
Infra: hermes-agent-ci-infra ef8dfb2.
This commit is contained in:
ethernet 2026-07-30 21:28:06 -04:00
parent 6d018b15c3
commit 96c08cf3c5

View file

@ -30,8 +30,20 @@ jobs:
# Build and test the image for each architecture. This job runs PR code,
# so it must remain secret-free. Publishing happens in the separate,
# protected publish job after these tests pass.
#
# Buildx layer cache lives in Artifact Registry (us-central1, same region
# as the ARC runners) instead of GitHub's cache CDN. Reads are keyless via
# GKE Workload Identity on the runner pods; writes are keyless via GitHub
# OIDC -> GCP WIF (google-github-actions/auth). Fork PRs get neither an
# id-token nor pod WI outside our cluster, so they build cache-cold —
# same behavior type=gha gave them.
build:
if: github.repository == 'NousResearch/hermes-agent'
permissions:
contents: read
# OIDC token for WIF cache writes. Fork-PR runs receive no id-token;
# the auth step below is skipped for them.
id-token: write
strategy:
fail-fast: false
matrix:
@ -39,13 +51,9 @@ jobs:
- arch: amd64
runner: arc-runner-set
platform: linux/amd64
cache-from: type=gha,scope=docker-amd64
cache-to: type=gha,mode=max,scope=docker-amd64
- arch: arm64
runner: arc-runner-arm64
platform: linux/arm64
cache-from: type=gha,scope=docker-arm64
cache-to: type=gha,mode=max,scope=docker-arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
@ -68,8 +76,32 @@ jobs:
if: steps.buildx.outcome == 'failure'
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
# Keyless GCP auth via GitHub OIDC -> WIF. Fork PRs have no id-token
# (permissions are read-only in their context), so this step is
# skipped and the build runs cache-read-only via the pod's GKE
# Workload Identity.
- name: Authenticate to GCP (WIF, cache writes)
id: gcp-auth
if: github.event.pull_request.head.repo.fork != true
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
project_id: hermes-agent-github-actions
workload_identity_provider: projects/1067970703723/locations/global/workloadIdentityPools/github-actions/providers/github-oidc
service_account: gha-buildx-cache@hermes-agent-github-actions.iam.gserviceaccount.com
token_format: access_token
- name: Log in to Artifact Registry
if: steps.gcp-auth.outputs.access_token != ''
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: us-central1-docker.pkg.dev
username: oauth2accesstoken
password: ${{ steps.gcp-auth.outputs.access_token }}
# Build once, load into the local daemon for testing. Cached
# per-arch; the push step below reuses every layer from this build.
# Cache lives in same-region Artifact Registry: reads always work
# (pod Workload Identity); writes only when the WIF auth step ran.
- name: Build image (${{ matrix.arch }})
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
@ -80,8 +112,8 @@ jobs:
tags: ${{ env.IMAGE_NAME }}:test
build-args: |
HERMES_GIT_SHA=${{ github.sha }}
cache-from: ${{ matrix.cache-from }}
cache-to: ${{ (github.event_name != 'pull_request') && matrix.cache-to || '' }}
cache-from: type=registry,ref=us-central1-docker.pkg.dev/hermes-agent-github-actions/ci-cache/hermes-agent:buildcache-${{ matrix.arch }}
cache-to: ${{ steps.gcp-auth.outputs.access_token != '' && format('type=registry,ref=us-central1-docker.pkg.dev/hermes-agent-github-actions/ci-cache/hermes-agent:buildcache-{0},mode=max,image-manifest=true', matrix.arch) || '' }}
# Run the docker-integration test suite against the freshly-built
# image already loaded into the local daemon (`:test`).
@ -131,6 +163,9 @@ jobs:
if: github.repository == 'NousResearch/hermes-agent' && (github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release')
needs: [build]
environment: container-publish
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
@ -138,13 +173,9 @@ jobs:
- arch: amd64
runner: arc-runner-set
platform: linux/amd64
cache-from: type=gha,scope=docker-amd64
cache-to: type=gha,mode=max,scope=docker-amd64
- arch: arm64
runner: arc-runner-arm64
platform: linux/arm64
cache-from: type=gha,scope=docker-arm64
cache-to: type=gha,mode=max,scope=docker-arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
steps:
@ -162,6 +193,24 @@ jobs:
if: steps.buildx.outcome == 'failure'
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
# Same keyless AR cache as the build job. Publish only runs on
# trusted main/release contexts, so WIF auth is unconditional here.
- name: Authenticate to GCP (WIF, cache)
id: gcp-auth
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
project_id: hermes-agent-github-actions
workload_identity_provider: projects/1067970703723/locations/global/workloadIdentityPools/github-actions/providers/github-oidc
service_account: gha-buildx-cache@hermes-agent-github-actions.iam.gserviceaccount.com
token_format: access_token
- name: Log in to Artifact Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: us-central1-docker.pkg.dev
username: oauth2accesstoken
password: ${{ steps.gcp-auth.outputs.access_token }}
- name: Log in to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
@ -182,8 +231,8 @@ jobs:
build-args: |
HERMES_GIT_SHA=${{ github.sha }}
outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
cache-from: ${{ matrix.cache-from }}
cache-to: ${{ matrix.cache-to }}
cache-from: type=registry,ref=us-central1-docker.pkg.dev/hermes-agent-github-actions/ci-cache/hermes-agent:buildcache-${{ matrix.arch }}
cache-to: type=registry,ref=us-central1-docker.pkg.dev/hermes-agent-github-actions/ci-cache/hermes-agent:buildcache-${{ matrix.arch }},mode=max,image-manifest=true
- name: Export digest
run: |