Add dispatch workflow to update the latest tag

This commit is contained in:
Felipe Knorr Kuhn 2025-02-28 23:22:00 -08:00
parent 5116da2626
commit 9c358060aa
No known key found for this signature in database
GPG key ID: 79619B52BB097C1A

View file

@ -0,0 +1,181 @@
name: Docker - Update latest tag
on:
workflow_dispatch:
inputs:
tag:
description: 'The Docker image tag to pull'
required: true
type: string
jobs:
retag-and-push:
strategy:
matrix:
service:
- frontend
- backend
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
id: buildx
with:
install: true
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: linux/amd64,linux/arm64
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get source image manifest and SHAs
id: source-manifest
run: |
set -e
echo "Fetching source manifest..."
MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }})
if [ -z "$MANIFEST" ]; then
echo "No manifest found. Assuming single-arch image."
exit 1
fi
echo "Original source manifest:"
echo "$MANIFEST" | jq .
AMD64_SHA=$(echo "$MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest')
ARM64_SHA=$(echo "$MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="arm64" and .platform.os=="linux") | .digest')
if [ -z "$AMD64_SHA" ] || [ -z "$ARM64_SHA" ]; then
echo "Source image is not multi-arch (missing amd64 or arm64)"
exit 1
fi
echo "Source amd64 manifest digest: $AMD64_SHA"
echo "Source arm64 manifest digest: $ARM64_SHA"
echo "amd64_sha=$AMD64_SHA" >> $GITHUB_OUTPUT
echo "arm64_sha=$ARM64_SHA" >> $GITHUB_OUTPUT
- name: Pull and retag architecture-specific images
run: |
set -e
docker buildx inspect --bootstrap
# Remove any existing local images to avoid cache interference
echo "Removing existing local images if they exist..."
docker image rm ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }} || true
# Pull amd64 image by digest
echo "Pulling amd64 image by digest..."
docker pull --platform linux/amd64 ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }}
PULLED_AMD64_MANIFEST_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
PULLED_AMD64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} --format '{{.Id}}')
echo "Pulled amd64 manifest digest: $PULLED_AMD64_MANIFEST_DIGEST"
echo "Pulled amd64 image ID (sha256): $PULLED_AMD64_IMAGE_ID"
# Pull arm64 image by digest
echo "Pulling arm64 image by digest..."
docker pull --platform linux/arm64 ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }}
PULLED_ARM64_MANIFEST_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
PULLED_ARM64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} --format '{{.Id}}')
echo "Pulled arm64 manifest digest: $PULLED_ARM64_MANIFEST_DIGEST"
echo "Pulled arm64 image ID (sha256): $PULLED_ARM64_IMAGE_ID"
# Tag the images
docker tag ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64
docker tag ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }} ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64
# Verify tagged images
TAGGED_AMD64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 --format '{{.Id}}')
TAGGED_ARM64_IMAGE_ID=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 --format '{{.Id}}')
echo "Tagged amd64 image ID (sha256): $TAGGED_AMD64_IMAGE_ID"
echo "Tagged arm64 image ID (sha256): $TAGGED_ARM64_IMAGE_ID"
- name: Push architecture-specific images
run: |
set -e
echo "Pushing amd64 image..."
docker push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64
PUSHED_AMD64_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
echo "Pushed amd64 manifest digest (local): $PUSHED_AMD64_DIGEST"
# Fetch manifest from registry after push
echo "Fetching pushed amd64 manifest from registry..."
PUSHED_AMD64_REGISTRY_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64)
PUSHED_AMD64_REGISTRY_DIGEST=$(echo "$PUSHED_AMD64_REGISTRY_MANIFEST" | jq -r '.config.digest')
echo "Pushed amd64 manifest digest (registry): $PUSHED_AMD64_REGISTRY_DIGEST"
echo "Pushing arm64 image..."
docker push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64
PUSHED_ARM64_DIGEST=$(docker inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 --format '{{index .RepoDigests 0}}' | cut -d@ -f2)
echo "Pushed arm64 manifest digest (local): $PUSHED_ARM64_DIGEST"
# Fetch manifest from registry after push
echo "Fetching pushed arm64 manifest from registry..."
PUSHED_ARM64_REGISTRY_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64)
PUSHED_ARM64_REGISTRY_DIGEST=$(echo "$PUSHED_ARM64_REGISTRY_MANIFEST" | jq -r '.config.digest')
echo "Pushed arm64 manifest digest (registry): $PUSHED_ARM64_REGISTRY_DIGEST"
- name: Create and push multi-arch manifest with original digests
run: |
set -e
echo "Creating multi-arch manifest with original digests..."
docker manifest create ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest \
${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.amd64_sha }} \
${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}@${{ steps.source-manifest.outputs.arm64_sha }}
echo "Pushing multi-arch manifest..."
docker manifest push ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest
- name: Clean up intermediate tags
if: success()
run: |
docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-amd64 || true
docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest-arm64 || true
docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:${{ github.event.inputs.tag }} || true
- name: Verify final manifest
run: |
set -e
echo "Fetching final generated manifest..."
FINAL_MANIFEST=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest)
echo "Generated final manifest:"
echo "$FINAL_MANIFEST" | jq .
FINAL_AMD64_SHA=$(echo "$FINAL_MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest')
FINAL_ARM64_SHA=$(echo "$FINAL_MANIFEST" | jq -r '.manifests[] | select(.platform.architecture=="arm64" and .platform.os=="linux") | .digest')
echo "Final amd64 manifest digest: $FINAL_AMD64_SHA"
echo "Final arm64 manifest digest: $FINAL_ARM64_SHA"
# Compare all digests
echo "Comparing digests..."
echo "Source amd64 digest: ${{ steps.source-manifest.outputs.amd64_sha }}"
echo "Pulled amd64 manifest digest: $PULLED_AMD64_MANIFEST_DIGEST"
echo "Pushed amd64 manifest digest (local): $PUSHED_AMD64_DIGEST"
echo "Pushed amd64 manifest digest (registry): $PUSHED_AMD64_REGISTRY_DIGEST"
echo "Final amd64 digest: $FINAL_AMD64_SHA"
echo "Source arm64 digest: ${{ steps.source-manifest.outputs.arm64_sha }}"
echo "Pulled arm64 manifest digest: $PULLED_ARM64_MANIFEST_DIGEST"
echo "Pushed arm64 manifest digest (local): $PUSHED_ARM64_DIGEST"
echo "Pushed arm64 manifest digest (registry): $PUSHED_ARM64_REGISTRY_DIGEST"
echo "Final arm64 digest: $FINAL_ARM64_SHA"
if [ "$FINAL_AMD64_SHA" != "${{ steps.source-manifest.outputs.amd64_sha }}" ] || [ "$FINAL_ARM64_SHA" != "${{ steps.source-manifest.outputs.arm64_sha }}" ]; then
echo "Error: Final manifest SHAs do not match source SHAs"
exit 1
fi
echo "Successfully created multi-arch ${{ secrets.DOCKER_USERNAME }}/${{ matrix.service }}:latest from ${{ github.event.inputs.tag }}"