Skip to content

Commit 838ccae

Browse files
cevhericlaude
andauthored
ci(docker): mirror image to Docker Hub alongside GHCR (#59)
Publish the same multi-arch image and tags to docker.io in the same buildx run, for discoverability (docker pull libredb/libredb-studio). GHCR stays canonical (no pull rate limits; referenced by Helm, CapRover and compose). Docker Hub is opt-in: it is only targeted when the DOCKER_HUB_TOKEN secret and DOCKER_HUB_USERNAME variable are set, so forks without them still build and push to GHCR cleanly. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 57a28e8 commit 838ccae

1 file changed

Lines changed: 47 additions & 3 deletions

File tree

.github/workflows/docker-build-push.yml

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,18 +69,62 @@ jobs:
6969
username: ${{ github.repository_owner }}
7070
password: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}
7171

72+
# Docker Hub is an optional secondary mirror for discoverability. It is
73+
# only used when DOCKER_HUB_TOKEN is configured (so forks without the
74+
# secret still build/push to GHCR cleanly).
75+
- name: Check Docker Hub availability
76+
id: dockerhub
77+
env:
78+
DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }}
79+
run: |
80+
if [ -n "$DOCKER_HUB_TOKEN" ]; then
81+
echo "enabled=true" >> "$GITHUB_OUTPUT"
82+
else
83+
echo "enabled=false" >> "$GITHUB_OUTPUT"
84+
fi
85+
86+
- name: Log in to Docker Hub
87+
if: steps.dockerhub.outputs.enabled == 'true'
88+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
89+
with:
90+
username: ${{ vars.DOCKER_HUB_USERNAME }}
91+
password: ${{ secrets.DOCKER_HUB_TOKEN }}
92+
7293
- name: Prepare image name (lowercase)
7394
id: image
7495
run: |
7596
IMAGE_NAME_LOWER=$(echo "${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')
7697
echo "name=${IMAGE_NAME_LOWER}" >> $GITHUB_OUTPUT
7798
echo "Image name (lowercase): ${IMAGE_NAME_LOWER}"
78-
99+
100+
# Build the list of target images. GHCR is always included; Docker Hub
101+
# (docker.io/<DOCKER_HUB_USERNAME>/<repo>) is added only when enabled.
102+
- name: Compose registry image list
103+
id: images
104+
env:
105+
GHCR_IMAGE: ${{ env.REGISTRY }}/${{ steps.image.outputs.name }}
106+
REPO_NAME: ${{ steps.image.outputs.name }}
107+
DH_ENABLED: ${{ steps.dockerhub.outputs.enabled }}
108+
DH_USERNAME: ${{ vars.DOCKER_HUB_USERNAME }}
109+
run: |
110+
IMAGES="$GHCR_IMAGE"
111+
if [ "$DH_ENABLED" = "true" ] && [ -n "$DH_USERNAME" ]; then
112+
SHORT_NAME="${REPO_NAME##*/}"
113+
IMAGES="$(printf '%s\ndocker.io/%s/%s' "$IMAGES" "$DH_USERNAME" "$SHORT_NAME")"
114+
fi
115+
{
116+
echo "list<<EOF"
117+
printf '%s\n' "$IMAGES"
118+
echo "EOF"
119+
} >> "$GITHUB_OUTPUT"
120+
echo "Target images:"
121+
printf '%s\n' "$IMAGES"
122+
79123
- name: Extract metadata for Docker
80124
id: meta
81125
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
82126
with:
83-
images: ${{ env.REGISTRY }}/${{ steps.image.outputs.name }}
127+
images: ${{ steps.images.outputs.list }}
84128
tags: |
85129
type=raw,value=${{ steps.extract-version.outputs.version }}
86130
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') }}
@@ -108,7 +152,7 @@ jobs:
108152
echo "" >> $GITHUB_STEP_SUMMARY
109153
echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
110154
echo "**Version:** ${{ steps.extract-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
111-
echo "**Registry:** ${{ env.REGISTRY }}" >> $GITHUB_STEP_SUMMARY
155+
echo "**Docker Hub mirror:** ${{ steps.dockerhub.outputs.enabled }}" >> $GITHUB_STEP_SUMMARY
112156
echo "**Image:** ${{ steps.image.outputs.name }}" >> $GITHUB_STEP_SUMMARY
113157
echo "" >> $GITHUB_STEP_SUMMARY
114158
echo "### Tags:" >> $GITHUB_STEP_SUMMARY

0 commit comments

Comments
 (0)