diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index 03a0f5bf37..260730004c 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -1,14 +1,9 @@ on: workflow_call: inputs: - platforms: - required: true - type: string cache: type: boolean default: true - use_native_arm64_builder: - type: boolean push_to_images: type: string version_prerelease: @@ -24,42 +19,36 @@ on: file_to_build: type: string +# This builds multiple images with one runner each, allowing us to build for multiple architectures +# using Github's runners. +# The two-step process is adapted form: +# https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners jobs: + # Build each (amd64 and arm64) image separately build-image: - runs-on: ubuntu-latest + runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 steps: - uses: actions/checkout@v4 - - uses: docker/setup-qemu-action@v3 - if: contains(inputs.platforms, 'linux/arm64') && !inputs.use_native_arm64_builder + - name: Prepare + env: + PUSH_TO_IMAGES: ${{ inputs.push_to_images }} + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + # Transform multi-line variable into comma-separated variable + image_names=${PUSH_TO_IMAGES//$'\n'/,} + echo "IMAGE_NAMES=${image_names%,}" >> $GITHUB_ENV - uses: docker/setup-buildx-action@v3 id: buildx - if: ${{ !(inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')) }} - - - name: Start a local Docker Builder - if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64') - run: | - docker run --rm -d --name buildkitd -p 1234:1234 --privileged moby/buildkit:latest --addr tcp://0.0.0.0:1234 - - - uses: docker/setup-buildx-action@v3 - id: buildx-native - if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64') - with: - driver: remote - endpoint: tcp://localhost:1234 - platforms: linux/amd64 - append: | - - endpoint: tcp://${{ vars.DOCKER_BUILDER_HETZNER_ARM64_01_HOST }}:13865 - platforms: linux/arm64 - name: mastodon-docker-builder-arm64-01 - driver-opts: - - servername=mastodon-docker-builder-arm64-01 - env: - BUILDER_NODE_1_AUTH_TLS_CACERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CACERT }} - BUILDER_NODE_1_AUTH_TLS_CERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CERT }} - BUILDER_NODE_1_AUTH_TLS_KEY: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_KEY }} - name: Log in to Docker Hub if: contains(inputs.push_to_images, 'tootsuite') @@ -76,8 +65,91 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/metadata-action@v5 + - name: Docker meta id: meta + uses: docker/metadata-action@v5 + if: ${{ inputs.push_to_images != '' }} + with: + images: ${{ inputs.push_to_images }} + flavor: ${{ inputs.flavor }} + labels: ${{ inputs.labels }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ inputs.file_to_build }} + build-args: | + MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} + MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} + SOURCE_COMMIT=${{ github.sha }} + platforms: ${{ matrix.platform }} + provenance: false + push: ${{ inputs.push_to_images != '' }} + cache-from: ${{ inputs.cache && 'type=gha' || '' }} + cache-to: ${{ inputs.cache && 'type=gha,mode=max' || '' }} + outputs: type=image,"name=${{ env.IMAGE_NAMES }}",push-by-digest=true,name-canonical=true,push=${{ inputs.push_to_images != '' }} + + - name: Export digest + if: ${{ inputs.push_to_images != '' }} + run: | + mkdir -p "${{ runner.temp }}/digests" + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + if: ${{ inputs.push_to_images != '' }} + uses: actions/upload-artifact@v4 + with: + # `hashFiles` is used to disambiguate between streaming and non-streaming images + name: digests-${{ hashFiles(inputs.file_to_build) }}-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + # Then merge the docker images into a single one + merge-images: + if: ${{ inputs.push_to_images != '' }} + runs-on: ubuntu-24.04 + needs: + - build-image + + env: + PUSH_TO_IMAGES: ${{ inputs.push_to_images }} + + steps: + - uses: actions/checkout@v4 + + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + # `hashFiles` is used to disambiguate between streaming and non-streaming images + pattern: digests-${{ hashFiles(inputs.file_to_build) }}-* + merge-multiple: true + + - name: Log in to Docker Hub + if: contains(inputs.push_to_images, 'tootsuite') + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Log in to the GitHub Container registry + if: contains(inputs.push_to_images, 'ghcr.io') + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 if: ${{ inputs.push_to_images != '' }} with: images: ${{ inputs.push_to_images }} @@ -85,18 +157,14 @@ jobs: tags: ${{ inputs.tags }} labels: ${{ inputs.labels }} - - uses: docker/build-push-action@v6 - with: - context: . - file: ${{ inputs.file_to_build }} - build-args: | - MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} - MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} - platforms: ${{ inputs.platforms }} - provenance: false - builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }} - push: ${{ inputs.push_to_images != '' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: ${{ inputs.cache && 'type=gha' || '' }} - cache-to: ${{ inputs.cache && 'type=gha,mode=max' || '' }} + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + run: | + echo "$PUSH_TO_IMAGES" | xargs -I{} \ + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '{}@sha256:%s ' *) + + - name: Inspect image + run: | + echo "$PUSH_TO_IMAGES" | xargs -i{} \ + docker buildx imagetools inspect {}:${{ steps.meta.outputs.version }} diff --git a/.github/workflows/build-nightly.yml b/.github/workflows/build-nightly.yml index 0ea553cfda..7dda6cb350 100644 --- a/.github/workflows/build-nightly.yml +++ b/.github/workflows/build-nightly.yml @@ -26,8 +26,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: Dockerfile - platforms: linux/amd64,linux/arm64 - use_native_arm64_builder: false cache: false push_to_images: | ghcr.io/${{ github.repository_owner }}/mastodon @@ -47,8 +45,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: streaming/Dockerfile - platforms: linux/amd64,linux/arm64 - use_native_arm64_builder: false cache: false push_to_images: | ghcr.io/${{ github.repository_owner }}/mastodon-streaming diff --git a/.github/workflows/build-push-pr.yml b/.github/workflows/build-push-pr.yml index d6324bad89..6ec561d2fb 100644 --- a/.github/workflows/build-push-pr.yml +++ b/.github/workflows/build-push-pr.yml @@ -32,8 +32,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: Dockerfile - platforms: linux/amd64,linux/arm64 - use_native_arm64_builder: false push_to_images: | ghcr.io/${{ github.repository_owner }}/mastodon version_metadata: ${{ needs.compute-suffix.outputs.metadata }} @@ -49,8 +47,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: streaming/Dockerfile - platforms: linux/amd64,linux/arm64 - use_native_arm64_builder: false push_to_images: | ghcr.io/${{ github.repository_owner }}/mastodon-streaming version_metadata: ${{ needs.compute-suffix.outputs.metadata }} diff --git a/.github/workflows/build-releases.yml b/.github/workflows/build-releases.yml index 808ffe115d..7508e4ff4e 100644 --- a/.github/workflows/build-releases.yml +++ b/.github/workflows/build-releases.yml @@ -13,8 +13,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: Dockerfile - platforms: linux/amd64,linux/arm64 - use_native_arm64_builder: false push_to_images: | ghcr.io/${{ github.repository_owner }}/mastodon # Do not use cache when building releases, so apt update is always ran and the release always contain the latest packages @@ -33,8 +31,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: streaming/Dockerfile - platforms: linux/amd64,linux/arm64 - use_native_arm64_builder: false push_to_images: | ghcr.io/${{ github.repository_owner }}/mastodon-streaming # Do not use cache when building releases, so apt update is always ran and the release always contain the latest packages diff --git a/.github/workflows/build-security.yml b/.github/workflows/build-security.yml index e9f1862f5d..7cfa8a1d6b 100644 --- a/.github/workflows/build-security.yml +++ b/.github/workflows/build-security.yml @@ -23,8 +23,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: Dockerfile - platforms: linux/amd64,linux/arm64 - use_native_arm64_builder: false cache: false push_to_images: | ghcr.io/${{ github.repository_owner }}/mastodon @@ -44,8 +42,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: streaming/Dockerfile - platforms: linux/amd64,linux/arm64 - use_native_arm64_builder: false cache: false push_to_images: | ghcr.io/${{ github.repository_owner }}/mastodon-streaming diff --git a/.github/workflows/test-image-build.yml b/.github/workflows/test-image-build.yml index 980e071897..bde40addd6 100644 --- a/.github/workflows/test-image-build.yml +++ b/.github/workflows/test-image-build.yml @@ -20,7 +20,6 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: Dockerfile - platforms: linux/amd64 # Testing only on native platform so it is performant cache: true build-image-streaming: @@ -31,5 +30,4 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: file_to_build: streaming/Dockerfile - platforms: linux/amd64 # Testing only on native platform so it is performant cache: true