88 required : true
99 type : string
1010 schedule :
11- - cron : ' 0 0 * * *' # Daily at midnight UTC
12-
11+ - cron : ' 0 0 * * *' # Daily at midnight UTC
1312env :
1413 # Define images to mirror (format: source -> mirrored to ghcr.io/$REPO/docker.io/source)
1514 IMAGES : |
@@ -35,24 +34,21 @@ jobs:
3534 MATRIX=$(echo "$IMAGES" | grep -v '^$' | jq -R -s -c 'split("\n") | map(select(length > 0))')
3635 echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
3736 fi
38-
3937 mirror :
4038 needs : prepare
4139 runs-on : ubuntu-latest
4240 permissions :
4341 packages : write
4442 strategy :
4543 fail-fast : false
46- max-parallel : 4 # Parallel image mirroring
44+ max-parallel : 4 # Parallel image mirroring
4745 matrix :
4846 image : ${{ fromJson(needs.prepare.outputs.matrix) }}
4947 steps :
5048 - name : Setup crane
5149 uses : imjasonh/setup-crane@v0.4
52-
5350 - name : Login to GHCR
5451 run : crane auth login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
55-
5652 - name : Login to Docker Hub (optional, increases rate limits)
5753 env :
5854 DOCKERHUB_USERNAME : ${{ secrets.DOCKERHUB_USERNAME }}
@@ -62,12 +58,11 @@ jobs:
6258 crane auth login docker.io -u "$DOCKERHUB_USERNAME" -p "$DOCKERHUB_TOKEN"
6359 fi
6460 continue-on-error : true
65-
6661 - name : Mirror ${{ matrix.image }}
6762 run : |
6863 SOURCE="docker.io/${{ matrix.image }}"
6964 DEST="ghcr.io/${{ github.repository }}/docker.io/${{ matrix.image }}"
70-
65+
7166 # Retry with exponential backoff
7267 retry() {
7368 local max=5 delay=30 attempt=1
@@ -78,35 +73,35 @@ jobs:
7873 done
7974 return 1
8075 }
81-
76+
8277 # Copy single tag if digest differs
8378 copy_tag() {
8479 local tag="$1"
8580 local src_digest dst_digest
86-
81+
8782 src_digest=$(retry crane digest "$SOURCE:$tag" 2>/dev/null) || return 1
8883 dst_digest=$(crane digest "$DEST:$tag" 2>/dev/null) || dst_digest=""
89-
84+
9085 if [ "$src_digest" = "$dst_digest" ]; then
9186 echo "⏭️ $tag (unchanged)"
9287 else
9388 retry crane copy "$SOURCE:$tag" "$DEST:$tag" && echo "✅ $tag"
9489 fi
9590 }
96-
91+
9792 echo "Mirroring: $SOURCE -> $DEST"
98-
93+
9994 # Get last 10 tags + ensure latest is included first
100- TAGS=$(retry crane ls "$SOURCE" | tail -10 )
95+ TAGS=$(retry crane ls "$SOURCE")
10196 if retry crane manifest "$SOURCE:latest" >/dev/null 2>&1; then
10297 TAGS=$(echo -e "latest\n$TAGS" | awk '!seen[$0]++')
10398 fi
104-
99+
105100 echo "Tags: $TAGS"
106-
101+
107102 # Export for subshells
108103 export SOURCE DEST
109104 export -f retry copy_tag
110-
105+
111106 # Copy in parallel
112107 echo "$TAGS" | xargs -P4 -I{} bash -c 'copy_tag "{}"'
0 commit comments