🏡 index : quine-global-labs/mirror-ublue-os-brew.git

name: Build container image
on:
  pull_request:
    branches:
      - main
  schedule:
    - cron: "0 1 * * SUN"
  push:
    branches:
      - main
    paths-ignore:
      - "**/README.md"
  workflow_dispatch:

env:
  IMAGE_DESC: "OCI image for Homebrew"
  IMAGE_KEYWORDS: "homebrew"
  IMAGE_LOGO_URL: "https://avatars.githubusercontent.com/u/136393846?s=200&v=4"
  IMAGE_NAME: "${{ github.event.repository.name }}"
  IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}"
  DEFAULT_TAG: "latest"

jobs:
  build_push:
    name: Build and push image
    permissions:
      contents: read
      packages: write
      id-token: write
    timeout-minutes: 60
    strategy:
      fail-fast: true
      matrix:
        platform: ["amd64", "arm64"]
    runs-on: ${{ matrix.platform == 'amd64' && 'ubuntu-24.04' || 'ubuntu-24.04-arm' }}
    steps:
      - name: Checkout
        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6

      - name: Build image
        run: podman build -t "${IMAGE_NAME}:${DEFAULT_TAG}" -f ./Containerfile .

      - name: Login to GitHub Container Registry
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          LOGIN_USER: ${{ github.actor }}
        run: |
          echo "${GITHUB_TOKEN}" | podman login -u "${LOGIN_USER}" --password-stdin "ghcr.io"
          echo "${GITHUB_TOKEN}" | docker login -u "${LOGIN_USER}" --password-stdin "ghcr.io"

      - name: Push to GHCR
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        id: push
        env:
          IMAGE_REGISTRY: ${{ env.IMAGE_REGISTRY }}
          IMAGE_NAME: ${{ env.IMAGE_NAME }}
          IMAGE_DIGEST: ${{ steps.load.outputs.digest }}
          PLATFORM: ${{ matrix.platform }}
          MAX_RETRIES: 3
        run: |
          set -x

          for i in $(seq "${MAX_RETRIES}"); do
            podman push --digestfile=/tmp/digestfile "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" "${IMAGE_REGISTRY}/${IMAGE_NAME}:${DEFAULT_TAG}-${PLATFORM}" && break || sleep $((5 * i));
          done
          echo "remote_image_digest=$(< /tmp/digestfile)" | tee "${GITHUB_OUTPUT}"

      - name: Install Cosign
        uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)

      - name: Sign Image
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        env:
          REMOTE_IMAGE_DIGEST: ${{ steps.push.outputs.remote_image_digest }}
          COSIGN_EXPERIMENTAL: false
          COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }}
        run: cosign sign -y --new-bundle-format=false --use-signing-config=false --key env://COSIGN_PRIVATE_KEY "${IMAGE_REGISTRY}/${IMAGE_NAME}@${REMOTE_IMAGE_DIGEST}"

      - name: Create Job Outputs
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        env:
          IMAGE_NAME: ${{ env.IMAGE_NAME }}
          PLATFORM: ${{ matrix.platform }}
          DIGEST: ${{ steps.push.outputs.remote_image_digest }}
        run: |
          set -x
          mkdir -p /tmp/outputs/digests
          echo "${DIGEST}" | tee "/tmp/outputs/digests/${IMAGE_NAME}-${PLATFORM}.txt"

      - name: Upload Output Artifacts
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
        with:
          name: ${{ env.IMAGE_NAME }}-${{ matrix.platform }}
          retention-days: 1
          if-no-files-found: error
          path: |
            /tmp/outputs/digests/*.txt

  manifest:
    name: Create Manifest
    runs-on: ubuntu-latest
    if: always()
    needs:
      - build_push
    container:
      image: docker.io/library/alpine:latest
      options: --privileged --security-opt seccomp=unconfined
    permissions:
      contents: read
      packages: write
      id-token: write
    steps:
      - name: Install dependencies
        run: apk add bash coreutils curl docker findutils fuse-overlayfs git grep jq podman sed

      # Necessary until bootc supports Cosign v3
      - name: Install old cosign
        env:
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          # Thank you Bri! :3 @b-
          curl --retry 3 -H "Authorization: Bearer ${GITHUB_TOKEN}" -fsSLo "/usr/bin/cosign" "https://github.com/sigstore/cosign/releases/download/v2.6.1/cosign-linux-amd64"
          chmod +x /usr/bin/cosign

      - name: Exit on failure
        shell: bash
        env:
          JOBS: ${{ toJson(needs) }}
        run: |
          for i in $(jq -r 'to_entries[] | .value.result' <<< "${JOBS}"); do
            if [ "$i" != "success" ] && [ "$i" != "skipped" ]; then
              echo "Status check not okay!"
              exit 1
            fi
          done

        # This generates a timestamp like what is defined on the ArtifactHub documentation
        # E.G: 2022-02-08T15:38:15Z'
        # https://artifacthub.io/docs/topics/repositories/container-images/
        # https://linux.die.net/man/1/date
      - name: Get current date
        id: date
        run: echo "date=$(date -u +%Y\-%m\-%d\T%H\:%M\:%S\Z)" | tee "${GITHUB_OUTPUT}"

      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - name: Read Brew Version
        id: brew_version
        run: echo "version=$(cat BREW_VERSION)" >> $GITHUB_OUTPUT

      - name: Image Metadata
        uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5
        id: metadata
        with:
          tags: |
            type=raw,value=${{ env.DEFAULT_TAG }}
            type=raw,value=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}}
            type=raw,value={{date 'YYYYMMDD'}}
            type=raw,value=${{ steps.brew_version.outputs.version }}
            type=ref,event=pr
            type=sha,enable=${{ github.event_name == 'pull_request' }}
          labels: |
            io.artifacthub.package.deprecated=false
            io.artifacthub.package.keywords=${{ env.IMAGE_KEYWORDS }}
            io.artifacthub.package.license=Apache-2.0
            io.artifacthub.package.logo-url=${{ env.IMAGE_LOGO_URL }}
            io.artifacthub.package.prerelease=false
            io.artifacthub.package.readme-url=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/${{ github.sha }}/README.md
            org.opencontainers.image.created=${{ steps.date.outputs.date }}
            org.opencontainers.image.description=${{ env.IMAGE_DESC }}
            org.opencontainers.image.documentation=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/${{ github.sha }}/README.md
            org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/blob/${{ github.sha }}/Containerfile
            org.opencontainers.image.title=${{ env.IMAGE_NAME }}
            org.opencontainers.image.url=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/tree/${{ github.sha }}
            org.opencontainers.image.vendor=${{ github.repository_owner }}
            org.opencontainers.image.version=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}}

      - name: Fetch Build Outputs
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
        with:
          pattern: ${{ env.IMAGE_NAME }}-*
          merge-multiple: true
          path: /tmp/artifacts

      - name: Load Outputs
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        shell: bash
        id: load-outputs
        run: |
          set -x
          OUTCSV="$(mktemp)"
          for digest_file in /tmp/artifacts/*.txt; do
            # Extract the platform from the file name
            PLATFORM="$(basename "${digest_file}" ".txt" | sed 's/.*-//g')"
            IMAGE_NAME="$(basename "${digest_file}" ".txt" | grep -Po ".*-" | sed 's/-$//')"
            DIGEST="$(< "${digest_file}")"
            echo "${PLATFORM},${IMAGE_NAME},${DIGEST}" | tee -a "${OUTCSV}"
          done
          echo "OUTPUT_CSV=$(base32 -w 0 < "${OUTCSV}")" | tee -a "${GITHUB_OUTPUT}"

      - name: Login to GitHub Container Registry
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        env:
          REGISTRY: ghcr.io
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | podman login -u "${{ github.actor }}" --password-stdin "${REGISTRY}"
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login -u "${{ github.actor }}" --password-stdin "${REGISTRY}"

      - name: Publish manifest with unified architecture and sign
        shell: bash
        if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        env:
          COSIGN_EXPERIMENTAL: false
          COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }}
          LABELS: ${{ steps.metadata.outputs.labels }}
          OUTPUT_CSV: ${{ steps.load-outputs.outputs.OUTPUT_CSV }}
          TAGS: ${{ steps.metadata.outputs.tags }}
        run: |
          set -x

          OUTPUT_CSV_FILE="$(mktemp)"
          echo "${OUTPUT_CSV}" | base32 -d | tee "${OUTPUT_CSV_FILE}"

          # This is insanely dumb, please PR if you have a better solution ~ @tulilirockz :3c
          IMAGES=$(cat "${OUTPUT_CSV_FILE}" | while IFS=$'\n' read -r ENTRY ; do echo "${ENTRY}" | cut -d, -f2 ; done | sort | uniq | tr "\n" " " | sed 's/.$//')
          for TARGET_IMAGE in ${IMAGES} ; do
            TARGET_MANIFEST="${IMAGE_REGISTRY}/${TARGET_IMAGE}"
            podman manifest create "${TARGET_MANIFEST}"

            PLATFORMS=$(grep -F -e "${IMAGE}" "${OUTPUT_CSV_FILE}" | while IFS=$'\n' read -r ENTRY ; do echo "${ENTRY}" | cut -d, -f1 ; done | sort | uniq | tr "\n" " " | sed 's/.$//')
            for TARGET_PLATFORM in ${PLATFORMS} ; do
              TARGET_DIGEST="$(grep -F -e ",${TARGET_IMAGE}," "${OUTPUT_CSV_FILE}" | grep -F -e "${TARGET_PLATFORM}" | cut -d, -f3 | tr -d "\n")"
              podman manifest add "${TARGET_MANIFEST}" "${TARGET_MANIFEST}@${TARGET_DIGEST}" --arch "${TARGET_PLATFORM}"
            done

            while IFS= read -r LABEL; do
              podman manifest annotate --index --annotation "${LABEL}" "${TARGET_MANIFEST}"
            done <<< "${LABELS}"

            while IFS= read -r TAG; do
              podman manifest push --all=false --digestfile=/tmp/digestfile "${TARGET_MANIFEST}" "${TARGET_MANIFEST}:${TAG}"
            done <<< "${TAGS}"

            cosign sign -y --new-bundle-format=false --use-signing-config=false --key env://COSIGN_PRIVATE_KEY "${TARGET_MANIFEST}@$(< /tmp/digestfile)"
          done