feat: add Docker Hub as primary registry for anonymous pulls

GHCR requires authentication even for public packages on some systems.
CI now pushes to both GHCR and Docker Hub. docker-compose.yml and Helm
chart point to Docker Hub where anonymous pulls always work. Build
directives kept as fallback for source-based builds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
anoracleofra-code
2026-03-28 08:13:14 -06:00
co-authored by Claude Opus 4.6
parent 66df14a93c
commit a3e7a2bc6b
6 changed files with 113 additions and 43 deletions
+94 -24
View File
@@ -6,9 +6,11 @@ on:
tags: ["v*.*.*"] tags: ["v*.*.*"]
pull_request: pull_request:
branches: ["main"] branches: ["main"]
env: env:
REGISTRY: ghcr.io GHCR_REGISTRY: ghcr.io
DOCKERHUB_BACKEND: bigbodycobain/shadowbroker-backend
DOCKERHUB_FRONTEND: bigbodycobain/shadowbroker-frontend
# github.repository as <account>/<repo> # github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }} IMAGE_NAME: ${{ github.repository }}
@@ -44,19 +46,26 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0
- name: Log into registry ${{ env.REGISTRY }} - name: Log into GHCR
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v3.0.0 uses: docker/login-action@v3.0.0
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log into Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract Docker metadata - name: Extract Docker metadata
id: meta id: meta
uses: docker/metadata-action@v5.0.0 uses: docker/metadata-action@v5.0.0
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-frontend images: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-frontend
- name: Build and push Docker image by digest - name: Build and push Docker image by digest
id: build id: build
@@ -68,7 +77,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=frontend-${{ matrix.platform }} cache-from: type=gha,scope=frontend-${{ matrix.platform }}
cache-to: type=gha,mode=max,scope=frontend-${{ matrix.platform }} cache-to: type=gha,mode=max,scope=frontend-${{ matrix.platform }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-frontend,push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} outputs: type=image,name=${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-frontend,push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}
- name: Export digest - name: Export digest
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
@@ -108,29 +117,56 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0
- name: Log into registry ${{ env.REGISTRY }} - name: Log into GHCR
uses: docker/login-action@v3.0.0 uses: docker/login-action@v3.0.0
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata - name: Log into Docker Hub
id: meta uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract Docker metadata (GHCR)
id: meta-ghcr
uses: docker/metadata-action@v5.0.0 uses: docker/metadata-action@v5.0.0
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-frontend images: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-frontend
tags: | tags: |
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Create and push manifest - name: Extract Docker metadata (Docker Hub)
id: meta-hub
uses: docker/metadata-action@v5.0.0
with:
images: ${{ env.DOCKERHUB_FRONTEND }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Create and push GHCR manifest
working-directory: /tmp/digests/frontend working-directory: /tmp/digests/frontend
run: | run: |
docker buildx imagetools create \ docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-frontend@sha256:%s ' *) $(printf '${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-frontend@sha256:%s ' *)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta-ghcr.outputs.json }}
- name: Push to Docker Hub
working-directory: /tmp/digests/frontend
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-frontend@sha256:%s ' *)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta-hub.outputs.json }}
build-backend: build-backend:
needs: ci-gate needs: ci-gate
@@ -159,19 +195,26 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0
- name: Log into registry ${{ env.REGISTRY }} - name: Log into GHCR
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v3.0.0 uses: docker/login-action@v3.0.0
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log into Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract Docker metadata - name: Extract Docker metadata
id: meta id: meta
uses: docker/metadata-action@v5.0.0 uses: docker/metadata-action@v5.0.0
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backend images: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-backend
- name: Build and push Docker image by digest - name: Build and push Docker image by digest
id: build id: build
@@ -184,7 +227,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=backend-${{ matrix.platform }} cache-from: type=gha,scope=backend-${{ matrix.platform }}
cache-to: type=gha,mode=max,scope=backend-${{ matrix.platform }} cache-to: type=gha,mode=max,scope=backend-${{ matrix.platform }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backend,push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} outputs: type=image,name=${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-backend,push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}
- name: Export digest - name: Export digest
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
@@ -224,26 +267,53 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0
- name: Log into registry ${{ env.REGISTRY }} - name: Log into GHCR
uses: docker/login-action@v3.0.0 uses: docker/login-action@v3.0.0
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata - name: Log into Docker Hub
id: meta uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract Docker metadata (GHCR)
id: meta-ghcr
uses: docker/metadata-action@v5.0.0 uses: docker/metadata-action@v5.0.0
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backend images: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-backend
tags: | tags: |
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Create and push manifest - name: Extract Docker metadata (Docker Hub)
id: meta-hub
uses: docker/metadata-action@v5.0.0
with:
images: ${{ env.DOCKERHUB_BACKEND }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Create and push GHCR manifest
working-directory: /tmp/digests/backend working-directory: /tmp/digests/backend
run: | run: |
docker buildx imagetools create \ docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backend@sha256:%s ' *) $(printf '${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-backend@sha256:%s ' *)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta-ghcr.outputs.json }}
- name: Push to Docker Hub
working-directory: /tmp/digests/backend
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}-backend@sha256:%s ' *)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta-hub.outputs.json }}
+4 -7
View File
@@ -46,17 +46,14 @@ if [ ! -f "$COMPOSE_FILE" ]; then
fi fi
# Detect stale compose file from pre-migration clones (before March 2026). # Detect stale compose file from pre-migration clones (before March 2026).
# The current compose file uses "image:" to pull pre-built images from GHCR. # Old versions used ghcr.io which requires auth. Current file uses Docker Hub.
# Old versions had "build:" directives that compile from local source — much if grep -q 'ghcr\.io' "$COMPOSE_FILE" 2>/dev/null; then
# slower and will NOT pick up new releases.
if grep -q '^\s*build:' "$COMPOSE_FILE" 2>/dev/null; then
echo "" echo ""
echo "================================================================" echo "================================================================"
echo " [!] WARNING: Your docker-compose.yml is outdated." echo " [!] WARNING: Your docker-compose.yml is outdated."
echo "" echo ""
echo " It contains 'build:' directives, which means Docker is" echo " It references ghcr.io which may require authentication."
echo " compiling from local source instead of pulling pre-built" echo " The current version uses Docker Hub for anonymous pulls."
echo " images. You will NOT receive updates this way."
echo "" echo ""
echo " Fix: re-clone the repository:" echo " Fix: re-clone the repository:"
echo " cd .. && rm -rf $(basename "$SCRIPT_DIR")" echo " cd .. && rm -rf $(basename "$SCRIPT_DIR")"
+7 -2
View File
@@ -1,6 +1,9 @@
services: services:
backend: backend:
image: ghcr.io/bigbodycobain/shadowbroker-backend:latest image: bigbodycobain/shadowbroker-backend:latest
build:
context: .
dockerfile: backend/Dockerfile
container_name: shadowbroker-backend container_name: shadowbroker-backend
ports: ports:
- "${BIND:-127.0.0.1}:8000:8000" - "${BIND:-127.0.0.1}:8000:8000"
@@ -33,7 +36,9 @@ services:
cpus: '2' cpus: '2'
frontend: frontend:
image: ghcr.io/bigbodycobain/shadowbroker-frontend:latest image: bigbodycobain/shadowbroker-frontend:latest
build:
context: ./frontend
container_name: shadowbroker-frontend container_name: shadowbroker-frontend
ports: ports:
- "${BIND:-127.0.0.1}:3000:3000" - "${BIND:-127.0.0.1}:3000:3000"
+2 -2
View File
@@ -13,7 +13,7 @@ shadowbroker:
runAsGroup: 1001 runAsGroup: 1001
image: image:
pullPolicy: Always pullPolicy: Always
repository: ghcr.io/bigbodycobain/shadowbroker-backend repository: bigbodycobain/shadowbroker-backend
tag: latest tag: latest
env: env:
AIS_API_KEY: AIS_API_KEY:
@@ -41,7 +41,7 @@ shadowbroker:
runAsGroup: 1001 runAsGroup: 1001
image: image:
pullPolicy: Always pullPolicy: Always
repository: ghcr.io/bigbodycobain/shadowbroker-frontend repository: bigbodycobain/shadowbroker-frontend
tag: latest tag: latest
env: env:
+3 -4
View File
@@ -7,15 +7,14 @@ echo ===================================================
echo. echo.
:: Check for stale docker-compose.yml from pre-migration clones :: Check for stale docker-compose.yml from pre-migration clones
findstr /R /C:"build:" docker-compose.yml >nul 2>&1 findstr /C:"ghcr.io" docker-compose.yml >nul 2>&1
if %errorlevel% equ 0 ( if %errorlevel% equ 0 (
echo. echo.
echo ================================================================ echo ================================================================
echo [!] WARNING: Your docker-compose.yml is outdated. echo [!] WARNING: Your docker-compose.yml is outdated.
echo. echo.
echo It contains 'build:' directives, which means Docker will echo It references ghcr.io which may require authentication.
echo compile from local source instead of pulling pre-built images. echo The current version uses Docker Hub for anonymous pulls.
echo You will NOT receive updates this way.
echo. echo.
echo If you use Docker, re-clone the repository: echo If you use Docker, re-clone the repository:
echo git clone https://github.com/BigBodyCobain/Shadowbroker.git echo git clone https://github.com/BigBodyCobain/Shadowbroker.git
+3 -4
View File
@@ -10,14 +10,13 @@ echo ""
# Check for stale docker-compose.yml from pre-migration clones # Check for stale docker-compose.yml from pre-migration clones
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ -f "$SCRIPT_DIR/docker-compose.yml" ] && grep -q '^\s*build:' "$SCRIPT_DIR/docker-compose.yml" 2>/dev/null; then if [ -f "$SCRIPT_DIR/docker-compose.yml" ] && grep -q 'ghcr\.io' "$SCRIPT_DIR/docker-compose.yml" 2>/dev/null; then
echo "" echo ""
echo "================================================================" echo "================================================================"
echo " [!] WARNING: Your docker-compose.yml is outdated." echo " [!] WARNING: Your docker-compose.yml is outdated."
echo "" echo ""
echo " It contains 'build:' directives, which means Docker will" echo " It references ghcr.io which may require authentication."
echo " compile from local source instead of pulling pre-built images." echo " The current version uses Docker Hub for anonymous pulls."
echo " You will NOT receive updates this way."
echo "" echo ""
echo " If you use Docker, re-clone the repository:" echo " If you use Docker, re-clone the repository:"
echo " git clone https://github.com/BigBodyCobain/Shadowbroker.git" echo " git clone https://github.com/BigBodyCobain/Shadowbroker.git"