Build multi-arch docker images and publish on DockerHub #929
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: Build multi-arch docker images and publish on DockerHub | |
# Control when actions will run | |
on: | |
workflow_dispatch: | |
inputs: | |
logLevel: | |
type: choice | |
description: Log level | |
required: false | |
default: info | |
options: | |
- info | |
- debug | |
platform: | |
type: choice | |
description: 'Platform to build' | |
required: false | |
default: all | |
# Limit available choices for manual runs | |
options: | |
- all | |
- linux/amd64 | |
- linux/arm64 | |
- linux/arm/v7 | |
push: | |
branches: | |
- main | |
- develop | |
tags: | |
- 'v*' | |
paths-ignore: | |
- '**.md' | |
#- '.github/workflows/**' | |
- 'docs/**' | |
pull_request: | |
branches: | |
- main | |
paths-ignore: | |
- '**.md' | |
#- '.github/workflows/**' | |
- 'docs/**' | |
schedule: | |
- cron: '0 2 * * *' # everyday at 2am | |
# Set environment variables | |
env: | |
DOCKERHUB_SLUG: ${{ secrets.DOCKERHUB_USERNAME }}/mopidy | |
# A workflow run is made up of one or more jobs that can run sequentially or in parallel (matrix) | |
jobs: | |
# Create a matrix of platforms depending on the input from workflow_dispatch or | |
determine_platform: | |
name: Determine Platform | |
runs-on: ubuntu-latest | |
outputs: | |
platforms: ${{ steps.set-matrix.outputs.platforms }} | |
steps: | |
- id: set-matrix | |
run: | | |
ALL_PLATFORMS=$(cat <<-END | |
["linux/amd64","linux/arm64","linux/arm/v7"] | |
END | |
) | |
if [ -z "${{ github.event.inputs.platform }}" ] || [ "${{ github.event.inputs.platform }}" == "all" ]; then | |
echo "PLATFORMS=${ALL_PLATFORMS}" >> $GITHUB_OUTPUT | |
else | |
SINGLE_PLATFORM="[\"${{ github.event.inputs.platform }}\"]" | |
echo "PLATFORMS=${SINGLE_PLATFORM}" >> $GITHUB_OUTPUT | |
fi | |
shell: bash | |
# Build docker image for each platform on a dedicated runner using the matrix strategy and push the image by digest | |
# see https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners | |
build: | |
name: Build | |
needs: determine_platform | |
# The type of runner that the job will run on | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
# List of platforms on which the job will be run in parallel. | |
matrix: | |
platform: ${{ fromJson(needs.determine_platform.outputs.platforms) }} | |
image: | |
- latest | |
- develop | |
- release | |
# Steps represent a sequence of tasks that will be executed as part of the job | |
steps: | |
# Overwrite platform matrix value for manual runs with a selected platform | |
- name: Set platform matrix for manual runs with a selected platform | |
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.platform != 'all' }} | |
run: echo "PLATFORM_MATRIX=[\"${{ github.event.inputs.platform }}\"]" >> $GITHUB_ENV | |
# Store current platform from matrix as variable $PLATFORM_PAIR (with "/" replaced by "-") | |
- name: Prepare | |
run: | | |
platform=${{ matrix.platform }} | |
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV | |
# Github action to provide runner with OS information | |
- name: Get GitHub Actions runner OS information | |
uses: kenchan0130/actions-system-info@master | |
id: system-info | |
- name: Output System information | |
run: | | |
OUTPUTS=( | |
"CPU Core: ${{ steps.system-info.outputs.cpu-core }}" | |
"CPU Model: ${{ steps.system-info.outputs.cpu-model }}" | |
"Hostname: ${{ steps.system-info.outputs.hostname }}" | |
"Kernel release: ${{ steps.system-info.outputs.kernel-release }}" | |
"Kernel version: ${{ steps.system-info.outputs.kernel-version }}" | |
"Name: ${{ steps.system-info.outputs.name }}" | |
"Platform: ${{ steps.system-info.outputs.platform }}" | |
"Release: ${{ steps.system-info.outputs.release }}" | |
"Total memory bytes: ${{ steps.system-info.outputs.totalmem }}" | |
) | |
for OUTPUT in "${OUTPUTS[@]}";do | |
echo "${OUTPUT}" | |
done | |
echo "Disk Space:" | |
df -h | |
# # GitHub Action to free disk space on Ubuntu action runners. | |
# - name: Free Disk Space (Ubuntu) | |
# uses: jlumbroso/free-disk-space@main | |
# Github Action to get branch or tag information without the /ref/* prefix | |
- name: Get branch names | |
id: branch-name | |
uses: tj-actions/branch-names@v6 | |
# Github Action to check-out your repository under $GITHUB_WORKSPACE, so your job can access it | |
- name: Checkout | |
uses: actions/checkout@v4.1.1 | |
# GitHub Action to install QEMU static binaries (optional for more platform options in buildx) | |
- name: Set up QEMU | |
uses: docker/setup-qemu-action@v3 | |
# GitHub Action to set up Docker Buildx. | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
with: | |
buildkitd-flags: ${{ github.event.inputs.logLevel == 'debug' && '--debug' || '' }} | |
# Hack to fix Errors on arm/v7 and linux/386 | |
# Work around for qemu bug on 32bit systems caused during rust compilation (https://github.com/JonasAlfredsson/docker-on-tmpfs?tab=readme-ov-file) | |
# - "Value too large for defined data type;" https://github.com/crazy-max/ghaction-docker-buildx/issues/172 | |
# - "object not found - no match for id (SOME_HASH)" on git update | |
- name: Run Docker on tmpfs | |
if: matrix.platform == 'linux/arm/v7' || matrix.platform == 'linux/i386' | |
uses: JonasAlfredsson/docker-on-tmpfs@v1 | |
with: | |
tmpfs_size: 10 | |
swap_size: 10 | |
swap_location: '/mnt/swapfile' | |
# GitHub Action to extract metadata (tags, labels) for Docker. | |
- name: Docker meta | |
id: docker_meta | |
uses: docker/metadata-action@v5 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
images: ${{ env.DOCKERHUB_SLUG }} | |
labels: | | |
org.opencontainers.image.title=${{ env.DOCKERHUB_SLUG }} | |
org.opencontainers.image.description='Mopidy music server with Iris Web-UI, Spotify support and many other extensions.' | |
org.opencontainers.image.vendor=${{ secrets.DOCKERHUB_USERNAME }} | |
# GitHub Action to login to DockerHub. | |
- name: Login to DockerHub | |
uses: docker/login-action@v3 | |
with: | |
username: ${{ secrets.DOCKERHUB_USERNAME }} | |
password: ${{ secrets.DOCKERHUB_TOKEN }} | |
# GitHub Action to build and push Docker image by digest (image identifier) | |
# - Use Docker-Image-Cache from GitHub Actions (gha) and cache all data (max) | |
- name: Build and push by digest | |
id: docker_build | |
uses: docker/build-push-action@v5 | |
with: | |
context: . | |
file: ./Dockerfile | |
platforms: ${{ matrix.platform }} | |
provenance: false | |
labels: ${{ steps.docker_meta.outputs.labels }} | |
tags: ${{ env.DOCKERHUB_SLUG }} | |
#cache-from: type=gha,scope=${{ github.repository }}-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.image }} | |
#cache-to: type=gha,mode=max,scope=${{ github.repository }}-${{ github.ref_name }}-${{ matrix.platform }}-${{ matrix.image }} | |
outputs: type=image,name=${{ env.DOCKERHUB_SLUG }},push-by-digest=true,name-canonical=true,push=true | |
build-args: | | |
IMG_VERSION=${{ matrix.image }} | |
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') | |
VCS_REF=${GITHUB_SHA::8} | |
# Export digest and store in GitHub artefact storage for later use in other jobs | |
- name: Export digest | |
run: | | |
mkdir -p /tmp/digests/${{ matrix.image }} | |
digest="${{ steps.docker_build.outputs.digest }}" | |
touch "/tmp/digests/${{ matrix.image }}/${digest#sha256:}" | |
- name: Upload digest | |
uses: actions/upload-artifact@v4 | |
with: | |
name: digests-${{ matrix.image }}-${{ env.PLATFORM_PAIR }} | |
path: /tmp/digests/${{ matrix.image }}/* | |
if-no-files-found: error | |
retention-days: 1 | |
- name: Clear digest | |
run: | | |
rm -rf /tmp/digests/${{ matrix.image }} | |
# This merge job will catch all digests from the build jobs, create a manifest list and push it to Docker Hub | |
merge: | |
if: ${{ github.event_name != 'pull_request' }} | |
name: Merge Docker manifests | |
strategy: | |
fail-fast: false | |
matrix: | |
image: | |
- latest | |
- develop | |
- release | |
runs-on: ubuntu-latest | |
needs: | |
- build | |
steps: | |
# Download digests from build jobs | |
- name: Download digests | |
uses: actions/download-artifact@v4 | |
with: | |
path: /tmp/digests/${{ matrix.image }} | |
pattern: digests-${{ matrix.image }}-* | |
merge-multiple: true | |
# GitHub Action to set up Docker Buildx. | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
# GitHub Action to extract metadata (tags, labels) for Docker. | |
- name: Docker meta | |
id: meta | |
uses: docker/metadata-action@v5 | |
with: | |
images: ${{ env.DOCKERHUB_SLUG }} | |
tags: | | |
type=raw,value=${{ matrix.image }},enable=${{ github.ref_name == github.event.repository.default_branch }} | |
type=raw,value=dev_${{ matrix.image }},enable=${{ github.ref == format('refs/heads/{0}', 'develop') }} | |
type=raw,value=test_${{ matrix.image }},enable=${{ github.ref_name != github.event.repository.default_branch && github.ref != format('refs/heads/{0}', 'develop') }} | |
# GitHub Action to login to DockerHub. | |
- name: Login to Docker Hub | |
uses: docker/login-action@v3 | |
with: | |
username: ${{ secrets.DOCKERHUB_USERNAME }} | |
password: ${{ secrets.DOCKERHUB_TOKEN }} | |
# Create and push manifest list | |
- name: Create manifest list and push | |
working-directory: /tmp/digests/${{ matrix.image }} | |
# Extract all tags from JSON-Query $DOCKER_METADATA_OUTPUT_JSON: -t tag1 -t tag2 ... | |
# Add ${{ env.DOCKERHUB_SLUG }}@sha256:digest1 sha256:digest2 ... | |
run: | | |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}') \ | |
$(printf '${{ env.DOCKERHUB_SLUG }}@sha256:%s ' *) | |
# GitHub Action to build and push Docker images with Buildx. | |
- name: Inspect image | |
run: | | |
docker buildx imagetools inspect ${{ env.DOCKERHUB_SLUG }}:${{ steps.meta.outputs.version }} | |