name: Build
on:
  workflow_dispatch:
    inputs:
      version:
        description: "Tag version to release"
        required: true
  push:
    paths-ignore:
      - "docs/**"
      - "README.md"
      - ".github/ISSUE_TEMPLATE/**"
    branches:
      - Alpha
    tags:
      - "v*"
  pull_request_target:
    branches:
      - Alpha
concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: true
  
env:
  REGISTRY: docker.io
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        jobs:
          - { goos: darwin, goarch: arm64, output: arm64 }
          - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible }
          - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64 }

          - { goos: linux, goarch: '386', output: '386' }
          - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible, test: test }
          - { goos: linux, goarch: amd64, goamd64: v3, output: amd64 }
          - { goos: linux, goarch: arm64, output: arm64 }
          - { goos: linux, goarch: arm, goarm: '5', output: armv5 }
          - { goos: linux, goarch: arm, goarm: '6', output: armv6 }
          - { goos: linux, goarch: arm, goarm: '7', output: armv7 }
          - { goos: linux, goarch: mips, mips: hardfloat, output: mips-hardfloat }
          - { goos: linux, goarch: mips, mips: softfloat, output: mips-softfloat }
          - { goos: linux, goarch: mipsle, mips: hardfloat, output: mipsle-hardfloat }
          - { goos: linux, goarch: mipsle, mips: softfloat, output: mipsle-softfloat }
          - { goos: linux, goarch: mips64, output: mips64 }
          - { goos: linux, goarch: mips64le, output: mips64le }
          - { goos: linux, goarch: loong64, output: loong64-abi1, abi: '1' }
          - { goos: linux, goarch: loong64, output: loong64-abi2, abi: '2' }
          - { goos: linux, goarch: riscv64, output: riscv64 }
          - { goos: linux, goarch: s390x, output: s390x }

          - { goos: windows, goarch: '386', output: '386' }
          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible }
          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64 }
          - { goos: windows, goarch: arm, goarm: '7', output: armv7 }
          - { goos: windows, goarch: arm64, output: arm64 }

          - { goos: freebsd, goarch: '386', output: '386' }
          - { goos: freebsd, goarch: amd64, goamd64: v1, output: amd64-compatible }
          - { goos: freebsd, goarch: amd64, goamd64: v3, output: amd64 }
          - { goos: freebsd, goarch: arm64, output: arm64 }

          - { goos: android, goarch: '386', ndk: i686-linux-android34, output: '386' }
          - { goos: android, goarch: amd64, ndk: x86_64-linux-android34, output: amd64 }
          - { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 }
          - { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 }

          # Go 1.21 can revert commit `9e4385` to work on Windows 7
          # https://github.com/golang/go/issues/64622#issuecomment-1847475161
          # (OR we can just use golang1.21.4 which unneeded any patch)
          - { goos: windows, goarch: '386', output: '386-go121', goversion: '1.21' }
          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go121, goversion: '1.21' }
          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go121, goversion: '1.21' }

          # Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016.
          - { goos: windows, goarch: '386', output: '386-go120', goversion: '1.20' }
          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' }
          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' }

          # Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later.
          - { goos: darwin, goarch: arm64, output: arm64-go120, goversion: '1.20' }
          - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' }
          - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' }

          # only for test
          - { goos: linux, goarch: '386', output: '386-go120', goversion: '1.20' }
          - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20', test: test }
          - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' }

    steps:
    - uses: actions/checkout@v4

    - name: Set up Go
      if: ${{ matrix.jobs.goversion == '' && matrix.jobs.goarch != 'loong64' }}
      uses: actions/setup-go@v5
      with:
        go-version: '1.22'

    - name: Set up Go
      if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goarch != 'loong64' }}
      uses: actions/setup-go@v5
      with:
        go-version: ${{ matrix.jobs.goversion }}

    - name: Set up Go1.22 loongarch abi1
      if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }}
      run: |
        wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.22.0/go1.22.0.linux-amd64-abi1.tar.gz
        sudo tar zxf go1.22.0.linux-amd64-abi1.tar.gz -C /usr/local
        echo "/usr/local/go/bin" >> $GITHUB_PATH

    - name: Set up Go1.22 loongarch abi2
      if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }}
      run: |
        wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.22.0/go1.22.0.linux-amd64-abi2.tar.gz
        sudo tar zxf go1.22.0.linux-amd64-abi2.tar.gz -C /usr/local
        echo "/usr/local/go/bin" >> $GITHUB_PATH

      # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
      # this patch file only works on golang1.22.x
      # that means after golang1.23 release it must be changed
      # revert:
      # 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
      # 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
      # 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
    - name: Revert Golang1.22 commit for Windows7/8
      if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }}
      run: |
        cd $(go env GOROOT)
        patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
        patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
        patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff

      # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
    - name: Revert Golang1.21 commit for Windows7/8
      if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.21' }}
      run: |
        cd $(go env GOROOT)
        curl https://github.com/golang/go/commit/9e43850a3298a9b8b1162ba0033d4c53f8637571.diff | patch --verbose -R -p 1

    - name: Set variables
      if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}
      run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
      shell: bash

    - name: Set variables
      if: ${{ github.event_name != 'workflow_dispatch' && github.ref_name == 'Alpha' }}
      run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
      shell: bash

    - name: Set Time Variable
      run: |
        echo "BUILDTIME=$(date)" >> $GITHUB_ENV
        echo "CGO_ENABLED=0" >> $GITHUB_ENV
        echo "BUILDTAG=-extldflags --static" >> $GITHUB_ENV

    - name: Setup NDK
      if: ${{ matrix.jobs.goos == 'android' }}
      uses: nttld/setup-ndk@v1
      id: setup-ndk
      with:
        ndk-version: r26c

    - name: Set NDK path
      if: ${{ matrix.jobs.goos == 'android' }}
      run: |
        echo "CC=${{steps.setup-ndk.outputs.ndk-path}}/toolchains/llvm/prebuilt/linux-x86_64/bin/${{matrix.jobs.ndk}}-clang" >> $GITHUB_ENV
        echo "CGO_ENABLED=1" >> $GITHUB_ENV
        echo "BUILDTAG=" >> $GITHUB_ENV

    - name: Test
      if: ${{ matrix.jobs.test == 'test' }}
      run: |
        go test ./...

    - name: Update CA
      run: |
        sudo apt-get install ca-certificates
        sudo update-ca-certificates
        cp -f /etc/ssl/certs/ca-certificates.crt component/ca/ca-certificates.crt

    - name: Build core
      env:
        GOOS: ${{matrix.jobs.goos}}
        GOARCH: ${{matrix.jobs.goarch}}
        GOAMD64: ${{matrix.jobs.goamd64}}
        GOARM: ${{matrix.jobs.arm}}
        GOMIPS: ${{matrix.jobs.mips}}
      run: |
        echo $CGO_ENABLED
        go build -v -tags "with_gvisor" -trimpath -ldflags "${BUILDTAG} -X 'github.com/metacubex/mihomo/constant.Version=${VERSION}' -X 'github.com/metacubex/mihomo/constant.BuildTime=${BUILDTIME}' -w -s -buildid="
        if [ "${{matrix.jobs.goos}}" = "windows" ]; then
          cp mihomo.exe mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}.exe
          zip -r mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.zip mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}.exe
        else
          cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}
          gzip -c mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}} > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.gz
          rm mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}
        fi

    - name: Create DEB package
      if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') }}
      run: |
        sudo apt-get install dpkg
        if [ "${{matrix.jobs.abi}}" = "1" ]; then
          ARCH=loongarch64
        elif [ "${{matrix.jobs.goarm}}" = "7" ]; then
          ARCH=armhf
        elif [ "${{matrix.jobs.goarch}}" = "arm" ]; then
          ARCH=armel
        else
          ARCH=${{matrix.jobs.goarch}}
        fi
        PackageVersion=$(curl -s "https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" | grep -o '"tag_name": "[^"]*' | grep -o '[^"]*$' | sed 's/v//g' )
        if [ $(git branch | awk -F ' ' '{print $2}') = "Alpha" ]; then
          PackageVersion="$(echo "${PackageVersion}" | awk -F '.' '{$NF = $NF + 1; print}' OFS='.')-${VERSION}"
        fi

        mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN
        mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin
        mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo
        mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/systemd/system/
        mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/share/licenses/mihomo

        cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin/mihomo
        cp LICENSE mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/share/licenses/mihomo/
        cp .github/mihomo.service mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/systemd/system/

        cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo/config.yaml <<EOF
        mixed-port: 7890
        external-controller: 127.0.0.1:9090
        EOF

        cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN/control <<EOF
        Package: mihomo
        Version: ${PackageVersion}
        Section:
        Priority: extra
        Architecture: ${ARCH}
        Maintainer: MetaCubeX <none@example.com>
        Homepage: https://wiki.metacubex.one/
        Description: The universal proxy platform.
        EOF

        dpkg-deb -Z gzip --build mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}

    - name: Convert DEB to RPM
      if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') }}
      run: |
        sudo apt-get install -y alien
        alien --to-rpm --scripts mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb
        mv mihomo*.rpm mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.rpm

    # - name: Convert DEB to PKG
    #   if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') && !contains(matrix.jobs.goarch, 'loong64') }}
    #   run: |
    #     docker pull archlinux
    #     docker run --rm -v ./:/mnt archlinux bash -c "
    #       pacman -Syu pkgfile base-devel --noconfirm
    #       curl -L https://github.com/helixarch/debtap/raw/master/debtap > /usr/bin/debtap
    #       chmod 755 /usr/bin/debtap
    #       debtap -u 
    #       debtap -Q /mnt/mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb
    #     "
    #     mv mihomo*.pkg.tar.zst mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.pkg.tar.zst

    - name: Save version
      run: |
        echo ${VERSION} > version.txt
      shell: bash

    - name: Archive production artifacts
      uses: actions/upload-artifact@v4
      with:
        name: "${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}"
        path: |
          mihomo*.gz
          mihomo*.deb
          mihomo*.rpm
          mihomo*.zip
          version.txt

  Upload-Prerelease:
    permissions: write-all
    if: ${{ github.event_name != 'workflow_dispatch' && github.ref_type == 'branch' && !startsWith(github.event_name, 'pull_request') }}
    needs: [build]
    runs-on: ubuntu-latest
    steps:
    - name: Download all workflow run artifacts
      uses: actions/download-artifact@v4
      with:
        path: bin/
        merge-multiple: true

    - name: Delete current release assets
      uses: 8Mi-Tech/delete-release-assets-action@main
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        tag: Prerelease-${{ github.ref_name }}
        deleteOnlyFromDrafts: false
    - name: Set Env
      run: |
        echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
      shell: bash

    - name: Tag Repo
      uses: richardsimko/update-tag@v1
      with:
        tag_name: Prerelease-${{ github.ref_name }}
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      
    - run: |
        cat > release.txt << 'EOF'
        Release created at  ${{ env.BUILDTIME }}
        Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version
        <br>
        [我应该下载哪个文件? / Which file should I download?](https://github.com/MetaCubeX/mihomo/wiki/FAQ)
        [二进制文件筛选 / Binary file selector](https://metacubex.github.io/Meta-Docs/startup/#_1)
        [查看文档 / Docs](https://metacubex.github.io/Meta-Docs/)
        EOF

    - name: Upload Prerelease
      uses: softprops/action-gh-release@v1
      if: ${{  success() }}
      with:
        tag_name: Prerelease-${{ github.ref_name }}
        files: |
          bin/*
        prerelease: true
        generate_release_notes: true
        body_path: release.txt

  Upload-Release:
    permissions: write-all
    if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}
    needs: [build]
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: Meta
          fetch-depth: '0'
          fetch-tags: 'true'

      - name: Get tags
        run: |
          echo "CURRENTVERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
          git fetch --tags
          echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD)" >> $GITHUB_ENV

      - name: Merge Alpha branch into Meta
        run: |
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"
          git fetch origin Alpha:Alpha
          git merge Alpha
          git push origin Meta
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Tag the commit
        run: |
          git tag ${{ github.event.inputs.version }}
          git push origin ${{ github.event.inputs.version }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Generate release notes
        run: |
            cp ./.github/genReleaseNote.sh ./
            bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION}
            rm ./genReleaseNote.sh
  
      - uses: actions/download-artifact@v4
        with:
          path: bin/
          merge-multiple: true
  
      - name: Display structure of downloaded files
        run: ls -R
        working-directory: bin
  
      - name: Upload Release
        uses: softprops/action-gh-release@v2
        if: ${{ success() }}
        with:
          tag_name: ${{ github.event.inputs.version }}
          files: bin/*
          body_path: release.md

  Docker:
    if: ${{ !startsWith(github.event_name, 'pull_request') }}
    permissions: write-all
    needs: [build]
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/download-artifact@v4
        with:
          path: bin/
          merge-multiple: true

      - name: Display structure of downloaded files
        run: ls -R
        working-directory: bin

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

      - name: Setup Docker buildx
        uses: docker/setup-buildx-action@v3
        with:
          version: latest
      
      # Extract metadata (tags, labels) for Docker
      # https://github.com/docker/metadata-action
      - name: Extract Docker metadata
        if: ${{ github.event_name != 'workflow_dispatch' }}
        id: meta_alpha
        uses: docker/metadata-action@v5
        with:
          images: '${{ env.REGISTRY }}/${{ github.repository }}'
      
      # Extract metadata (tags, labels) for Docker
      # https://github.com/docker/metadata-action
      - name: Extract Docker metadata
        if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}
        id: meta_release
        uses: docker/metadata-action@v5
        with:
          images: '${{ env.REGISTRY }}/${{ github.repository }}'
          tags: |
            ${{ github.event.inputs.version }}
          flavor: |
            latest=true
          labels: org.opencontainers.image.version=${{ github.event.inputs.version }}
      
      - name: Show files
        run: |
          ls .
          ls bin/
      
      - name: login to docker REGISTRY
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.DOCKER_HUB_USER }}
          password: ${{ secrets.DOCKER_HUB_TOKEN }}

      # Build and push Docker image with Buildx (don't push on PR)
      # https://github.com/docker/build-push-action
      - name: Build and push Docker image
        if: ${{ github.event_name != 'workflow_dispatch' }}
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile
          push: ${{ github.event_name != 'pull_request' }}
          platforms: |
            linux/386
            linux/amd64
            linux/arm64
            linux/arm/v7
          tags: ${{ steps.meta_alpha.outputs.tags }}
          labels: ${{ steps.meta_alpha.outputs.labels }}
      
      - name: Build and push Docker image
        if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile
          push: ${{ github.event_name != 'pull_request' }}
          platforms: |
            linux/386
            linux/amd64
            linux/arm64
            linux/arm/v7
          tags: ${{ steps.meta_release.outputs.tags }}
          labels: ${{ steps.meta_release.outputs.labels }}