Generate image metadata for BuildKit-powered build through `docker buildx`

About my problem / Question

Currently, I use GitHub Actions to generate image metadata and setup multi-arch image builds using buildx bake because I do HCL configuration stuff for other parts of image builds. I want to migrate these workflows away from GitHub Actions into GitLab CI/CD so I can leverage other features, especially using the job-specific deploy tokens for GitLab Container Registry.

So how do I dynamically generate image metadata based on what’s changed on the docker directory (e.g. only build our custom Mkdocs Material theme Docker image if docker/mkdocs-material directory has some changes) without cloning any of GitHub Actions workflow repositories and run them from source? (I can reproduce the login step and multiarch setup through QEMU.)

My current workflow

The following code below are copied from .github/workflows/mkdocs-material-image.yml · f23d44b39d493c9f458ab55a8470f969abfa6f58 · The Pins Team / Infrastructure Team - The Pins Team / Docker-related Stuff / Custom Docker Images for CI and CD · GitLab as an reference to this community question:

name: "mkdocs-material"

on:
  push:
    branches:
      - master
    paths:
      - docker/**
      - .github/workflows/*.yml
      - .trigger-rebuild
  pull_request:
    branches:
      - master
    paths:
      - docker/**
      - .github/workflows/*.yml
      - .trigger-rebuild

env:
  # GHCR
  gh_namespace: thepinsteam-customimagebuilds/cicd-images
  # since our RHQCR and Docker Hub namespaces are same,
  # we can just squash them into one env.
  docker_namespace: madebythepinshub
  # in GitLab Container Registry, it's group/subgroup/goes/here/uwu/repo as it's namespace
  gitlab_container_registry_endpoint: registry.gitlab.com
  gitlab_container_registry_namespace: madebythepinshub/infra/docker/custom-cicd-images
  # Image name finally goes here
  image_name: mkdocs-material

jobs:
  build-image:
    name: Build Docker image
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Get files changed
        uses: dorny/paths-filter@v2
        id: filter
        with:
          # Base branch used to get changed files
          # TODO: Rename to main soon
          base: 'master'

          # Enables setting an output in the format in `${FILTERl_NAME}_files
          # with the names of the matching files formatted as JSON array
          list-files: json

          # Returns list of changed files matching each filter
          filters: |
            dockerfile:
              - 'docker/mkdocs-material/**'
            manual:
              - '.trigger-rebuild'
              - '.github/workflows/mkdocs-material-image.yml'

      # Login step
      - name: Login with Docker Hub
        if: ${{ github.event_name != 'pull_request' && (steps.filter.outputs.dockerfile == 'true' || steps.filter.outputs.manual == 'true') }}
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
          password: ${{ secrets.DOCKER_REGISTRY_ACCESS_KEY }}
      - name: Login with GHCR
        if: ${{ github.event_name != 'pull_request' && (steps.filter.outputs.dockerfile == 'true' || steps.filter.outputs.manual == 'true') }}
        uses: docker/login-action@v1
        with:
          username: RecapTimeBot
          password: ${{ secrets.GH_SERVICE_ACCOUNT_API_KEY }}
          registry: ghcr.io
      - name: Login with RHQCR
        if: ${{ github.event_name != 'pull_request' && (steps.filter.outputs.dockerfile == 'true' || steps.filter.outputs.manual == 'true') }}
        uses: docker/login-action@v1
        with:
          username: madebythepinshub+github_actions
          password: ${{ secrets.RHQCR_SERVICE_ACCOUNT_KEY }}
          registry: quay.io
      - name: Login with GitLab Container Registry at GitLab SaaS
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.GITLAB_CONTAINER_REGISTRY_USERNAME }}
          password: ${{ secrets.GITLAB_CONTAINER_REGISTRY_PASSWORD }}
          registry: ${{ env.gitlab_container_registry_endpoint }}

      # Ensure this step always executed before the build happens
      - name: Generate matadata
        uses: docker/metadata-action@v3
        id: metadata
        with:
          images: |
            ghcr.io/${{ env.gh_namespace }}/${{ env.image_name }}
            ${{ env.gitlab_container_registry_endpoint }}/${{ env.gitlab_container_registry_namespace }}/${{ env.image_name }}
            quay.io/${{ env.docker_namespace }}/${{ env.image_name }}
            ${{ env.docker_namespace }}/${{ env.image_name }}
          tags: |
            type=sha,format=long
          flavor: |
            latest=true

      # Since we're doing multiarch builds, we need to setup QEMU and Buildx ahead of time
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v1
      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v1
        with:
          # DOCKER_BUILDKIT will be set to 1 for "docker build" commands
          install: true

      # We need to switch from the regular build-action because we're using bakefiles in Terraform style, HCL.
      - name: Build with buildx bake
        if: ${{ steps.filter.outputs.dockerfile == 'true' || steps.filter.outputs.manual == 'true' }}
        uses: docker/bake-action@v1.6.0
        with:
          files: |
            ./docker/mkdocs-material/build.hcl
            ${{ steps.metadata.outputs.bake-file }}
          push: ${{ github.event_name != 'pull_request' }}
          targets: build