Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Workflow for Multi-Architecture Docker Image Build, Push, and Manifest Management #535

Draft
wants to merge 21 commits into
base: develop
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 100 additions & 94 deletions .github/workflows/docker-hub-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
branches:
- master
- develop
- multiArchitectureFeature
tags:
- 'v*.*.*'

Expand All @@ -14,7 +15,9 @@ concurrency:
cancel-in-progress: true

env:
IMAGE_NAME: index.docker.io/metacall/core
DOCKER_REGISTRY: index.docker.io
DOCKER_USERNAME: mryash
IMAGE_NAME: core
BUILDKIT_VERSION: 0.13.0

jobs:
Expand All @@ -26,41 +29,31 @@ jobs:
matrix:
platform:
- linux/amd64
- linux/arm64
- linux/riscv64
- linux/ppc64le
- linux/s390x
# - linux/arm64
# - linux/riscv64
# - linux/ppc64le
# - linux/s390x
- linux/386
- linux/arm/v7
- linux/arm/v6
# - linux/arm/v7
# - linux/arm/v6
# - linux/mips64le
# - linux/mips64

steps:
- name: Checkout the code
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Docker Setup BuildX
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v3
with:
version: v${{ env.BUILDKIT_VERSION }}

- name: Verify Docker BuildX Version
run: docker buildx version

- name: Authenticate to Docker registry
if: github.event_name != 'pull_request'
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
Expand All @@ -72,93 +65,106 @@ jobs:
run: |
./docker-compose.sh platform

# - name: Generate images
# if: github.event_name != 'pull_request'
# run: |
# for tag in "deps" "dev" "runtime" "cli"; do
# mkdir -p "/tmp/images/${tag}"
# digest="$(docker images --no-trunc --quiet metacall/core:${tag})"
# echo "FROM metacall/core:${tag}@${digest}" &> "/tmp/images/${tag}/Dockerfile"
# done

# - name: Build and push by digest (deps)
# id: build
# uses: docker/build-push-action@v6
# if: github.event_name != 'pull_request'
# with:
# context: /tmp/images/deps/Dockerfile
# platforms: ${{ matrix.platform }}
# labels: ${{ steps.meta.outputs.labels }}
# outputs: type=image,name=docker.io/${{ env.IMAGE_NAME }}:deps,push-by-digest=true,name-canonical=true,push=true

- name: Export digests
if: github.event_name != 'pull_request'
- name: List Docker Images
run: docker images

- name: Tag Platform Images
run: |
PLATFORM=${{ matrix.platform }}
echo "PLATFORM=${PLATFORM//\//-}" >> $GITHUB_ENV
platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-')
echo "Platform Tag: ${platform_tag}"
for tag in "deps" "dev" "runtime" "cli"; do
mkdir -p "/tmp/digests/${tag}"
digest="$(docker images --no-trunc --quiet metacall/core:${tag})"
touch "/tmp/digests/${tag}/${digest#sha256:}"
docker tag ${DOCKER_USERNAME}/${IMAGE_NAME}:${tag} \
${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag}
done

- name: Upload digests
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1

merge:
name: Merge digests for the manifest
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
needs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Create Test Dockerfile
run: |
cat <<EOF > Dockerfile
FROM ${DOCKER_USERNAME}/${IMAGE_NAME}:cli
RUN echo "console.log('abcde')" > script.js
RUN metacallcli script.js
EOF

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: v${{ env.BUILDKIT_VERSION }}
- name: Build and Test Image
run: |
set -exuo pipefail
platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-')
docker build --platform ${{ matrix.platform }} -t test-image .
docker run --rm --platform=${{ matrix.platform }} test-image | grep "abcde"

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}
- name: Push Platform Images
run: |
platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-')
for tag in "deps" "dev" "runtime" "cli"; do
echo "Pushing image for tag: ${tag} with platform: ${platform_tag}"
docker push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag}
done

- name: Authenticate to Docker registry
manifest:
name: Create and Push Manifest Lists
needs: build
runs-on: ubuntu-latest
steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

- name: Create manifest list and push
if: github.ref == 'refs/heads/master' || contains(github.ref, 'refs/tags/')
- name: Create and Push Manifest Lists
run: |
for tag in "deps" "dev" "runtime" "cli"; do
cd "/tmp/digests/${tag}"
IMAGE_HASHES=$(printf '${{ env.IMAGE_NAME }}:${tag}@sha256:%s ' *)
for image in ${IMAGE_HASHES}; do
docker image tag ${image} ${{ env.IMAGE_NAME }}:${tag}
docker push ${{ env.IMAGE_NAME }}:${tag}
echo "Creating manifest for tag: $tag"
platform_tags=""
platforms=("linux-amd64" "linux-arm64" "linux-riscv64" "linux-ppc64le" "linux-s390x" "linux-386" "linux-arm-v7" "linux-arm-v6")
for platform in "${platforms[@]}"; do
platform_tags="${platform_tags} ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform}"
done
docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag} ${platform_tags} --amend
docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}
done

- name: Create Version Specific Tags
if: startsWith(github.ref, 'refs/tags/')
run: |
VERSION=${GITHUB_REF#refs/tags/v}
tags=("deps" "dev" "runtime" "cli")
platforms=("linux-amd64" "linux-arm64" "linux-riscv64" "linux-ppc64le" "linux-s390x" "linux-386" "linux-arm-v7" "linux-arm-v6")
for tag in "${tags[@]}"; do
platform_tags=""
for platform in "${platforms[@]}"; do
platform_tags="${platform_tags} ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform}"
done
docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${VERSION}-${tag} ${platform_tags} --amend
docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${VERSION}-${tag}
done

# Create and push the 'latest' tag for CLI
cli_platform_tags=""
for platform in ${{ matrix.platform }}; do
cli_platform_tags="${cli_platform_tags} ${DOCKER_USERNAME}/${IMAGE_NAME}:cli-${platform}"
done
docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:latest ${cli_platform_tags} --amend
docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:latest

cleanup:
name: Cleanup Platform Specific Tags
needs: [build, manifest]
runs-on: ubuntu-latest
if: always()
steps:
- name: Remove Platform-Specific Tags
run: |
platforms=("linux-amd64" "linux-arm64" "linux-riscv64" "linux-ppc64le" "linux-s390x" "linux-386" "linux-arm-v7" "linux-arm-v6")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, can't you get this directly from matrix.platform?

tags=("deps" "dev" "runtime" "cli")

for platform in "${platforms[@]}"; do
for tag in "${tags[@]}"; do
tag_to_delete="${tag}-${platform}"
echo "Deleting tag: ${tag_to_delete}"

curl -X DELETE \
-H "Authorization: Bearer ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}" \
"https://hub.docker.com/v2/repositories/${DOCKER_USERNAME}/${IMAGE_NAME}/tags/${tag_to_delete}/"
done
docker buildx imagetools create -t ${{ env.IMAGE_NAME }}:${tag} ${IMAGE_HASHES}
if [[ "${tag}" = "cli" ]]; then
docker buildx imagetools create -t ${{ env.IMAGE_NAME }}:latest ${IMAGE_HASHES}
if [[ "${{ contains(github.ref, 'refs/tags/') }}" = true ]]; then
TAG=${GITHUB_REF#refs/*/}
VERSION=${TAG#v}
docker buildx imagetools create -t ${{ env.IMAGE_NAME }}:${VERSION} ${IMAGE_HASHES}
fi
fi
done
Loading