ci: Add GitHub Container Registry (ghcr.io) publishing - #4013
Conversation
|
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRelease pipeline now publishes Docker images and multi-arch manifests to GitHub Container Registry (GHCR) alongside Docker Hub. Build and manifest jobs gain GHCR permissions, egress allowlist entries, and login steps; manifest scripts conditionally mirror multi-arch manifests to GHCR when repository context exists. ChangesGitHub Container Registry Publishing
Sequence DiagramsequenceDiagram
participant CI as Release Workflow
participant DockerHub as docker.io/<owner>/<repo>
participant GHCR as ghcr.io/<owner>/<repo>
CI->>CI: build per-arch images (amd64, arm64, UBI9 variants)
CI->>DockerHub: push per-arch images
CI->>GHCR: login (github.actor + GITHUB_TOKEN)
CI->>GHCR: imagetools create / push multi-arch manifests (mirror Docker Hub)
CI->>CI: emit dockerhub-tag and ghcr-tag outputs
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Confidence Score: 5/5Safe to merge — changes are strictly additive CI/CD plumbing; Docker Hub publishing is completely untouched and GHCR mirroring is wrapped in best-effort error handling throughout. The change is purely additive CI configuration. Docker Hub flows are unchanged. GHCR steps use No files require special attention. The one minor observation (jq multi-line output) is inside the best-effort subshell and would only cause a GHCR-side warning, not a Docker Hub regression. Important Files Changed
Reviews (13): Last reviewed commit: "Merge branch 'dev' into feat/add-ghcr-pu..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.github/workflows/release-pipeline.yml (2)
1673-1697:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftDecouple Docker Hub publishing from GHCR publishing.
Each build job builds and pushes both registries in one
docker/build-push-actionby passing a combined tag list (${BASE_TAG}for Docker Hub +${GHCR_TAG}for GHCR) viatags: ${{ steps.tags.outputs.tags }}. A GHCR auth/availability failure will fail the shared step and block Docker Hub publication too. Split into separate push steps per registry (or push once by digest and then fan out tags per registry).Also applies to: 1777-1801, 1927-1953, 2034-2060
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release-pipeline.yml around lines 1673 - 1697, The current build step "Build and push AMD64 Docker image" is pushing both Docker Hub (BASE_TAG) and GHCR (GHCR_TAG) in a single docker/build-push-action invocation using steps.tags.outputs.tags, which causes a failure in one registry to block the other; modify the workflow so the image build and pushes are decoupled: either (a) run one build that outputs an image digest (use build-push-action with push: false and output the image digest), then add two separate docker/build-push-action steps that only push the already-built image to each registry/tag (one using BASE_TAG for Docker Hub, another using GHCR_TAG for GHCR), or (b) keep the build+push but split into two distinct build-push-action invocations — one that only pushes BASE_TAG and one that only pushes GHCR_TAG — ensuring each uses its own registry credentials; apply this refactor to the similarly structured steps that create tags and push images (the blocks that generate BASE_TAG/GHCR_TAG and use steps.tags.outputs.tags).
1628-1640:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd
pkg-containers.githubusercontent.com:443to the harden-runnerallowed-endpointsfor GHCR publish/manifest jobs.
docker-build-amd64,docker-build-arm64,docker-build-ubi9-amd64,docker-build-ubi9-arm64,docker-manifest, anddocker-manifest-ubi9allowghcr.io:443but omitpkg-containers.githubusercontent.com:443; withegress-policy: block, GHCR redirects for blob/manifest operations can fail. Other GHCR-consuming jobs in this workflow already includepkg-containers.githubusercontent.com:443, so align these blocks.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release-pipeline.yml around lines 1628 - 1640, The harden-runner job blocks for docker-build-amd64, docker-build-arm64, docker-build-ubi9-amd64, docker-build-ubi9-arm64, docker-manifest, and docker-manifest-ubi9 are missing pkg-containers.githubusercontent.com:443 in their allowed-endpoints list; update each of those allowed-endpoints entries to include "pkg-containers.githubusercontent.com:443" alongside ghcr.io:443 so GHCR blob/manifest redirects succeed under egress-policy: block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/scripts/create-docker-manifest.sh:
- Around line 41-67: The GHCR mirroring block (uses GHCR_IMAGE,
GHCR_AMD64_DIGEST, GHCR_ARM64_DIGEST and runs docker manifest
inspect/create/push) can fail under set -e and thus mark the whole job failed;
make the GHCR steps best-effort by isolating their exit status: either disable
errexit around that block (e.g., set +e before the GHCR commands and restore set
-e after) or run the whole block in a subshell and swallow failures (e.g., ( ...
) || true), ensuring you still log errors from docker manifest
inspect/create/push but do not let a non-zero exit code from those commands
abort the script.
---
Outside diff comments:
In @.github/workflows/release-pipeline.yml:
- Around line 1673-1697: The current build step "Build and push AMD64 Docker
image" is pushing both Docker Hub (BASE_TAG) and GHCR (GHCR_TAG) in a single
docker/build-push-action invocation using steps.tags.outputs.tags, which causes
a failure in one registry to block the other; modify the workflow so the image
build and pushes are decoupled: either (a) run one build that outputs an image
digest (use build-push-action with push: false and output the image digest),
then add two separate docker/build-push-action steps that only push the
already-built image to each registry/tag (one using BASE_TAG for Docker Hub,
another using GHCR_TAG for GHCR), or (b) keep the build+push but split into two
distinct build-push-action invocations — one that only pushes BASE_TAG and one
that only pushes GHCR_TAG — ensuring each uses its own registry credentials;
apply this refactor to the similarly structured steps that create tags and push
images (the blocks that generate BASE_TAG/GHCR_TAG and use
steps.tags.outputs.tags).
- Around line 1628-1640: The harden-runner job blocks for docker-build-amd64,
docker-build-arm64, docker-build-ubi9-amd64, docker-build-ubi9-arm64,
docker-manifest, and docker-manifest-ubi9 are missing
pkg-containers.githubusercontent.com:443 in their allowed-endpoints list; update
each of those allowed-endpoints entries to include
"pkg-containers.githubusercontent.com:443" alongside ghcr.io:443 so GHCR
blob/manifest redirects succeed under egress-policy: block.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: dac36b0f-0ba1-449a-8f25-335b2eddadc6
📒 Files selected for processing (3)
.github/workflows/release-pipeline.yml.github/workflows/scripts/create-docker-manifest-ubi9.sh.github/workflows/scripts/create-docker-manifest.sh
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.github/workflows/release-pipeline.yml (2)
1673-1696:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDecouple GHCR pushes from the Docker Hub build-push step
docker/build-push-actionpushes all tags/registries configured in the same step; if the GHCR push fails (auth/outage/permissions), the whole step typically fails—turning GHCR into a hard dependency that can block the Docker Hub release path even though downstream manifest scripts treat GHCR as best-effort.Suggested pattern
- name: Determine Docker tags id: tags run: | VERSION="${{ needs.detect-changes.outputs.transport-version }}" BASE_TAG="${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ env.IMAGE_NAME }}:v${VERSION}-amd64" GHCR_REPO=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') - GHCR_TAG="ghcr.io/${GHCR_REPO}:v${VERSION}-amd64" - { - echo "tags<<EOF" - echo "${BASE_TAG}" - echo "${GHCR_TAG}" - echo "EOF" - } >> $GITHUB_OUTPUT + echo "dockerhub_tag=${BASE_TAG}" >> $GITHUB_OUTPUT + echo "ghcr_tag=ghcr.io/${GHCR_REPO}:v${VERSION}-amd64" >> $GITHUB_OUTPUT - - name: Build and push AMD64 Docker image + - name: Build and push AMD64 Docker image to Docker Hub uses: step-security/docker-build-push-action@846549baaf047e867d038826129a64d81df0f704 with: push: true - tags: ${{ steps.tags.outputs.tags }} + tags: ${{ steps.tags.outputs.dockerhub_tag }} + - name: Mirror AMD64 Docker image to GHCR + continue-on-error: true + uses: step-security/docker-build-push-action@846549baaf047e867d038826129a64d81df0f704 + with: + push: true + tags: ${{ steps.tags.outputs.ghcr_tag }}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release-pipeline.yml around lines 1673 - 1696, The current build step "Build and push AMD64 Docker image" pushes all tags returned by the "tags" step making GHCR failures block the Docker Hub push; change the "Build and push AMD64 Docker image" step to only push the Docker Hub tag (use the BASE_TAG output from the tags step) and add a separate step that pushes the GHCR_TAG (from steps.tags.outputs) with isolated auth and failure handling (e.g., its own push action or the same action but targeting only GHCR) and mark that GHCR push step as non-blocking (continue-on-error or conditional on auth) so GHCR becomes best-effort while the Docker Hub release remains reliable.
1628-1640:⚠️ Potential issue | 🟠 MajorAdd
pkg-containers.githubusercontent.com:443to the harden-runner allowlist for the GHCR publish/manifest jobsIn
.github/workflows/release-pipeline.yml, the GHCR egress allowlists for the GHCR-related publish/manifest steps include onlyghcr.io:443(e.g., blocks around 1628-1640, 1737-1744, 1822-1827, 1886-1894, 1993-2001, 2081-2085) and omitpkg-containers.githubusercontent.com:443. These jobs run.github/workflows/scripts/create-docker-manifest.sh/create-docker-manifest-ubi9.sh, which performsdocker manifest inspect/docker manifest pushagainstghcr.io. GitHub Container Registry can serve image-layer/blob content frompkg-containers.githubusercontent.comeven when interacting withghcr.io, so restricting egress toghcr.iocan still break GHCR image operations—align this allowlist with the other GHCR-consuming jobs that already allowpkg-containers.githubusercontent.com:443.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release-pipeline.yml around lines 1628 - 1640, The GHCR-related egress allowlist in the release pipeline's harden-runner steps currently lists ghcr.io:443 but omits pkg-containers.githubusercontent.com:443, which can cause docker manifest inspect/push (scripts create-docker-manifest.sh and create-docker-manifest-ubi9.sh) to fail when layers are served from pkg-containers.githubusercontent.com; update the allowed-endpoints block (the allowed-endpoints entry used by the GHCR publish/manifest jobs) to include pkg-containers.githubusercontent.com:443 alongside ghcr.io:443 so the runner can access image layer/blob content for GHCR operations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/release-pipeline.yml:
- Around line 1673-1696: The current build step "Build and push AMD64 Docker
image" pushes all tags returned by the "tags" step making GHCR failures block
the Docker Hub push; change the "Build and push AMD64 Docker image" step to only
push the Docker Hub tag (use the BASE_TAG output from the tags step) and add a
separate step that pushes the GHCR_TAG (from steps.tags.outputs) with isolated
auth and failure handling (e.g., its own push action or the same action but
targeting only GHCR) and mark that GHCR push step as non-blocking
(continue-on-error or conditional on auth) so GHCR becomes best-effort while the
Docker Hub release remains reliable.
- Around line 1628-1640: The GHCR-related egress allowlist in the release
pipeline's harden-runner steps currently lists ghcr.io:443 but omits
pkg-containers.githubusercontent.com:443, which can cause docker manifest
inspect/push (scripts create-docker-manifest.sh and
create-docker-manifest-ubi9.sh) to fail when layers are served from
pkg-containers.githubusercontent.com; update the allowed-endpoints block (the
allowed-endpoints entry used by the GHCR publish/manifest jobs) to include
pkg-containers.githubusercontent.com:443 alongside ghcr.io:443 so the runner can
access image layer/blob content for GHCR operations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 441164bd-e397-4490-a32d-4c76f8d55bfe
📒 Files selected for processing (1)
.github/workflows/release-pipeline.yml
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/release-pipeline.yml (1)
1661-1672: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueConsider using consistent action sources for Docker logins.
Docker Hub login uses
step-security/docker-login-actionwhile GHCR login usesdocker/login-action. Both are SHA-pinned and functionally equivalent, but using the same provider would improve consistency with the rest of the workflow's hardening approach.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release-pipeline.yml around lines 1661 - 1672, Replace the mixed Docker login actions with a single consistent provider: update the "Log in to Docker Hub" step that currently uses step-security/docker-login-action to use the same action provider as the GHCR step (docker/login-action) with an appropriate SHA/pinned version (matching the GHCR step's c94ce9fb4685... or other vetted SHA), ensuring the inputs (username/password) remain ${{ secrets.DOCKER_USERNAME }} and ${{ secrets.DOCKER_PASSWORD }} and that the "Log in to GitHub Container Registry" step continues to specify registry: ghcr.io and use ${{ github.actor }} / ${{ secrets.GITHUB_TOKEN }} so both login steps use docker/login-action consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/release-pipeline.yml:
- Around line 1661-1672: Replace the mixed Docker login actions with a single
consistent provider: update the "Log in to Docker Hub" step that currently uses
step-security/docker-login-action to use the same action provider as the GHCR
step (docker/login-action) with an appropriate SHA/pinned version (matching the
GHCR step's c94ce9fb4685... or other vetted SHA), ensuring the inputs
(username/password) remain ${{ secrets.DOCKER_USERNAME }} and ${{
secrets.DOCKER_PASSWORD }} and that the "Log in to GitHub Container Registry"
step continues to specify registry: ghcr.io and use ${{ github.actor }} / ${{
secrets.GITHUB_TOKEN }} so both login steps use docker/login-action
consistently.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: afbf4b51-f177-4612-be92-8e23bc82efb2
📒 Files selected for processing (1)
.github/workflows/release-pipeline.yml
e0d9b87 to
d68f2ab
Compare
5039c19 to
8715758
Compare
|
Pushed
Outside-diff coderabbitai comment about decoupling Docker Hub publishing from GHCR publishing — happy to discuss in a follow-up; current scope is the additive GHCR mirror within the existing release pipeline. A full decouple (separate jobs / separate triggers) is a larger structural change I'd want maintainer direction on before scoping. |
4f1c80e to
c581cb4
Compare
|
prefect thank you - ill get it merged tomorrow |
939d662 to
98d5a4c
Compare
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
Adds ghcr.io as an additional publishing target alongside Docker Hub.
Docker Hub publishing is unchanged; this is strictly additive.
Changes in .github/workflows/release-pipeline.yml + the two manifest
helper scripts (create-docker-manifest.sh, create-docker-manifest-ubi9.sh):
- Four Docker build jobs (amd64, arm64, ubi9-amd64, ubi9-arm64) gain
packages: write permission, a ghcr.io:443 egress allow-list entry,
and a "Log in to GitHub Container Registry" step using GITHUB_TOKEN
after the existing Docker Hub login. Tag step emits a matching
ghcr.io/${{ github.repository }} per-arch tag (lowercased), so
docker/build-push-action pushes to both registries in one build.
- Two manifest jobs (docker-manifest, docker-manifest-ubi9) gain
packages: write and the same ghcr login + egress entry.
- Both manifest scripts append a GITHUB_REPOSITORY-guarded block after
the Docker Hub manifest push that builds and pushes the same
multi-arch manifest tags to ghcr.io. The Docker Hub block is
untouched; the GHCR block is wrapped in a subshell with a warning
fallback so a GHCR failure cannot regress Docker Hub publish.
Manifest digest selector matches by platform.architecture (jq
'.manifests[] | select(.platform.architecture == "amd64") | .digest')
instead of positional [0], since buildx with default provenance
produces an OCI index containing both the platform image manifest and
a provenance attestation manifest with no guaranteed ordering.
A header comment in create-docker-manifest.sh documents the one-time
maintainer step required after first publish (GHCR packages default to
private; the REST API does not expose a visibility-PATCH endpoint for
container packages, so the flip to Public has to be done once in the
package settings UI).
Squashed from 2 commits.
98d5a4c to
993a196
Compare
Adds ghcr.io as an additional publishing target alongside Docker Hub.
Docker Hub publishing is unchanged; this is strictly additive.
Changes in .github/workflows/release-pipeline.yml + the two manifest
helper scripts (create-docker-manifest.sh, create-docker-manifest-ubi9.sh):
- Four Docker build jobs (amd64, arm64, ubi9-amd64, ubi9-arm64) gain
packages: write permission, a ghcr.io:443 egress allow-list entry,
and a "Log in to GitHub Container Registry" step using GITHUB_TOKEN
after the existing Docker Hub login. Tag step emits a matching
ghcr.io/${{ github.repository }} per-arch tag (lowercased), so
docker/build-push-action pushes to both registries in one build.
- Two manifest jobs (docker-manifest, docker-manifest-ubi9) gain
packages: write and the same ghcr login + egress entry.
- Both manifest scripts append a GITHUB_REPOSITORY-guarded block after
the Docker Hub manifest push that builds and pushes the same
multi-arch manifest tags to ghcr.io. The Docker Hub block is
untouched; the GHCR block is wrapped in a subshell with a warning
fallback so a GHCR failure cannot regress Docker Hub publish.
Manifest digest selector matches by platform.architecture (jq
'.manifests[] | select(.platform.architecture == "amd64") | .digest')
instead of positional [0], since buildx with default provenance
produces an OCI index containing both the platform image manifest and
a provenance attestation manifest with no guaranteed ordering.
A header comment in create-docker-manifest.sh documents the one-time
maintainer step required after first publish (GHCR packages default to
private; the REST API does not expose a visibility-PATCH endpoint for
container packages, so the flip to Public has to be done once in the
package settings UI).
Squashed from 2 commits.
Co-authored-by: Justin Wood <woody@apple.com>
Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
Summary
This PR adds GitHub Container Registry (ghcr.io) as an additional publishing target alongside Docker Hub.
Motivation
Docker Hub's rate limiting (100 pulls/6hrs anonymous, 200 free) increasingly impacts CI/CD and self-hosted infrastructure. ghcr.io provides no rate limits for public images, a unified code+containers ecosystem, needs no extra secrets (uses the existing GITHUB_TOKEN), and reuses the same build — just an additional registry target.
Changes
All changes are in
.github/workflows/release-pipeline.ymland its two manifest helper scripts; they are strictly additive and leave the existing Docker Hub build logic, tags, platforms, cache, and build args unchanged..github/workflows/release-pipeline.yml:docker-build-amd64,docker-build-arm64,docker-build-ubi9-amd64,docker-build-ubi9-arm64) gainpackages: writepermission, aghcr.io:443egress allow-list entry, and a "Log in to GitHub Container Registry" step (docker/login-action@v3, registryghcr.io, usernamegithub.actor, passwordsecrets.GITHUB_TOKEN) immediately after the existing Docker Hub login.ghcr.io/${{ github.repository }}per-arch tag (lowercased) alongside the unchanged Docker Hub tag, sodocker/build-push-actionpushes to both registries in one build.docker-manifest,docker-manifest-ubi9) gainpermissions: contents: read / packages: write, aghcr.io:443egress entry, and the same ghcr login step after the Docker Hub login..github/workflows/scripts/create-docker-manifest.shandcreate-docker-manifest-ubi9.sh: after the existing Docker Hub manifest creation/push, append an additive,GITHUB_REPOSITORY-guarded block that assembles and pushes the same multi-arch manifest tags (:vX.Y.Z,:latest,:vX.Y.Z-ubi9,:latest-ubi9per the existing stable-version rules) toghcr.io. The Docker Hub manifest logic is untouched.Backward Compatibility
Fully backward compatible — Docker Hub publishing is unchanged; this only adds an additional registry target.
Testing
🤖 Generated with Claude Code
Summary by CodeRabbit
🔧 One-time maintainer step: make the GHCR package public
Heads-up for maintainers: the first time this workflow publishes to
ghcr.io/maximhq/bifrost, GitHub creates the package as private by default. To let usersdocker pullit without authentication, a maintainer needs to set its visibility to Public once:It's a one-time action — subsequent pushes inherit the setting. (Flagged by an automated reviewer; surfacing it here so the rollout is smooth.)