name: EasyTier Core

on:
  push:
    branches: [ "develop", "main", "releases/**" ]
  pull_request:
    branches: [ "develop", "main" ]
    types: [ opened, synchronize, reopened, ready_for_review ]

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

env:
  CARGO_TERM_COLOR: always

defaults:
  run:
    # necessary for windows
    shell: bash

jobs:
  pre_job:
    # continue-on-error: true # Uncomment once integration is finished
    runs-on: ubuntu-latest
    if: github.event_name != 'pull_request' || !github.event.pull_request.draft
    # Map a step output to a job output
    outputs:
      # do not skip push on branch starts with releases/
      should_skip: ${{ steps.skip_check.outputs.should_skip == 'true' && !startsWith(github.ref_name, 'releases/') }}
    steps:
      - id: skip_check
        uses: fkirc/skip-duplicate-actions@v5
        with:
          # All of these options are optional, so you can remove them if you are happy with the defaults
          concurrent_skipping: 'same_content_newer'
          skip_after_successful_duplicate: 'true'
          cancel_others: 'true'
          paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/core.yml", ".github/actions/**", "easytier-web/**"]'
  build_web:
    runs-on: ubuntu-latest
    needs: pre_job
    if: needs.pre_job.outputs.should_skip != 'true'
    steps:
      - uses: actions/checkout@v5

      - name: Setup Frontend Environment
        uses: ./.github/actions/prepare-pnpm
        with:
          build-filter: './easytier-web/*'

      - name: Archive artifact
        uses: actions/upload-artifact@v5
        with:
          name: easytier-web-dashboard
          path: |
            easytier-web/frontend/dist/*
  build:
    strategy:
      fail-fast: true
      matrix:
        include:
          - TARGET: x86_64-unknown-linux-musl
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-x86_64
          - TARGET: aarch64-unknown-linux-musl
            OS: ubuntu-24.04-arm
            ARTIFACT_NAME: linux-aarch64

          - TARGET: riscv64gc-unknown-linux-musl
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-riscv64
          - TARGET: loongarch64-unknown-linux-musl
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-loongarch64

          - TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-armv7hf
          - TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-armv7
          - TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-armhf
          - TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-arm

          - TARGET: mips-unknown-linux-musl
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-mips
          - TARGET: mipsel-unknown-linux-musl
            OS: ubuntu-24.04
            ARTIFACT_NAME: linux-mipsel

          - TARGET: x86_64-unknown-freebsd
            OS: ubuntu-24.04
            ARTIFACT_NAME: freebsd-13.2-x86_64
            BSD_VERSION: 13.2

          - TARGET: x86_64-apple-darwin
            OS: macos-latest
            ARTIFACT_NAME: macos-x86_64
          - TARGET: aarch64-apple-darwin
            OS: macos-latest
            ARTIFACT_NAME: macos-aarch64

          - TARGET: x86_64-pc-windows-msvc
            OS: windows-latest
            ARTIFACT_NAME: windows-x86_64
          - TARGET: i686-pc-windows-msvc
            OS: windows-latest
            ARTIFACT_NAME: windows-i686
          - TARGET: aarch64-pc-windows-msvc
            OS: windows-11-arm
            ARTIFACT_NAME: windows-arm64

    runs-on: ${{ matrix.OS }}
    env:
      NAME: easytier
      TARGET: ${{ matrix.TARGET }}
      OS: ${{ matrix.OS }}
      OSS_BUCKET: ${{ secrets.ALIYUN_OSS_BUCKET }}
    needs:
      - pre_job
      - build_web
    if: needs.pre_job.outputs.should_skip != 'true'
    steps:
      - uses: actions/checkout@v5

      - name: Set current ref as env variable
        run: |
          echo "GIT_DESC=$(git log -1 --format=%cd.%h --date=format:%Y-%m-%d_%H:%M:%S)" >> $GITHUB_ENV

      - name: Download web artifact
        uses: actions/download-artifact@v4
        with:
          name: easytier-web-dashboard
          path: easytier-web/frontend/dist/

      - name: Prepare build environment
        uses: ./.github/actions/prepare-build
        with:
          target: ${{ matrix.TARGET }}
          gui: true
          pnpm: true
          token: ${{ secrets.GITHUB_TOKEN }}

      - uses: Swatinem/rust-cache@v2
        with:
          # The prefix cache key, this can be changed to start a new cache manually.
          # default: "v0-rust"
          prefix-key: ""
          shared-key: "core-registry"
          cache-targets: "false"

      - uses: mlugg/setup-zig@v2
        if: ${{ contains(matrix.OS, 'ubuntu') }}
        with:
          version: 0.16.0
          use-cache: true

      - uses: taiki-e/install-action@v2
        if: ${{ contains(matrix.OS, 'ubuntu') }}
        with:
          tool: cargo-zigbuild

      - name: Build
        if: ${{ !contains(matrix.TARGET, 'mips') }}
        run: |          
          if [[ "$TARGET" == *windows* ]]; then
            SUFFIX=.exe
          else
            SUFFIX=""
          fi

          if [[ "$TARGET" =~ (x86_64-unknown-linux-musl|aarch64-unknown-linux-musl|windows|darwin) ]]; then
            BUILD=build
          else
            BUILD=zigbuild
          fi

          if [[ "$TARGET" =~ ^(riscv64|loongarch64|aarch64).*$ || "$TARGET" =~ (freebsd|windows) ]]; then
            FEATURES="mimalloc"
          else
            FEATURES="jemalloc"
          fi
          
          cargo $BUILD --release --target $TARGET --package=easytier-web --features=embed
          mv ./target/$TARGET/release/easytier-web"$SUFFIX" ./target/$TARGET/release/easytier-web-embed"$SUFFIX"
          
          cargo $BUILD --release --target $TARGET --features=$FEATURES

      - name: Build (MIPS)
        if: ${{ contains(matrix.TARGET, 'mips') }}
        env:
          RUSTC_BOOTSTRAP: 1
        run: |
          cargo build -r --target $TARGET -Z build-std=std,panic_abort --package=easytier --features=jemalloc

      - name: Compress
        run: |
          mkdir -p ./artifacts/objects/

          # windows is the only OS using a different convention for executable file name
          if [[ $OS =~ ^windows.*$ ]]; then
              SUFFIX=.exe
              case $TARGET in
                  x86_64*)  ARCH_DIR=x86_64 ;;
                  i686*)    ARCH_DIR=i686 ;;
                  aarch64*) ARCH_DIR=arm64 ;;
              esac
              if [[ -n "$ARCH_DIR" ]]; then
                  find "easytier/third_party/${ARCH_DIR}" -maxdepth 1 -type f \( -name "*.dll" -o -name "*.sys" \) -exec cp {} ./artifacts/objects/ \;
              fi
          fi

          if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then
            TAG=$GITHUB_REF_NAME
          else
            TAG=$GITHUB_SHA
          fi
          
          if [[ $OS =~ ^ubuntu.*$ && ! $TARGET =~ (loongarch|freebsd) ]]; then
            HOST_ARCH=$(uname -m)
            case $HOST_ARCH in
              x86_64)  UPX_ARCH="amd64" ;;
              aarch64) UPX_ARCH="arm64" ;;
              *)       UPX_ARCH="amd64" ;;
            esac

            UPX_VERSION=4.2.4
            UPX_PKG="upx-${UPX_VERSION}-${UPX_ARCH}_linux"
            curl -L "https://github.com/upx/upx/releases/download/v${UPX_VERSION}/${UPX_PKG}.tar.xz" -s | tar xJvf -
            cp "${UPX_PKG}/upx" .
            UPX_BIN=./upx
          fi

          for BIN in ./target/$TARGET/release/easytier-{core,cli,web,web-embed}"$SUFFIX"; do
            if [[ -f "$BIN" ]]; then
              if [[ -n "$UPX_BIN" ]]; then
                $UPX_BIN --lzma --best "$BIN" || true
              fi

              mv "$BIN" ./artifacts/objects/
            fi
          done

          mv ./artifacts/objects/* ./artifacts/
          rm -rf ./artifacts/objects/

      - name: Archive artifact
        uses: actions/upload-artifact@v5
        with:
          name: easytier-${{ matrix.ARTIFACT_NAME }}
          path: |
            ./artifacts/*

  build_magisk:
    runs-on: ubuntu-latest
    needs: [ pre_job, build_web, build ]
    if: needs.pre_job.result == 'success' && needs.pre_job.outputs.should_skip != 'true' && !cancelled()
    steps:
      - name: Checkout Code
        uses: actions/checkout@v5  # 必须先检出代码才能获取模块配置

      # 下载二进制文件到独立目录
      - name: Download Linux aarch64 binaries
        uses: actions/download-artifact@v4
        with:
          name: easytier-linux-aarch64
          path: ./downloaded-binaries/  # 独立目录避免冲突

      # 将二进制文件复制到 Magisk 模块目录
      - name: Prepare binaries
        run: |
          mkdir -p ./easytier-contrib/easytier-magisk/
          cp ./downloaded-binaries/easytier-core ./easytier-contrib/easytier-magisk/
          cp ./downloaded-binaries/easytier-cli ./easytier-contrib/easytier-magisk/
          cp ./downloaded-binaries/easytier-web ./easytier-contrib/easytier-magisk/

      # 上传生成的模块
      - name: Upload Magisk Module
        uses: actions/upload-artifact@v5
        with:
          name: Easytier-Magisk
          path: |
            ./easytier-contrib/easytier-magisk
            !./easytier-contrib/easytier-magisk/build.sh
            !./easytier-contrib/easytier-magisk/magisk_update.json
          if-no-files-found: error

  core-result:
    runs-on: ubuntu-latest
    needs: [ pre_job, build_web, build, build_magisk ]
    if: needs.pre_job.result == 'success' && needs.pre_job.outputs.should_skip != 'true' && !cancelled()
    steps:
      - name: Mark result as failed
        if: contains(needs.*.result, 'failure')
        run: exit 1
