Update All Images [11] #11
This file contains 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
name: Update Latest Images | |
run-name: ${{ github.event_name == 'schedule' && format('Daily Update [{0}]', github.run_number) || github.event.inputs.specific_image != '' && format('Update {0} [{1}]', github.event.inputs.specific_image, github.run_number) || format('Update All Images [{0}]', github.run_number) }} | |
on: | |
schedule: | |
# Run daily at midnight UTC | |
- cron: '0 0 * * *' | |
workflow_dispatch: | |
inputs: | |
force_update_all: | |
description: 'Force update all latest images' | |
required: false | |
default: false | |
type: boolean | |
specific_image: | |
description: 'Specific image to update (leave empty for all). No need to include dockerhub- prefix' | |
required: false | |
type: string | |
jobs: | |
find-and-update-images: | |
runs-on: ubuntu-latest | |
permissions: | |
contents: read | |
packages: write | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Find latest images | |
id: find_images | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
echo "::group::Finding Latest Images" | |
# Get all images with 'latest' tag | |
PACKAGE_NAME="dockerhub-" | |
ORG="${{ github.repository_owner }}" | |
if [[ -n "${{ github.event.inputs.specific_image }}" ]]; then | |
# If a specific image was requested | |
SPECIFIC_IMAGE="${{ github.event.inputs.specific_image }}" | |
# Strip dockerhub- prefix if user accidentally included it | |
SPECIFIC_IMAGE="${SPECIFIC_IMAGE#dockerhub-}" | |
echo "Processing specific image: ${SPECIFIC_IMAGE}" | |
API_URL="/orgs/${ORG}/packages/container/${PACKAGE_NAME}${SPECIFIC_IMAGE}" | |
echo "Querying: $API_URL" | |
# Check if specific image exists in the registry | |
gh api \ | |
-H "Accept: application/vnd.github+json" \ | |
"${API_URL}" > image_info.json | |
if [[ $? -eq 0 ]]; then | |
# Image exists | |
IMAGE_DIGEST=$(jq -r '.metadata.container.digest' image_info.json) | |
echo "Image ${SPECIFIC_IMAGE} found in registry, will update." | |
export IMAGES="${SPECIFIC_IMAGE}" | |
else | |
echo "Image ${SPECIFIC_IMAGE} not found in registry. Skipping update." | |
mkdir -p /tmp/libertyhub | |
echo "[]" > /tmp/libertyhub/images_to_update.txt | |
exit 0 | |
fi | |
else | |
# Get only existing dockerhub-* packages from GitHub Container Registry | |
echo "Fetching existing mirrored images from ghcr.io/${ORG}..." | |
# Use GitHub CLI to list all packages starting with 'dockerhub-' | |
API_URL="/orgs/${ORG}/packages?package_type=container" | |
echo "Querying: $API_URL" | |
gh api \ | |
-H "Accept: application/vnd.github+json" \ | |
"${API_URL}" > all_images.json | |
# Initialize IMAGES array | |
IMAGES=() | |
# Extract all dockerhub packages in one jq command | |
mapfile -t IMAGES < <(jq -r '.packages[] | .name | select(startswith("dockerhub-")) | sub("^dockerhub-"; "")' all_images.json) | |
# Verify we found images | |
echo "Found ${#IMAGES[@]} mirrored images to check for updates" | |
if [[ -z "$IMAGES" ]]; then | |
echo "No existing mirrored images found in GitHub Container Registry." | |
echo "No images to update." | |
mkdir -p /tmp/libertyhub | |
echo "[]" > /tmp/libertyhub/images_to_update.txt | |
exit 0 | |
fi | |
fi | |
# Save images to a file for next step | |
mkdir -p /tmp/libertyhub | |
printf "%s\n" "${IMAGES[@]}" > /tmp/libertyhub/images_to_update.txt | |
echo "Found images to update: ${IMAGES[@]}" | |
echo "::endgroup::" | |
- name: Update latest images | |
run: | | |
echo "::group::Updating Images" | |
mkdir -p /tmp/libertyhub | |
IMAGES=$(cat /tmp/libertyhub/images_to_update.txt) | |
ORG="${{ github.repository_owner }}" | |
UPDATED=0 | |
SKIPPED=0 | |
FAILED=0 | |
for IMAGE_NAME in $IMAGES; do | |
echo "Processing: $IMAGE_NAME" | |
# Source image will be from Docker Hub | |
SOURCE_IMAGE="${IMAGE_NAME}:latest" | |
TARGET_IMAGE="ghcr.io/${ORG}/dockerhub-${IMAGE_NAME}:latest" | |
echo "Pulling latest from Docker Hub: ${SOURCE_IMAGE}" | |
if docker pull "${SOURCE_IMAGE}"; then | |
# Get source digest | |
SOURCE_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${SOURCE_IMAGE}" | cut -d '@' -f 2) | |
# Tag for GitHub packages | |
docker tag "${SOURCE_IMAGE}" "${TARGET_IMAGE}" | |
echo "Pushing to GitHub Packages as ${TARGET_IMAGE}" | |
if docker push "${TARGET_IMAGE}"; then | |
UPDATED=$((UPDATED+1)) | |
TARGET_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${TARGET_IMAGE}" | cut -d '@' -f 2) | |
echo "✅ Updated ${IMAGE_NAME}:latest" | |
echo " Source Digest: ${SOURCE_DIGEST}" | |
echo " Target Digest: ${TARGET_DIGEST}" | |
else | |
FAILED=$((FAILED+1)) | |
echo "❌ Failed to push ${TARGET_IMAGE}" | |
fi | |
else | |
SKIPPED=$((SKIPPED+1)) | |
echo "⚠️ Could not pull ${SOURCE_IMAGE}, skipping" | |
fi | |
echo "-----------------------" | |
done | |
echo "::endgroup::" | |
echo "Update Summary:" | |
echo "- Updated: $UPDATED" | |
echo "- Skipped: $SKIPPED" | |
echo "- Failed: $FAILED" | |
# Set job status based on results | |
if [[ $FAILED -gt 0 ]]; then | |
exit 1 | |
fi |