# Based on https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners

name: Publish Docker image to Dockerhub

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

on:
  workflow_dispatch:
  push:
    branches:
      - 'master'
    tags:
      - '*.*.*'
    # don't trigger if just updating docs
    paths-ignore:
      - 'README.md'
      - '.github/**'
      - 'flatpak/**'
  # use release instead of tags once version is correctly parsed
  # https://github.com/docker/metadata-action/issues/422
  # https://github.com/docker/metadata-action/issues/240
#  release:
#    types: [ published ]

# define in GH Repository -> Actions -> Variables (or act .variables) to enable pushing to registries
# -- will only push to registries that are defined
# EX
# DOCKERHUB_SLUG=foxxmd/multi-scrobbler
# GHCR_SLUG=ghcr.io/foxxmd/multi-scrobbler
  
jobs:
  test:
    if: github.event_name != 'pull_request'
    uses: ./.github/workflows/testAndSanity.yml

  build:
    name: Build OCI Images
    if: ${{ github.event_name != 'pull_request' && (vars.DOCKERHUB_SLUG != '' ||  vars.GHCR_SLUG != '') }}
    needs: test
    runs-on: ${{ matrix.os }}
    permissions:
      packages: write
      contents: read
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            arch: amd64
            platform: linux/amd64
          - os: ubuntu-24.04-arm
            arch: arm64
            platform: linux/arm64
    steps:
      - name: Prepare
        run: |
          platform=${{ matrix.platform }}
          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV     

          # list all registries to push to and join all non-empty with comma
          # https://unix.stackexchange.com/a/693165/116849
          # https://stackoverflow.com/a/9429887/1469797
          strings=("${VARS_DOCKERHUB_SLUG}" "${VARS_GHCR_SLUG}")
          for i in ${!strings[@]}; do [[ -z ${strings[i]} ]] && unset strings[i]; done
          joined_string=$(IFS=, ; echo "${strings[*]}")
          echo "REGISTRIES_JOINED=$joined_string" >> $GITHUB_ENV 
        env:
          VARS_DOCKERHUB_SLUG: ${{ vars.DOCKERHUB_SLUG }}
          VARS_GHCR_SLUG: ${{vars.GHCR_SLUG}}
          

      - name: Check out the repo
        uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 #v4.3.0
        with:
          persist-credentials: false

      - name: Set short git commit SHA
        id: appvars
        # https://dev.to/hectorleiva/github-actions-and-creating-a-short-sha-hash-8b7
        # short sha available under env.COMMIT_SHORT_SHA
        run: |
          calculatedSha=$(git rev-parse --short HEAD)
          branchName=$(git rev-parse --abbrev-ref HEAD)
          echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV
          echo "COMMIT_BRANCH=$branchName" >> $GITHUB_ENV

      - name: Get App Version
        id: appversion
        env:
          # use release instead of tags once version is correctly parsed
          #APP_VERSION: ${{ github.event.release.tag_name }}

          # https://github.com/actions/runner/issues/409#issuecomment-752775072
          # https://stackoverflow.com/a/69919067/1469797
          APP_VERSION: ${{ contains(github.ref, 'refs/tags/') && github.ref_name || format('{0}-{1}', env.COMMIT_BRANCH, env.COMMIT_SHORT_SHA ) }}
        run: |
          echo appversion=$APP_VERSION >>${GITHUB_OUTPUT}

      - name: Login to Docker Hub
        if: ${{ github.event_name != 'pull_request' && vars.DOCKERHUB_SLUG != '' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
  
      - name: Login to GitHub Container Registry
        if: ${{ github.event_name != 'pull_request' && vars.GHCR_SLUG != '' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      # metadata extract for docker labels/image names is done in merge job
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 #v3.11.1
      
      # https://github.com/docker/build-push-action/issues/671#issuecomment-1619353328
      # for caching
      - name: Build and push by digest
        id: build
        uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0
        with:
          build-args: |
            APP_BUILD_VERSION=${{steps.appversion.outputs.appversion}}
          platforms: ${{ matrix.platform }}
          labels: ${{ steps.meta.outputs.labels }}
          outputs: type=image,"name=${{ env.REGISTRIES_JOINED }}",push-by-digest=true,name-canonical=true,push=true
          #cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
          #cache-to: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
      
      - name: Export digest
        run: |
          mkdir -p /tmp/digests
          digest="${VARS_DIGEST}"
          touch "/tmp/digests/${digest#sha256:}"
        env:
            VARS_DIGEST: ${{ steps.build.outputs.digest }}      
      - name: Upload digest
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 #4.6.2
        with:
          name: digests-${{ env.PLATFORM_PAIR }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
    name: Merge OCI Images and Push
    if: ${{ github.event_name != 'pull_request' && (vars.DOCKERHUB_SLUG != '' ||  vars.GHCR_SLUG != '') }}
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    needs:
      - build
      - test
    steps:
      - name: Download digests
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 #4.3.0
        with:
          path: /tmp/digests
          pattern: digests-*
          merge-multiple: true

      - name: Login to Docker Hub
        if: ${{ github.event_name != 'pull_request' && vars.DOCKERHUB_SLUG != '' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to GitHub Container Registry
        if: ${{ github.event_name != 'pull_request' && vars.GHCR_SLUG != '' }}
        uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 
      
      - name: Extract metadata (tags, labels)
        id: meta
        uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 #5.9.0
        with:
          images: |
            ${{ vars.DOCKERHUB_SLUG }}
            ${{ vars.GHCR_SLUG }}
          # generate Docker tags based on the following events/attributes
          # https://github.com/docker/metadata-action/issues/247#issuecomment-1511259674 for NOT is default branch, eventually
          tags: |
            type=edge

            # push with branch name as tag if not master/main
            type=ref,event=branch,enable=${{ !endsWith(github.ref, 'master') }}
            
            # tag non-prelease as latest -- has a higher priority than regular tag so it shows first in registries
            type=match,pattern=\d.\d.\d$,priority=901
            
            # tag all semver (include pre-release)
            type=semver,pattern={{version}}
        #  flavor: |
        #    latest=false
          labels: |
            org.opencontainers.image.title=Multi-Scrobbler
            org.opencontainers.image.description=Scrobble from many sources to many clients
            org.opencontainers.image.vendor=FoxxMD
      
      - name: Create manifest list and push dockerhub
        if: ${{ vars.DOCKERHUB_SLUG != '' }}
        working-directory: /tmp/digests
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            $(printf "${VARS_DOCKERHUB_SLUG}@sha256:%s " *)
        env:
          VARS_DOCKERHUB_SLUG: ${{ vars.DOCKERHUB_SLUG }}
      
      - name: Create manifest list and push gchr
        if: ${{ vars.GHCR_SLUG != '' }}
        working-directory: /tmp/digests
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            $(printf "${VARS_GHCR_SLUG}@sha256:%s " *)
        env:
          VARS_GHCR_SLUG: ${{ vars.GHCR_SLUG }}    
      
      - name: Inspect image dockerhub
        if: ${{ vars.DOCKERHUB_SLUG != '' }}
        run: |
          docker buildx imagetools inspect ${VARS_DOCKERHUB_SLUG}:${STEPS_META_OUTPUTS_VERSION}
        env:
          VARS_DOCKERHUB_SLUG: ${{ vars.DOCKERHUB_SLUG }}
          STEPS_META_OUTPUTS_VERSION: ${{ steps.meta.outputs.version }}

      - name: Inspect image ghcr
        if: ${{ vars.GHCR_SLUG != '' }}
        run: |
            docker buildx imagetools inspect ${VARS_GHCR_SLUG}:${STEPS_META_OUTPUTS_VERSION}   
        env:
          VARS_GHCR_SLUG: ${{ vars.GHCR_SLUG }}
          STEPS_META_OUTPUTS_VERSION: ${{ steps.meta.outputs.version }}