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 22 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
181 changes: 89 additions & 92 deletions .github/workflows/docker-hub-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ concurrency:
cancel-in-progress: true

env:
IMAGE_NAME: index.docker.io/metacall/core
DOCKER_REGISTRY: index.docker.io
DOCKER_USERNAME: metacall
Copy link
Member

Choose a reason for hiding this comment

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

Docker username is a secret that must be provided by GitHub secrets.

IMAGE_NAME: core
BUILDKIT_VERSION: 0.13.0

jobs:
Expand All @@ -26,41 +28,28 @@ jobs:
matrix:
platform:
- linux/amd64
- linux/386
- linux/arm64
- linux/riscv64
- linux/ppc64le
- linux/s390x
- linux/386
- 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 +61,101 @@ 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: 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 metacall/${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
- 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

merge:
name: Merge digests for the manifest
- name: Run Tests
run: |
set -exuo pipefail
platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-')
cat <<EOF > Dockerfile.test
FROM ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:cli-${platform_tag}
RUN echo "console.log('abcde')" > script.js
RUN metacallcli script.js
EOF

docker build --platform ${{ matrix.platform }} -f Dockerfile.test -t test-image .
docker run --rm --platform=${{ matrix.platform }} test-image

manifest:
name: Create and Push Manifest Lists
needs: build
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: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: v${{ env.BUILDKIT_VERSION }}

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

- name: Authenticate to Docker registry
- 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=""
for platform in "linux/amd64" "linux/386" "linux/arm64" "linux/riscv64" "linux/ppc64le" "linux/s390x" "linux/arm/v7" "linux/arm/v6"; do
platform_tag=$(echo "${platform}" | tr '/' '-')
platform_tags="${platform_tags} ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag}"
done
echo "Creating manifest with tags: ${platform_tags}"
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/386" "linux/arm64" "linux/riscv64" "linux/ppc64le" "linux/s390x" "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

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-386" "linux-arm64" "linux-riscv64" "linux-ppc64le" "linux-s390x" "linux-arm-v7" "linux-arm-v6")
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