---
# Github Actions release for rclone
# -*- compile-command: "yamllint -f parsable build_publish_docker_image.yml" -*-

name: Build & Push Docker Images

# Trigger the workflow on push or pull request
on:
  push:
    branches:
      - '**'
    tags:
      - '**'
  workflow_dispatch:
    inputs:
      manual:
        description: Manual run (bypass default conditions)
        type: boolean
        default: true

jobs:
  build-image:
    if: inputs.manual || (github.repository == 'rclone/rclone' && github.event_name != 'pull_request')
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - platform: linux/amd64
            runs-on: ubuntu-24.04
          - platform: linux/386
            runs-on: ubuntu-24.04
          - platform: linux/arm64
            runs-on: ubuntu-24.04-arm
          - platform: linux/arm/v7
            runs-on: ubuntu-24.04-arm
          - platform: linux/arm/v6
            runs-on: ubuntu-24.04-arm

    name: Build Docker Image for ${{ matrix.platform }}
    runs-on: ${{ matrix.runs-on }}

    steps:
      - name: Free Space
        shell: bash
        run: |
          df -h .
          # Remove android SDK
          sudo rm -rf /usr/local/lib/android || true
          # Remove .net runtime
          sudo rm -rf /usr/share/dotnet || true
          df -h .

      - name: Checkout Repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Set REPO_NAME Variable
        run: |
          echo "REPO_NAME=`echo ${{github.repository}} | tr '[:upper:]' '[:lower:]'`" >> ${GITHUB_ENV}

      - name: Set PLATFORM Variable
        run: |
          platform=${{ matrix.platform }}
          echo "PLATFORM=${platform//\//-}" >> $GITHUB_ENV

      - name: Set CACHE_NAME Variable
        shell: python
        run: |
          import os, re

          def slugify(input_string, max_length=63):
            slug = input_string.lower()
            slug = re.sub(r'[^a-z0-9 -]', ' ', slug)
            slug = slug.strip()
            slug = re.sub(r'\s+', '-', slug)
            slug = re.sub(r'-+', '-', slug)
            slug = slug[:max_length]
            slug = re.sub(r'[-]+$', '', slug)
            return slug

          ref_name_slug = "cache"

          if os.environ.get("GITHUB_REF_NAME") and os.environ['GITHUB_EVENT_NAME'] == "pull_request":
            ref_name_slug += "-pr-" + slugify(os.environ['GITHUB_REF_NAME'])

          with open(os.environ['GITHUB_ENV'], 'a') as env:
              env.write(f"CACHE_NAME={ref_name_slug}\n")

      - name: Get ImageOS
        # There's no way around this, because "ImageOS" is only available to
        # processes, but the setup-go action uses it in its key.
        id: imageos
        uses: actions/github-script@v8
        with:
          result-encoding: string
          script: |
            return process.env.ImageOS

      - name: Extract Metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        env:
          DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,manifest-descriptor # Important for digest annotation (used by Github packages)
        with:
          images: |
            ghcr.io/${{ env.REPO_NAME }}
          labels: |
            org.opencontainers.image.url=https://github.com/rclone/rclone/pkgs/container/rclone
            org.opencontainers.image.vendor=${{ github.repository_owner }}
            org.opencontainers.image.authors=rclone <https://github.com/rclone>
            org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
            org.opencontainers.image.revision=${{ github.sha }}
          tags: |
            type=sha
            type=ref,event=pr
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}
            type=semver,pattern={{major}}.{{minor}}
            type=raw,value=beta,enable={{is_default_branch}}

      - name: Setup QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Load Go Build Cache for Docker
        id: go-cache
        uses: actions/cache@v4
        with:
          key: ${{ runner.os }}-${{ steps.imageos.outputs.result }}-go-${{ env.CACHE_NAME }}-${{ env.PLATFORM }}-${{ hashFiles('**/go.mod') }}-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-${{ steps.imageos.outputs.result }}-go-${{ env.CACHE_NAME }}-${{ env.PLATFORM }}
          # Cache only the go builds, the module download is cached via the docker layer caching
          path: |
            go-build-cache

      - name: Inject Go Build Cache into Docker
        uses: reproducible-containers/buildkit-cache-dance@v3
        with:
          cache-map: |
            {
              "go-build-cache": "/root/.cache/go-build"
            }
          skip-extraction: ${{ steps.go-cache.outputs.cache-hit }}

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          # This is the user that triggered the Workflow. In this case, it will
          # either be the user whom created the Release or manually triggered
          # the workflow_dispatch.
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Publish Image Digest
        id: build
        uses: docker/build-push-action@v6
        with:
          file: Dockerfile
          context: .
          provenance: false
          # don't specify 'tags' here (error "get can't push tagged ref by digest")
          # tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          annotations: ${{ steps.meta.outputs.annotations }}
          platforms: ${{ matrix.platform }}
          outputs: |
            type=image,name=ghcr.io/${{ env.REPO_NAME }},push-by-digest=true,name-canonical=true,push=true
          cache-from: |
            type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:build-${{ env.CACHE_NAME }}-${{ env.PLATFORM }}
          cache-to: |
            type=registry,ref=ghcr.io/${{ env.REPO_NAME }}:build-${{ env.CACHE_NAME }}-${{ env.PLATFORM }},image-manifest=true,mode=max,compression=zstd

      - name: Export Image Digest
        run: |
          mkdir -p /tmp/digests
          digest="${{ steps.build.outputs.digest }}"
          touch "/tmp/digests/${digest#sha256:}"

      - name: Upload Image Digest
        uses: actions/upload-artifact@v5
        with:
          name: digests-${{ env.PLATFORM }}
          path: /tmp/digests/*
          retention-days: 1
          if-no-files-found: error

  merge-image:
    name: Merge & Push Final Docker Image
    runs-on: ubuntu-24.04
    needs:
      - build-image

    steps:
      - name: Download Image Digests
        uses: actions/download-artifact@v6
        with:
          path: /tmp/digests
          pattern: digests-*
          merge-multiple: true

      - name: Set REPO_NAME Variable
        run: |
          echo "REPO_NAME=`echo ${{github.repository}} | tr '[:upper:]' '[:lower:]'`" >> ${GITHUB_ENV}

      - name: Extract Metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v5
        env:
          DOCKER_METADATA_ANNOTATIONS_LEVELS: index
        with:
          images: |
            ${{ env.REPO_NAME }}
            ghcr.io/${{ env.REPO_NAME }}
          labels: |
            org.opencontainers.image.url=https://github.com/rclone/rclone/pkgs/container/rclone
            org.opencontainers.image.vendor=${{ github.repository_owner }}
            org.opencontainers.image.authors=rclone <https://github.com/rclone>
            org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
            org.opencontainers.image.revision=${{ github.sha }}
          tags: |
            type=sha
            type=ref,event=pr
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}
            type=semver,pattern={{major}}.{{minor}}
            type=raw,value=beta,enable={{is_default_branch}}

      - name: Extract Tags
        shell: python
        run: |
          import json, os

          metadata_json = os.environ['DOCKER_METADATA_OUTPUT_JSON']
          metadata = json.loads(metadata_json)

          tags = [f"--tag '{tag}'" for tag in metadata["tags"]]
          tags_string = " ".join(tags)

          with open(os.environ['GITHUB_ENV'], 'a') as env:
              env.write(f"TAGS={tags_string}\n")

      - name: Extract Annotations
        shell: python
        run: |
          import json, os

          metadata_json = os.environ['DOCKER_METADATA_OUTPUT_JSON']
          metadata = json.loads(metadata_json)

          annotations = [f"--annotation '{annotation}'" for annotation in metadata["annotations"]]
          annotations_string = " ".join(annotations)

          with open(os.environ['GITHUB_ENV'], 'a') as env:
              env.write(f"ANNOTATIONS={annotations_string}\n")

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          # This is the user that triggered the Workflow. In this case, it will
          # either be the user whom created the Release or manually triggered
          # the workflow_dispatch.
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Create & Push Manifest List
        working-directory: /tmp/digests
        run: |
          docker buildx imagetools create \
          ${{ env.TAGS }} \
          ${{ env.ANNOTATIONS }} \
          $(printf 'ghcr.io/${{ env.REPO_NAME }}@sha256:%s ' *)

      - name: Inspect and Run Multi-Platform Image
        run: |
          docker buildx imagetools inspect --raw ${{ env.REPO_NAME }}:${{ steps.meta.outputs.version }}
          docker buildx imagetools inspect --raw ghcr.io/${{ env.REPO_NAME }}:${{ steps.meta.outputs.version }}
          docker run --rm ghcr.io/${{ env.REPO_NAME }}:${{ steps.meta.outputs.version }} version
