name: Build Linux App Release

on:
  push:
    tags:
      - "v[0-9]+.[0-9]+.[0-9]+"
  workflow_dispatch:

permissions:
  contents: write

concurrency:
  group: cicy-desktop-release-linux
  cancel-in-progress: true

jobs:
  build-linux-app:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      # cicy-code is NOT built-from-source here anymore (2026-06): bundled
      # sidecar = the per-platform optionalDependency, seeded by localbin at runtime.
      # The .cicy-code-ref source-build → vendor/cicy-code path was stale + never
      # packaged, so it's removed. Sync just pins optionalDeps to latest.
      - name: Sync runtime deps to latest (cicy-code + cicy-mihomo; drop msys2)
        run: node scripts/sync-runtime-deps.cjs

      - name: Install project dependencies
        run: npm install --no-audit

      - name: Install Electron build dependencies
        run: npm install --save-dev --no-audit electron@41.0.2 electron-builder@26.7.0

      # (2026-06 关键修复): runner 是 x64,npm 只装 linux-x64 的 cicy-code/mihomo →
      # linux-arm64 用户的包里塞的是 x64 二进制。强制把两个 linux 架构都装进 node_modules,
      # electron-builder 两架构都打包,localbin 按用户机器选。必须在所有 npm install 之后。
      - name: Bundle BOTH linux arches of cicy-code/mihomo (runner is single-arch)
        shell: bash
        run: |
          set -e
          # npm pack(不看 os/cpu/lockfile)+ 解压,绕开 `npm install --force` 在已有 lockfile 上的 no-op。
          for pkg in cicy-code-linux-x64 cicy-code-linux-arm64 cicy-mihomo-linux-x64 cicy-mihomo-linux-arm64; do
            ver=$(node -e "const o=require('./package.json').optionalDependencies||{}; process.stdout.write(o['$pkg']||'')")
            [ -z "$ver" ] && { echo "skip $pkg"; continue; }
            bin=cicy-code; case "$pkg" in cicy-mihomo*) bin=mihomo;; esac
            if [ -f "node_modules/$pkg/$bin" ]; then echo "$pkg present"; continue; fi
            echo "npm pack $pkg@$ver → node_modules/$pkg"
            tmp=$(mktemp -d); ( cd "$tmp" && npm pack "$pkg@$ver" --silent ); tar -xzf "$tmp"/*.tgz -C "$tmp"
            rm -rf "node_modules/$pkg"; mkdir -p "node_modules/$pkg"; cp -R "$tmp/package/." "node_modules/$pkg/"; rm -rf "$tmp"
          done
          ls node_modules | grep -E 'cicy-(code|mihomo)-linux' || true
          for a in x64 arm64; do test -f "node_modules/cicy-code-linux-$a/cicy-code" || { echo "::error::cicy-code-linux-$a missing"; exit 1; }; done

      # NOTE: the homepage SPA is rebuilt automatically by the prebuild:linux npm
      # hook (scripts/build-homepage.cjs) right before `npm run build:linux` below,
      # so the AppImage can never ship a stale homepage. No explicit step needed.

      - name: Build Linux AppImage
        shell: bash
        env:
          GH_TOKEN: ${{ github.token }}
          PUBLISH_OWNER: ${{ github.repository_owner }}
          PUBLISH_REPO: ${{ github.event.repository.name }}
          CICY_OBFUSCATE: "1"   # 发版混淆 JS(prebuild 主进程 + Vite 渲染层),仅 release CI
        run: |
          set -euo pipefail
          if [ "$GITHUB_REF_TYPE" = "tag" ]; then
            for attempt in $(seq 1 60); do
              gh release view "$GITHUB_REF_NAME" --repo "${{ github.repository }}" >/dev/null 2>&1 && break
              [ "$attempt" -eq 60 ] && { echo "::error::release was not created by the Windows workflow within 5 minutes"; exit 1; }
              sleep 5
            done
          fi
          npm run build:linux -- --x64 --config.linux.target=AppImage --config.publish.owner="$PUBLISH_OWNER" --config.publish.repo="$PUBLISH_REPO" --publish always

      # AppImage + 版本指针传到 R2 —— app-updater 现已全走 R2(不碰 GitHub),linux 也要在
      # R2 上有 versioned AppImage(CiCy-Desktop-<ver>.AppImage)和自己的指针 linux-latest-version.txt。
      # 和 mac/win 同一 bucket/凭证。electron-builder 默认产物名含空格,重命名成代码约定的连字符名。
      - name: Upload AppImage to R2 (versioned + latest pointer)
        if: startsWith(github.ref, 'refs/tags/v')
        shell: bash
        env:
          R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
          R2_API_TOKEN:  ${{ secrets.R2_API_TOKEN }}
        run: |
          set -e
          if [ -z "${R2_ACCOUNT_ID:-}" ] || [ -z "${R2_API_TOKEN:-}" ]; then echo "::warning::R2 creds missing — skip"; exit 0; fi
          VER="${GITHUB_REF_NAME#v}"
          BASE="releases"
          HOST="https://r2.deepfetch.de5.net/releases"
          APP=$(ls dist/*.AppImage 2>/dev/null | head -1)
          [ -n "$APP" ] && [ -f "$APP" ] || { echo "::error::no AppImage built"; exit 1; }
          DEST="CiCy-Desktop-$VER.AppImage"
          node scripts/r2.mjs put "$BASE/$DEST" "$APP"
          node scripts/r2.mjs put "$BASE/CiCy-Desktop-latest.AppImage" "$APP" || true
          # 版本指针最后写(包传完再更新),避免版本号涨了但包还没就位。
          printf '%s' "$VER" > linux-latest-version.txt
          node scripts/r2.mjs put "$BASE/linux-latest-version.txt" linux-latest-version.txt
          echo "R2 linux: $HOST/$DEST"
