Let fork PRs run test/lint by sharing images as artifacts#118
Open
ShanaLMoore wants to merge 2 commits into
Open
Let fork PRs run test/lint by sharing images as artifacts#118ShanaLMoore wants to merge 2 commits into
ShanaLMoore wants to merge 2 commits into
Conversation
Fork PRs get a read-only GITHUB_TOKEN and no secrets, so they cannot push images to GHCR. #117 stopped the build step from failing on that push, but the downstream test and lint jobs pull the built images from the registry, so on a fork PR they had nothing to pull and still failed. Instead of pushing on fork PRs, the build job now loads the amd64 image locally, retags it to the platform-less tag the multiarch manifest would have produced (what docker-compose references), and uploads it as a workflow artifact. The test and lint jobs download and `docker load` those artifacts before `docker compose`, so the images are present locally and the registry pull no-ops. All new steps are gated on the fork-PR condition, so same-repo pushes, same-repo PRs, merges to the default branch, and workflow_dispatch are unchanged and continue to push/pull via the registry exactly as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Enables fork-based pull requests to run test and lint jobs successfully without requiring any registry push permissions by passing built Docker images between jobs via workflow artifacts (fork PRs only).
Changes:
- build.yaml: On fork PRs, load the amd64 image locally, retag it to the platform-less
:${TAG},docker saveit, and upload it as afork-image-*artifact. - test.yaml / lint.yaml: On fork PRs, download
fork-image-*artifacts anddocker loadthem before runningdocker compose, so images are available locally even when GHCR push/pull is unavailable.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| .github/workflows/build.yaml | Exports fork-PR-built images as artifacts (amd64 only) for downstream jobs to consume. |
| .github/workflows/test.yaml | Downloads and loads fork-PR image artifacts so docker compose can start without pulling from GHCR. |
| .github/workflows/lint.yaml | Downloads and loads fork-PR image artifacts so lint can run using locally available images. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Addresses Copilot feedback: the load steps use the bash-specific shopt builtin, so declare shell: bash rather than rely on the runner default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maxkadel
approved these changes
Jul 13, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the reusable build/test/lint workflows fully pass on pull requests opened from a fork, without granting forks any elevated permissions.
Follow-up to #117 (cc @laritakr). Context: samvera-labs/hyku_knapsack#53 / samvera-labs/hyku_knapsack#58.
The problem #117 left open
Fork PRs run with a read-only
GITHUB_TOKENand no secrets, so they can't push images to GHCR. #117 correctly stopped the build step from failing on that denied push. Buttest.yamlandlint.yamlobtain the built images by pulling them from GHCR (docker compose pull/up). On a fork PR nothing was pushed, so there's nothing to pull:web/workerhave abuild:context in the app compose file, so compose can rebuild them locally, butsolr(and any registry-only service) has only animage:reference, sodocker compose upcan neither pull nor build it and the test job fails.So #117 moved the failure from the build step to the test step (as @rob and Max both called out).
The fix: hand images to test/lint as artifacts instead of via the registry
On fork PRs only:
load: truethe amd64 image into the local daemon, retag it from:${TAG}-amd64to the platform-less:${TAG}that the (skipped) multiarch manifest job would have created and thatdocker-composereferences,docker saveit, and upload it as afork-image-<component>artifact. Only the amd64 leg is exported since test/lint run onubuntu-latest; the arm leg still builds to validate the Dockerfile.download-artifact+docker loadthe tarballs beforedocker compose, so the images are present locally and the registry pull no-ops.This keeps forks fully unprivileged: no secrets, no write token, no
pull_request_target. It just shares the already-built images within the same workflow run (test/lint alreadyneeds: buildin the callers).Not changed
Every new step is gated on
github.event_name == 'pull_request' && head.repo.full_name != github.repository. Same-repo pushes, same-repo PRs, merges to the default branch, andworkflow_dispatchtake none of the new paths and push/pull via the registry exactly as before.Tradeoffs / open questions for review
docker saveof a full Rails app image is large; upload+download+load adds minutes to fork-PR runs and consumes artifact storage (retention-days: 1to limit it). Acceptable cost for "forks can run CI at all," but worth a sanity check. Callers that buildbasewill also export it unless they scopecomponents.🤖 Generated with Claude Code