name: Docker

on:
  pull_request:
  push:
    branches:
      - main
  merge_group:
  release:
    types:
      - published

permissions:
  contents: read

defaults:
  run:
    shell: bash -xeuo pipefail {0}

env:
  # odeprecated: remove 22.04 image in Homebrew >=6.1
  VERSIONS: '["22.04", "24.04", "26.04"]'

jobs:
  generate-tags:
    if: github.repository_owner == 'Homebrew'
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.attributes.outputs.matrix }}
      tags: ${{ steps.attributes.outputs.tags }}
      labels: ${{ steps.attributes.outputs.labels }}
      push: ${{ steps.attributes.outputs.push }}
      merge: ${{ steps.attributes.outputs.merge }}
      core_revision: ${{ steps.attributes.outputs.core_revision }}
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Fetch origin/HEAD from Git
        run: git fetch origin HEAD

      - name: Determine build attributes
        id: attributes
        run: |
          date="$(date --rfc-3339=seconds --utc)"
          brew_version="$(git describe --tags --dirty --abbrev=7)"
          core_revision="$(git ls-remote https://github.com/Homebrew/homebrew-core refs/heads/main | cut -f1)"

          DELIMITER="END_LABELS_$(uuidgen)"
          cat <<EOS | tee -a "${GITHUB_OUTPUT}"
          labels<<${DELIMITER}
          org.opencontainers.image.created=${date}
          org.opencontainers.image.url=https://brew.sh
          org.opencontainers.image.documentation=https://docs.brew.sh
          org.opencontainers.image.source=https://github.com/${GITHUB_REPOSITORY}
          org.opencontainers.image.version=${brew_version}
          org.opencontainers.image.revision=${GITHUB_SHA}
          org.opencontainers.image.vendor=${GITHUB_REPOSITORY_OWNER}
          org.opencontainers.image.licenses=BSD-2-Clause
          ${DELIMITER}
          EOS

          typeset -A tag_hash
          typeset -A push_hash
          matrix=()
          merge=false
          while IFS=$'\n' read -r version; do
            tags=()
            if [[ "${GITHUB_EVENT_NAME}" == "release" ]]; then
              tags+=(
                "ghcr.io/homebrew/ubuntu${version}:${brew_version}"
                "ghcr.io/homebrew/ubuntu${version}:latest"
              )
              if [[ "${version}" == "24.04" ]]; then
                tags+=(
                  "ghcr.io/homebrew/brew:${brew_version}"
                  "ghcr.io/homebrew/brew:latest"
                )
              fi
            elif [[ "${GITHUB_EVENT_NAME}" == "push" &&
                    ("${GITHUB_REF}" == "refs/heads/main") ]]; then
              if [[ "${version}" == "24.04" ]]; then
                tags+=("ghcr.io/homebrew/brew:main")
              fi
              tags+=("ghcr.io/homebrew/ubuntu${version}:main")
            fi

            if [[ "${#tags[@]}" -ne 0 ]]; then
              tags_as_json_array="$(
                jq --null-input --compact-output '$ARGS.positional' --args "${tags[@]}"
              )"
              tag_hash["${version}"]="${tags_as_json_array}"
              push_hash["${version}"]=true
              merge=true
              matrix+=("${version}")
            else
              push_hash["${version}"]=false
            fi
          done <<<"$(jq --raw-output '.[]' <<<"${VERSIONS}")"

          {
            # Transform the `matrix` variable into a JSON array.
            echo "matrix=$(jq --null-input --compact-output '$ARGS.positional' --args "${matrix[@]}")"
            echo "merge=${merge}"
            echo "core_revision=${core_revision}"
          } >>"${GITHUB_OUTPUT}"

          {
            DELIMITER="END_TAGS_$(uuidgen)"
            has_previous=
            echo "tags<<${DELIMITER}"
            printf '{'
            for version in "${!tag_hash[@]}"; do
              [[ -n "${has_previous:-}" ]] && printf ','
              printf '"%s": %s' "${version}" "${tag_hash[$version]}"
              has_previous=1
            done
            echo '}'
            echo "${DELIMITER}"
          } | tee -a "${GITHUB_OUTPUT}"

          {
            DELIMITER="END_PUSH_$(uuidgen)"
            has_previous=
            echo "push<<${DELIMITER}"
            printf '{'
            for version in "${!push_hash[@]}"; do
              [[ -n "${has_previous:-}" ]] && printf ','
              printf '"%s": %s' "${version}" "${push_hash[$version]}"
              has_previous=1
            done
            echo '}'
            echo "${DELIMITER}"
          } | tee -a "${GITHUB_OUTPUT}"

  build:
    needs: generate-tags
    if: github.repository_owner == 'Homebrew'
    name: docker (${{ matrix.arch }} Ubuntu ${{ matrix.version }})
    runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
    strategy:
      fail-fast: false
      matrix:
        # odeprecated: remove 22.04 image in Homebrew >=5.4
        version: ["22.04", "24.04", "26.04"]
        arch: ["x86_64", "arm64"]

    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Fetch origin/HEAD from Git
        run: git fetch origin HEAD

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
        with:
          cache-binary: false

      - name: Retrieve build attributes
        id: attributes
        env:
          VERSION: ${{ matrix.version }}
          PUSH: ${{ needs.generate-tags.outputs.push }}
        run: |
          # odeprecated: remove 22.04 image in Homebrew >=6.1
          if [[ "${VERSION}" == "22.04" ]]; then
            echo "The homebrew/ubuntu22.04 image is deprecated and will soon be retired. Use homebrew/brew." > .docker-deprecate
          fi

          filter="$(printf '.["%s"]' "${VERSION}")"
          echo "push=$(jq --raw-output "${filter}" <<<"${PUSH}")" >>"${GITHUB_OUTPUT}"

      - name: Log in to GitHub Packages (github-actions[bot])
        uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
        with:
          registry: ghcr.io
          username: github-actions[bot]
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build Docker image
        uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
        with:
          context: .
          load: true
          tags: brew
          cache-from: type=registry,ref=ghcr.io/homebrew/ubuntu${{ matrix.version }}:cache-${{ matrix.arch }}
          build-args: |
            version=${{ matrix.version }}
            HOMEBREW_CORE_REVISION=${{ needs.generate-tags.outputs.core_revision }}
          labels: ${{ needs.generate-tags.outputs.labels }}

      - name: Set environment variables
        if: matrix.version == '22.04'
        # odeprecated: remove 22.04 in Homebrew >=6.1
        run: echo "HOMEBREW_GLIBC_TESTING=1" >> "$GITHUB_ENV"

      - name: Run brew test-bot --only-setup
        run: docker run --env HOMEBREW_GLIBC_TESTING --rm brew brew test-bot --only-setup

      - name: Log in to GitHub Packages (BrewTestBot)
        if: fromJSON(steps.attributes.outputs.push)
        uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
        with:
          registry: ghcr.io
          username: BrewTestBot
          password: ${{ secrets.HOMEBREW_BREW_GITHUB_PACKAGES_TOKEN }}

      - name: Deploy the Docker image by digest
        id: digest
        if: fromJSON(steps.attributes.outputs.push)
        uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
        with:
          context: .
          cache-from: type=registry,ref=ghcr.io/homebrew/ubuntu${{ matrix.version }}:cache-${{ matrix.arch }}
          cache-to: type=registry,ref=ghcr.io/homebrew/ubuntu${{ matrix.version }}:cache-${{ matrix.arch }},mode=max
          build-args: |
            version=${{ matrix.version }}
            HOMEBREW_CORE_REVISION=${{ needs.generate-tags.outputs.core_revision }}
          labels: ${{ needs.generate-tags.outputs.labels }}
          outputs: type=image,name=ghcr.io/homebrew/ubuntu${{ matrix.version }},name-canonical=true,push=true,push-by-digest=true

      - name: Export the Docker image digest
        if: fromJSON(steps.attributes.outputs.push)
        run: |
          mkdir -p "${RUNNER_TEMP}"/digests
          echo "${DIGEST#sha256:}" >"${RUNNER_TEMP}/digests/${VERSION}-${ARCH}"
        env:
          DIGEST: ${{ steps.digest.outputs.digest }}
          VERSION: ${{ matrix.version }}
          ARCH: ${{ matrix.arch }}

      - name: Upload the Docker image digest
        if: fromJSON(steps.attributes.outputs.push)
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: digest-${{ matrix.version }}-${{ matrix.arch }}
          path: ${{ runner.temp }}/digests/*

  merge:
    needs: [generate-tags, build]
    if: github.repository_owner == 'Homebrew' && fromJSON(needs.generate-tags.outputs.merge)
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        version: ${{ fromJSON(needs.generate-tags.outputs.matrix) }}
    steps:
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
        with:
          cache-binary: false

      - name: Download Docker image digests
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: ${{ runner.temp }}/digests
          pattern: digest-${{ matrix.version }}-*
          merge-multiple: true

      - name: Log in to GitHub Packages (BrewTestBot)
        uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
        with:
          registry: ghcr.io
          username: BrewTestBot
          password: ${{ secrets.HOMEBREW_BREW_GITHUB_PACKAGES_TOKEN }}

      - name: Merge and push Docker image
        env:
          TAGS: ${{ needs.generate-tags.outputs.tags }}
          VERSION: ${{ matrix.version }}
        run: |
          filter="$(printf '.["%s"].[]' "${VERSION}")"
          tag_args=()
          while IFS=$'\n' read -r tag; do
            [[ -n "${tag}" ]] || continue
            tag_args+=("--tag=${tag}")
          done <<<"$(jq --raw-output "${filter}" <<<"${TAGS}")"

          image_args=("ghcr.io/homebrew/ubuntu${VERSION}@sha256:$(<"${RUNNER_TEMP}/digests/${VERSION}-x86_64")")
          image_args+=("ghcr.io/homebrew/ubuntu${VERSION}@sha256:$(<"${RUNNER_TEMP}/digests/${VERSION}-arm64")")

          attempts=0
          until docker buildx imagetools create "${tag_args[@]}" "${image_args[@]}"; do
            attempts=$((attempts + 1))
            if [[ $attempts -ge 3 ]]; then
              echo "[$(date -u)] ERROR: Failed after 3 attempts." >&2
              exit 1
            fi
            delay=$((2 ** attempts))
            if [[ $delay -gt 15 ]]; then delay=15; fi
            echo "Push failed (attempt $attempts). Retrying in ${delay} seconds..."
            sleep ${delay}
          done
