---
name: Build binary releases
concurrency:
  cancel-in-progress: true
  group: ${{ github.workflow }}-${{ github.ref }}
on:
  pull_request:
    branches:
      - main
    paths-ignore:
      - 'docs/**'
  push:
    branches:
      - main
    tags:
      - v*.*.*
  workflow_dispatch:
    inputs:
      version:
        description: 'FrankenPHP version'
        required: false
        type: string
  schedule:
    - cron:  '0 0 * * *'
env:
  IMAGE_NAME: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/')) && 'dunglas/frankenphp' || 'dunglas/frankenphp-dev' }}    
jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      push: ${{ toJson((steps.check.outputs.ref || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request')) && true || false) }}
      platforms: ${{ steps.matrix.outputs.platforms }}
      metadata: ${{ steps.matrix.outputs.metadata }}
      ref: ${{ steps.check.outputs.ref }}
    steps:
      -
        name: Get version
        id: check
        if: github.event_name == 'schedule'
        run: |
          ref="${{ (github.ref_type == 'tag' && github.ref_name) || (github.event_name == 'workflow_dispatch' && inputs.version) || '' }}"
          if [[ -z "${ref}" ]]; then
            ref="$(gh release view --repo dunglas/frankenphp --json tagName --jq '.tagName')"
          fi

          echo "ref=${ref}" >> "${GITHUB_OUTPUT}"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      -
        uses: actions/checkout@v4
        with:
          ref: ${{ steps.check.outputs.ref }}
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      -
        name: Create platforms matrix
        id: matrix
        run: |
          METADATA="$(docker buildx bake --print static-builder | jq -c)"
          {
            echo metadata="${METADATA}"
            echo platforms="$(jq -c 'first(.target[]) | .platforms' <<< "${METADATA}")"
          } >> "${GITHUB_OUTPUT}"
        env:
          SHA: ${{ github.sha }}
          VERSION: ${{ steps.check.outputs.ref || 'dev' }}
  build-linux:
    strategy:
      fail-fast: false
      matrix:
        platform: ${{ fromJson(needs.prepare.outputs.platforms) }}
        debug: [false]
        mimalloc: [false]
        include:
          -
            qemu: true
          -
            platform: linux/amd64
            qemu: false
          -
            platform: linux/amd64
            qemu: false
            debug: true
          -
            platform: linux/amd64
            qemu: false
            mimalloc: true
    name: Build ${{ matrix.platform }} static binary${{ matrix.debug && ' (debug)' || '' }}${{ matrix.mimalloc && ' (mimalloc)' || '' }}
    runs-on: ubuntu-latest
    needs: [ prepare ]
    steps:
      -
        uses: actions/checkout@v4
        with:
          ref: ${{ needs.prepare.outputs.ref }}
      -
        name: Set up QEMU
        if: matrix.qemu
        uses: docker/setup-qemu-action@v3
        with:
          platforms: ${{ matrix.platform }}
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          platforms: ${{ matrix.platform }}
      -
        name: Login to DockerHub
        if: ${{ fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc }}
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}    
      -
        name: Build
        id: build
        uses: docker/bake-action@v5
        with:
          pull: true
          load: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug || matrix.mimalloc }}
          targets: static-builder
          set: |
            ${{ matrix.debug && 'static-builder.args.DEBUG_SYMBOLS=1' || '' }}
            ${{ matrix.mimalloc && 'static-builder.args.MIMALLOC=1' || '' }}
            ${{ matrix.platform == 'linux/arm64' && 'static-builder.args.NO_COMPRESS=1' || '' }}
            *.tags=
            *.platform=${{ matrix.platform }}
            *.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
            *.cache-from=type=gha,scope=refs/heads/main-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
            *.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }},ignore-error=true
            ${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
        env:
          SHA: ${{ github.sha }}
          VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref || 'dev' }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      -
        # Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
        name: Export metadata
        if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc
        run: |
          mkdir -p /tmp/metadata

          # shellcheck disable=SC2086
          digest=$(jq -r '."static-builder"."containerimage.digest"' <<< ${METADATA})
          touch "/tmp/metadata/${digest#sha256:}"
        env:
          METADATA: ${{ steps.build.outputs.metadata }}
      -
        name: Upload metadata
        if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc
        uses: actions/upload-artifact@v3
        with:
          name: metadata-static-builder
          path: /tmp/metadata/*
          if-no-files-found: error
          retention-days: 1
      -
        name: Copy binary
        if: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug || matrix.mimalloc }}
        run: |
          digest=$(jq -r '."static-builder"."containerimage.config.digest"' <<< "${METADATA}")
          docker create --platform=${{ matrix.platform }} --name static-builder "${digest}"
          docker cp "static-builder:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}"
        env:
          METADATA: ${{ steps.build.outputs.metadata }}
          BINARY: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}
      -
        name: Upload artifact
        if: ${{ !fromJson(needs.prepare.outputs.push) }}
        uses: actions/upload-artifact@v3
        with:
          name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
          path: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
      -
        name: Upload special assets
        if: fromJson(needs.prepare.outputs.push) && (matrix.debug || matrix.mimalloc) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
        run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" frankenphp-linux-x86_64${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }} --repo dunglas/frankenphp --clobber
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
  push:
    runs-on: ubuntu-latest
    needs:
      - prepare
      - build-linux
    if: fromJson(needs.prepare.outputs.push)
    steps:
      -
        name: Download metadata
        uses: actions/download-artifact@v3
        with:
          name: metadata-static-builder
          path: /tmp/metadata
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      -
        name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}
      -
        name: Create manifest list and push
        working-directory: /tmp/metadata
        run: |
          # shellcheck disable=SC2046,SC2086
          docker buildx imagetools create $(jq -cr '.target."static-builder".tags | map("-t " + .) | join(" ")' <<< "${METADATA}") \
            $(printf "${IMAGE_NAME}@sha256:%s " *)
        env:
          METADATA: ${{ needs.prepare.outputs.metadata }}
      -
        name: Inspect image
        run: |
          # shellcheck disable=SC2046,SC2086
          docker buildx imagetools inspect "$(jq -cr '.target."static-builder".tags | first' <<< "${METADATA}")"
        env:
          METADATA: ${{ needs.prepare.outputs.metadata }}  
      -
        name: Copy binary
        run: |
          tag=$(jq -cr '.target."static-builder".tags | first' <<< "${METADATA}")
          docker cp "$(docker create --platform=linux/amd64 --name static-builder "${tag}"):/go/src/app/dist/frankenphp-linux-x86_64" frankenphp-linux-x86_64 ; docker rm static-builder
          docker cp "$(docker create --platform=linux/arm64 --name static-builder "${tag}"):/go/src/app/dist/frankenphp-linux-aarch64" frankenphp-linux-aarch64 ; docker rm static-builder
        env:
          METADATA: ${{ needs.prepare.outputs.metadata }}
      -
        name: Upload asset
        if: needs.prepare.outputs.ref || github.ref_type == 'tag'
        run: gh release upload "${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}" frankenphp-linux-x86_64 frankenphp-linux-aarch64 --repo dunglas/frankenphp --clobber
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  build-mac:
    strategy:
      fail-fast: false
      matrix:
        platform: ['arm64', 'x86_64']
    name: Build macOS ${{ matrix.platform }} binaries
    runs-on: ${{ matrix.platform == 'arm64' && 'macos-14' || 'macos-13' }}
    needs: [ prepare ]
    env:
      HOMEBREW_NO_AUTO_UPDATE: 1
    steps:
      -
        uses: actions/checkout@v4
        with:
          ref: ${{ needs.prepare.outputs.ref }}
      -
        uses: actions/setup-go@v5
        with:
          go-version: '1.22'
          cache-dependency-path: |
            go.sum 
            caddy/go.sum
      -
        name: Set FRANKENPHP_VERSION
        run: |
          if [ "${GITHUB_REF_TYPE}" == "tag" ]; then
            export FRANKENPHP_VERSION=${GITHUB_REF_NAME:1}
          elif [ "${GITHUB_EVENT_NAME}" == "schedule" ]; then
            export FRANKENPHP_VERSION="${{ needs.prepare.outputs.ref }}"
          else
            export FRANKENPHP_VERSION=${GITHUB_SHA}
          fi

          echo "FRANKENPHP_VERSION=${FRANKENPHP_VERSION}" >> "${GITHUB_ENV}"
      -
        name: Build FrankenPHP
        run: ./build-static.sh
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          RELEASE: ${{ (needs.prepare.outputs.ref || github.ref_type == 'tag') && '1' || '' }}
      -
        name: Upload artifact
        if: github.ref_type == 'branch'
        uses: actions/upload-artifact@v3
        with:
          name: frankenphp-mac-${{ matrix.platform }}
          path: dist/frankenphp-mac-${{ matrix.platform }}
