Skip to content

Commit cf7cf61

Browse files
authored
Handle fork PR Docker cache permissions (#3423)
### Motivation - External fork PRs were failing CI at Docker build/cache steps because the runner `GITHUB_TOKEN` for forks does not have permission to write GHCR packages or cache entries. ### Description - In `.github/workflows/ci.yml` conditionally disable `cache-to` exports to GHCR for external pull requests while preserving cache exports for same-repo runs, pushes, merge groups, and workflow dispatches. - In `.github/workflows/anneal.yml` conditionally disable `cache-to` and guard the `Build and push Docker image` step so image push/cache writes are only attempted when the run is allowed to write to GHCR. - In `.github/workflows/anneal.yml` add `if:` guards on Anneal consumer jobs (`anneal_tests` and `verify_examples`) so those consumer jobs are skipped for external fork PRs that cannot publish the GHCR image they would consume. ### Testing - Ran `./ci/check_actions.sh` and it completed successfully. - Ran `git diff --check` and it produced no issues. - Ran `CARGO_ZEROCOPY_AUTO_INSTALL_TOOLCHAIN=1 ./githooks/pre-push` to exercise the repo hooks in a non-interactive way and it completed successfully (the initial pre-push without auto-install hit local tooling prompts).
1 parent f70e422 commit cf7cf61

2 files changed

Lines changed: 20 additions & 4 deletions

File tree

.github/workflows/anneal.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ jobs:
8989
cache-from: |
9090
type=registry,ref=ghcr.io/google/zerocopy/anneal-cache:${{ steps.docker_tag.outputs.tag }}
9191
type=registry,ref=ghcr.io/google/zerocopy/anneal-cache:main
92-
cache-to: type=registry,ref=ghcr.io/google/zerocopy/anneal-cache:${{ steps.docker_tag.outputs.tag }},mode=max
92+
# External pull requests have read-only package permissions, so they
93+
# can consume GHCR caches but cannot export updated cache layers.
94+
cache-to: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && format('type=registry,ref=ghcr.io/google/zerocopy/anneal-cache:{0},mode=max', steps.docker_tag.outputs.tag) || '' }}
9395

9496
- name: Record build time
9597
env:
@@ -137,7 +139,9 @@ jobs:
137139
# The build portion of this step will always be cached thanks to the
138140
# dry-run build above.
139141
- name: Build and push Docker image
140-
if: steps.check_remote.outputs.match != 'true'
142+
# External pull requests cannot write the PR-specific image tag to GHCR;
143+
# trusted runs still push, so unexpected GHCR write failures stay loud.
144+
if: steps.check_remote.outputs.match != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
141145
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
142146
# NOTE: All arguments here must match the dry-run step above exactly
143147
# in order to ensure we hit the cache for the local build!
@@ -154,7 +158,9 @@ jobs:
154158
cache-from: |
155159
type=registry,ref=ghcr.io/google/zerocopy/anneal-cache:${{ steps.docker_tag.outputs.tag }}
156160
type=registry,ref=ghcr.io/google/zerocopy/anneal-cache:main
157-
cache-to: type=registry,ref=ghcr.io/google/zerocopy/anneal-cache:${{ steps.docker_tag.outputs.tag }},mode=max
161+
# External pull requests have read-only package permissions, so they
162+
# can consume GHCR caches but cannot export updated cache layers.
163+
cache-to: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && format('type=registry,ref=ghcr.io/google/zerocopy/anneal-cache:{0},mode=max', steps.docker_tag.outputs.tag) || '' }}
158164

159165
measure_image_size:
160166
# This job measures the size of the Docker image built in the previous step
@@ -230,6 +236,10 @@ jobs:
230236
name: Anneal Tests
231237
runs-on: ubuntu-latest
232238
needs: build_docker_env
239+
# External pull requests cannot push the Docker image these jobs pull from
240+
# GHCR. Skip the consumer jobs in that case rather than failing on an
241+
# expected permission denial.
242+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
233243
permissions:
234244
contents: write # Required to push benchmark data to the storage branch
235245
packages: read # required to pull docker caches from ghcr.io
@@ -355,6 +365,10 @@ jobs:
355365
name: Verify example (${{ matrix.example }})
356366
runs-on: ubuntu-latest
357367
needs: build_docker_env
368+
# External pull requests cannot push the Docker image these jobs pull from
369+
# GHCR. Skip the consumer jobs in that case rather than failing on an
370+
# expected permission denial.
371+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
358372
permissions:
359373
contents: read
360374
packages: read # required to pull docker caches from ghcr.io

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1040,7 +1040,9 @@ jobs:
10401040
cache-from: |
10411041
type=registry,ref=ghcr.io/google/zerocopy/zerocopy-ci-cache:${{ steps.docker_tag.outputs.tag }}
10421042
type=registry,ref=ghcr.io/google/zerocopy/zerocopy-ci-cache:main
1043-
cache-to: type=registry,ref=ghcr.io/google/zerocopy/zerocopy-ci-cache:${{ steps.docker_tag.outputs.tag }},mode=max
1043+
# External pull requests have read-only package permissions, so they
1044+
# can consume GHCR caches but cannot export updated cache layers.
1045+
cache-to: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && format('type=registry,ref=ghcr.io/google/zerocopy/zerocopy-ci-cache:{0},mode=max', steps.docker_tag.outputs.tag) || '' }}
10441046

10451047
# Used to signal to branch protections that all other jobs have succeeded.
10461048
all-jobs-succeed:

0 commit comments

Comments
 (0)