Skip to content

sync-gpustack-images #220

sync-gpustack-images

sync-gpustack-images #220

name: sync-gpustack-images
on:
schedule:
# Weekly: sync new dev images (diff against the latest release)
- cron: "25 18 * * 0"
# Every 4 hours: sync only gpustack/gpustack:dev
- cron: "45 */4 * * *"
workflow_dispatch:
inputs:
dev:
description: "Whether to sync dev images instead of release images (default: false)"
required: false
default: false
type: boolean
docker_login:
description: "Log in to Docker Hub to raise the pull rate limit (default: false)"
required: false
default: false
type: boolean
full_sync:
description: "Sync all images of the target tag instead of only the diff (default: false)"
required: false
default: false
type: boolean
force_sync:
description: "Comma-separated images to force sync, e.g. gpustack:dev,mirrored-node-feature-discovery:v0.18.3 (gpustack/ namespace is prepended automatically)"
required: false
default: ""
type: string
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
sync-images:
name: Compare and Sync Images
runs-on: ubuntu-latest
steps:
- name: 🏷️ Fetch Latest Tags
id: get_tags
run: |
# Fetch the latest two release tags from the repository
TAGS=$(curl -s -H "Authorization: Bearer ${{ github.token }}" "https://api.github.com/repos/gpustack/gpustack/releases?page=1&per_page=2" | grep '"tag_name":' | awk -F'"' '{print $4}')
LATEST_TAG=$(echo "$TAGS" | head -n 1)
SECOND_LATEST_TAG=$(echo "$TAGS" | sed -n '2p')
if [[ "${{ inputs.dev }}" == 'true' || "${{ github.event_name }}" == 'schedule' ]]; then
SECOND_LATEST_TAG=$LATEST_TAG
LATEST_TAG=dev
fi
echo "🚀 Latest tag: $LATEST_TAG"
echo "⏮️ Second latest tag: $SECOND_LATEST_TAG"
echo "LATEST_TAG=$LATEST_TAG" >> "$GITHUB_ENV"
echo "SECOND_LATEST_TAG=$SECOND_LATEST_TAG" >> "$GITHUB_ENV"
- name: 🔍 Determine Images to Sync
id: get_images
run: |
# Build the list of images to sync based on the trigger and inputs
if [[ "${{ github.event_name }}" == 'schedule' && "${{ github.event.schedule }}" == '45 */4 * * *' ]]; then
# Every-4-hours schedule: only sync gpustack/gpustack:dev
IMAGES_TO_SYNC="gpustack/gpustack:dev"
elif [ -n "${{ inputs.force_sync }}" ]; then
# Force sync: only the specified images (comma-separated name:tag, gpustack/ namespace prepended)
IMAGES_TO_SYNC=$(echo "${{ inputs.force_sync }}" | tr ',' '\n' | \
sed 's/^[[:space:]]*//; s/[[:space:]]*$//' | sed '/^$/d' | sed 's#^#gpustack/#' | awk '!seen[$0]++')
echo "🔧 Force-sync images requested:"
echo "$IMAGES_TO_SYNC"
elif [[ "${{ inputs.full_sync }}" == 'true' ]]; then
# Full sync: all images of the target tag
IMAGES_TO_SYNC=$(docker run -q --rm --entrypoint gpustack gpustack/gpustack:"$LATEST_TAG" list-images | uniq || true)
else
# Default: diff between the two tags
IMAGES_TO_SYNC=$(grep -Fvf \
<(docker run -q --rm --entrypoint gpustack gpustack/gpustack:"$SECOND_LATEST_TAG" list-images) \
<(docker run -q --rm --entrypoint gpustack gpustack/gpustack:"$LATEST_TAG" list-images) | \
uniq || true)
fi
# On release syncs (manual dispatch with dev=false), also sync gpustack/gpustack:latest.
# Schedules set LATEST_TAG=dev, so they are excluded; force_sync uses an explicit list.
if [[ "$LATEST_TAG" != 'dev' && -z "${{ inputs.force_sync }}" ]]; then
echo "🏷️ Release sync: including gpustack/gpustack:latest"
IMAGES_TO_SYNC=$(printf '%s\n' "$IMAGES_TO_SYNC" "gpustack/gpustack:latest" | sed '/^$/d' | awk '!seen[$0]++')
fi
if [ -z "$IMAGES_TO_SYNC" ]; then
echo "✅ No new images found for synchronization."
echo "HAS_NEW_IMAGES=false" >> "$GITHUB_ENV"
else
echo "📦 Images identified for synchronization:"
echo "$IMAGES_TO_SYNC"
echo "HAS_NEW_IMAGES=true" >> "$GITHUB_ENV"
{
echo "IMAGES_TO_SYNC<<EOF"
echo "$IMAGES_TO_SYNC"
echo "EOF"
} >> "$GITHUB_ENV"
# Initialize the Job Summary header
echo "### 🔄 Image Synchronization Report" >> "$GITHUB_STEP_SUMMARY"
echo "The following images were identified and synced:" >> "$GITHUB_STEP_SUMMARY"
fi
- name: 🔑 Login to Docker Hub (Improve API Limits)
if: env.HAS_NEW_IMAGES == 'true' && inputs.docker_login == 'true'
run: |
# Log in to Docker Hub to raise the pull rate limit
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USER }}" docker.io --password-stdin
- name: 🔄 Sync to Quay.io
if: env.HAS_NEW_IMAGES == 'true'
run: |
# Sync each identified image to Quay.io
echo "${{ secrets.DOCKER_QUAY_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_QUAY_USER }}" quay.io --password-stdin
echo "#### 🟢 Quay.io" >> "$GITHUB_STEP_SUMMARY"
echo "$IMAGES_TO_SYNC" | while read -r image; do
if [ -z "$image" ]; then continue; fi
echo "➡️ Syncing: $image"
skopeo copy --all --retry-times 3 "docker://${image}" "docker://quay.io/${image}"
# Append success status to Job Summary
echo "- ✅ \`$image\`" >> "$GITHUB_STEP_SUMMARY"
done
- name: 🔄 Sync to SWR
if: env.HAS_NEW_IMAGES == 'true'
run: |
# Sync each identified image to SWR
echo "${{ secrets.DOCKER_SWR_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_SWR_USER }}" "${{ secrets.DOCKER_SWR_REGISTRY }}" --password-stdin
echo "#### 🔵 SWR" >> "$GITHUB_STEP_SUMMARY"
echo "$IMAGES_TO_SYNC" | while read -r image; do
if [ -z "$image" ]; then continue; fi
echo "➡️ Syncing: $image"
skopeo copy --all --retry-times 3 "docker://${image}" "docker://${{ secrets.DOCKER_SWR_REGISTRY }}/${image}"
echo "- ✅ \`$image\`" >> "$GITHUB_STEP_SUMMARY"
done