name: Release

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

jobs:
  macos:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: aarch64-apple-darwin
      - name: Build
        uses: PyO3/maturin-action@v1
        with:
          args: --release --out dist --strip --universal2
          # sccache: 'true'
      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-macos-universal2
          path: dist
      - name: Upload binary artifacts
        uses: actions/upload-artifact@v4
        with:
          name: apple-darwin-bin
          path: target/release/cargo-zigbuild
          if-no-files-found: error
      - name: Archive binary
        if: "startsWith(github.ref, 'refs/tags/')"
        run: |
          cd target/release
          tar czvf cargo-zigbuild-${{ github.ref_name }}.apple-darwin.tar.gz cargo-zigbuild
          shasum -a 256 cargo-zigbuild-${{ github.ref_name }}.apple-darwin.tar.gz > cargo-zigbuild-${{ github.ref_name }}.apple-darwin.tar.gz.sha256
          cd -
      - name: Upload binary to GitHub Release
        if: "startsWith(github.ref, 'refs/tags/')"
        uses: softprops/action-gh-release@v2
        with:
          files: |
            target/release/cargo-zigbuild*.tar.gz*
          prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }}
          generate_release_notes: true

  windows:
    runs-on: windows-latest
    strategy:
      matrix:
        platform:
          - arch: x64
            target: x86_64-pc-windows-msvc
          - arch: x86
            target: i686-pc-windows-msvc
          - arch: arm64
            target: aarch64-pc-windows-msvc
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
      - uses: dtolnay/rust-toolchain@stable
      - name: Build
        uses: PyO3/maturin-action@v1
        with:
          target: ${{ matrix.platform.target }}
          args: --release --out dist --strip
          # sccache: 'true'
      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.platform.arch }}
          path: dist
      - name: Upload binary artifacts
        uses: actions/upload-artifact@v4
        with:
          name: windows-${{ matrix.platform.arch }}-bin
          path: target/${{ matrix.platform.target }}/release/cargo-zigbuild.exe
          if-no-files-found: error
      - name: Archive binary
        if: "startsWith(github.ref, 'refs/tags/')"
        run: |
          cd target/${{ matrix.platform.target }}/release
          7z a cargo-zigbuild-${{ github.ref_name }}.windows-${{ matrix.platform.arch }}.zip cargo-zigbuild.exe
          $sha256 = (Get-FileHash cargo-zigbuild-${{ github.ref_name }}.windows-${{ matrix.platform.arch }}.zip -Algorithm SHA256).Hash.ToLower()
          "$sha256  cargo-zigbuild-${{ github.ref_name }}.windows-${{ matrix.platform.arch }}.zip" | Out-File cargo-zigbuild-${{ github.ref_name }}.windows-${{ matrix.platform.arch }}.zip.sha256
          cd -
      - name: Upload binary to GitHub Release
        if: "startsWith(github.ref, 'refs/tags/')"
        uses: softprops/action-gh-release@v2
        with:
          files: |
            target/${{ matrix.platform.target }}/release/cargo-zigbuild-*.zip*
          prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }}
          generate_release_notes: true

  linux:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        platform: [
          { target: "x86_64-unknown-linux-musl", image_tag: "x86_64-musl" },
          { target: "i686-unknown-linux-musl", image_tag: "i686-musl" },
          { target: "aarch64-unknown-linux-musl", image_tag: "aarch64-musl" },
          { target: "armv7-unknown-linux-musleabihf", image_tag: "armv7-musleabihf" },
          { target: "armv7-unknown-linux-musleabi", image_tag: "armv7-musleabi" },
        ]
    container:
      image: docker://ghcr.io/rust-cross/rust-musl-cross:${{ matrix.platform.image_tag }}
      env:
        CFLAGS_armv7_unknown_linux_musleabihf: '-mfpu=vfpv3-d16'
    steps:
      - uses: actions/checkout@v4
      - name: Build
        uses: PyO3/maturin-action@main
        with:
          target: ${{ matrix.platform.target }}
          manylinux: auto
          container: off
          args: --release -o dist --strip
          # sccache: 'true'
      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.platform.target }}
          path: dist
      - name: Upload binary artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.platform.target }}-bin
          path: target/${{ matrix.platform.target }}/release/cargo-zigbuild
          if-no-files-found: error
      - name: Archive binary
        if: "startsWith(github.ref, 'refs/tags/')"
        run: |
          cd target/${{ matrix.platform.target }}/release
          tar czvf cargo-zigbuild-${{ github.ref_name }}.${{ matrix.platform.target }}.tar.gz cargo-zigbuild
          shasum -a 256 cargo-zigbuild-${{ github.ref_name }}.${{ matrix.platform.target }}.tar.gz > cargo-zigbuild-${{ github.ref_name }}.${{ matrix.platform.target }}.tar.gz.sha256
          cd -
      - name: Upload binary to GitHub Release
        if: "startsWith(github.ref, 'refs/tags/')"
        uses: softprops/action-gh-release@v2
        with:
          files: |
            target/${{ matrix.platform.target }}/release/cargo-zigbuild*.tar.gz*
          prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }}
          generate_release_notes: true

  docker:
    name: Build Docker Image
    if: github.event_name != 'pull_request'
    runs-on: ubuntu-latest
    environment: Docker Hub
    steps:
      - uses: actions/checkout@v4
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ${{ secrets.DOCKER_USERNAME }}/cargo-zigbuild
            ghcr.io/${{ github.repository_owner }}/cargo-zigbuild
          tags: |
            type=schedule
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            type=sha
      - name: Setup QEMU
        uses: dbhi/qus/action@main
      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: docker build
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/386,linux/arm64
          push: ${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - name: Docker Hub Description
        uses: peter-evans/dockerhub-description@v4
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}
          repository: ${{ secrets.DOCKER_USERNAME }}/cargo-zigbuild

  docker-win:
    name: Build Docker Image (Windows)
    if: github.event_name != 'pull_request'
    runs-on: windows-latest
    environment: Docker Hub
    defaults:
      run:
        shell: pwsh
    steps:
      - uses: actions/checkout@v4
      - id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ${{ secrets.DOCKER_USERNAME }}/cargo-zigbuild.windows
            ghcr.io/${{ github.repository_owner }}/cargo-zigbuild.windows
          tags: |
            type=schedule
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            type=sha
      - name: Install containerd
        run: |
          # source: # https://github.com/containerd/containerd/blob/main/docs/getting-started.md#installing-containerd-on-windows
          $url = "https://api.github.com/repos/containerd/containerd/releases/latest"
          $Version = (Invoke-RestMethod -Uri $url -UseBasicParsing).tag_name.TrimStart('v')
          $Arch = "amd64"	# arm64 also available
          curl.exe -LO https://github.com/containerd/containerd/releases/download/v$Version/containerd-$Version-windows-$Arch.tar.gz
          tar.exe xvf .\containerd-$Version-windows-amd64.tar.gz
          Copy-Item -Path .\bin -Destination $Env:ProgramFiles\containerd -Recurse -Force
          $Path = [Environment]::GetEnvironmentVariable("PATH", "Machine") + [IO.Path]::PathSeparator + "$Env:ProgramFiles\containerd"
          [Environment]::SetEnvironmentVariable( "Path", $Path, "Machine")
          $Env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
          containerd.exe config default | Out-File $Env:ProgramFiles\containerd\config.toml -Encoding ascii
          containerd.exe --register-service
          Start-Service containerd
      - name: Setup windows-container-networking-cni
        run: |
          # source: https://github.com/moby/buildkit/blob/master/docs/windows.md
          $networkName = 'nat'
          $natInfo = Get-HnsNetwork -ErrorAction Ignore | Where-Object { $_.Name -eq $networkName }
          $gateway = $natInfo.Subnets[0].GatewayAddress
          $subnet = $natInfo.Subnets[0].AddressPrefix
          $cniConfPath = "$env:ProgramFiles\containerd\cni\conf\0-containerd-nat.conf"
          $cniBinDir = "$env:ProgramFiles\containerd\cni\bin"
          $url = "https://api.github.com/repos/microsoft/windows-container-networking/releases/latest"
          # fix $cniVersion to 0.3.0 as buildkit does not support windows-cni 0.3.1+ (at the time of writing this workflow)
          $cniVersion = '0.3.0' # (Invoke-RestMethod -Uri $url -UseBasicParsing).tag_name.TrimStart('v')
          mkdir $cniBinDir -Force
          curl.exe -LO https://github.com/microsoft/windows-container-networking/releases/download/v$cniVersion/windows-container-networking-cni-amd64-v$cniVersion.zip
          tar xvf windows-container-networking-cni-amd64-v$cniVersion.zip -C $cniBinDir
          $natConfig = @"
          {
              "cniVersion": "$cniVersion",
              "name": "$networkName",
              "type": "nat",
              "master": "Ethernet",
              "ipam": {
                  "subnet": "$subnet",
                  "routes": [
                      {
                          "gateway": "$gateway"
                      }
                  ]
              },
              "capabilities": {
                  "portMappings": true,
                  "dns": true
              }
          }
          "@
          Set-Content -Path $cniConfPath -Value $natConfig
      - name: Setup Buildkit
        run: |
          # source: https://github.com/moby/buildkit/blob/master/docs/windows.md
          $url = "https://api.github.com/repos/moby/buildkit/releases/latest"
          $version = (Invoke-RestMethod -Uri $url -UseBasicParsing).tag_name
          $arch = "amd64" # arm64 binary available too
          curl.exe -LO https://github.com/moby/buildkit/releases/download/$version/buildkit-$version.windows-$arch.tar.gz
          mv bin bin2
          tar.exe xvf .\buildkit-$version.windows-$arch.tar.gz
          Copy-Item -Path ".\bin" -Destination "$Env:ProgramFiles\buildkit" -Recurse -Force
          $Path = [Environment]::GetEnvironmentVariable("PATH", "Machine") + [IO.Path]::PathSeparator + "$Env:ProgramFiles\buildkit"
          [Environment]::SetEnvironmentVariable( "Path", $Path, "Machine")
          $Env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
          buildkitd --register-service --service-name buildkitd --containerd-cni-config-path="C:\Program Files\containerd\cni\conf\0-containerd-nat.conf" --containerd-cni-binary-dir="C:\Program Files\containerd\cni\bin"
          Start-Service buildkitd
      - uses: docker/setup-buildx-action@v3
        with:
          # https://github.com/microsoft/Windows-Containers/blob/Main/helpful_tools/GitHubActions/install_buildkit_workflow.yaml
          driver: remote
          endpoint: npipe:////./pipe/buildkitd
      - uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          file: Dockerfile.windows
          platforms: windows/amd64
          push: ${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      - uses: peter-evans/dockerhub-description@v4
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}
          repository: ${{ secrets.DOCKER_USERNAME }}/cargo-zigbuild

  release:
    name: Release
    runs-on: ubuntu-latest
    if: "startsWith(github.ref, 'refs/tags/')"
    needs: [ macos, windows, linux ]
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: wheels-*
          merge-multiple: true
      - name: Publish to PyPI
        env:
          MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_PASSWORD }}
        uses: PyO3/maturin-action@v1
        with:
          command: upload
          args: --skip-existing *
