name: Publish Mini Program Dev Version

on:
  workflow_dispatch:
  push:
    branches:
      - main
    paths:
      - "mini/**"
      - ".gitea/workflows/miniapp-preview.yml"

jobs:
  publish:
    # 标准标签：hk / bt-runner 均带 ubuntu-latest，任一空闲 runner 接单
    runs-on: ubuntu-latest
    steps:
      # 精简 runner（Alpine）无 node，无法跑 action-checkout；直接用 git 浅克隆
      - name: Checkout
        env:
          REPO_URL: ${{ gitea.server_url }}/${{ gitea.repository }}
          REF_NAME: ${{ gitea.ref_name }}
        run: |
          git init -q .
          git remote add origin "$REPO_URL"
          git fetch -q --depth 20 origin "$REF_NAME"
          git checkout -q FETCH_HEAD
          git log --oneline -3

      - name: Prepare Node toolchain
        run: |
          # 优先复用 runner 宿主机缓存的工具链（工作目录持久化）
          TOOLDIR="$RUNNER_WORKSPACE/.lunar-toolchain"
          mkdir -p "$TOOLDIR"
          if [ -x "$TOOLDIR/node/bin/node" ] && "$TOOLDIR/node/bin/node" --version >/dev/null 2>&1; then
            echo "复用缓存 Node 工具链"
          else
            # 清理旧缓存：Alpine busybox mv 遇已存在目录会合并而非替换，
            # 残留的旧版 node 会污染环境导致 symbol not found
            rm -rf "$TOOLDIR"
            mkdir -p "$TOOLDIR"
            # 探测 libc：Alpine(musl) 用 unofficial musl 构建，其余用官方 glibc 构建（tar.gz，busybox tar 不支持 xz）
            # 关键教训：unofficial-builds 在亚洲带宽极低，wget --timeout 只限单次读取，
            # 慢速断流仍返回成功，产生截断的 gzip（解压出损坏二进制报 symbol not found）。
            # 必须用 sha256 硬校验，不匹配则换源重试。
            if ldd --version 2>&1 | grep -qi musl || [ ! -e /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 ]; then
              NODE_DIST="node-v22.12.0-linux-x64-musl"
              NODE_SHA="8d96de0293fc3978d09e2a3fee68c66bca4efcca90f52105e6f7463636e97308"
              # 优先 npmmirror（国内/亚洲快），unofficial-builds 作为后备
              URLS="https://registry.npmmirror.com/-/binary/node-unofficial-builds/v22.12.0/$NODE_DIST.tar.gz
                    https://unofficial-builds.nodejs.org/download/release/v22.12.0/$NODE_DIST.tar.gz"
            else
              NODE_DIST="node-v22.12.0-linux-x64"
              NODE_SHA="e05a4d65232ae2b27b3d77da2e368522fb46b923335b8e0d5f77624c32484044"
              URLS="https://nodejs.org/dist/v22.12.0/$NODE_DIST.tar.gz
                    https://mirrors.aliyun.com/nodejs-release/v22.12.0/$NODE_DIST.tar.gz"
            fi
            DL_OK=""
            for url in $URLS; do
              echo "尝试下载: $url"
              if wget -t 2 -T 120 -q -O "$TOOLDIR/node.tgz" "$url"; then
                # sha256 硬校验（busybox 有 sha256sum）：拦截截断/错误页等假文件
                if [ "$(sha256sum "$TOOLDIR/node.tgz" | cut -d' ' -f1)" = "$NODE_SHA" ]; then
                  echo "下载成功且校验通过"
                  DL_OK=1
                  break
                fi
                echo "sha256 校验失败，文件可能截断，尝试下一源"
              else
                echo "wget 下载失败，尝试下一源"
              fi
              rm -f "$TOOLDIR/node.tgz"
            done
            test -n "$DL_OK" || { echo "::error::所有下载源均失败或校验不通过"; exit 1; }
            tar -C "$TOOLDIR" -xzf "$TOOLDIR/node.tgz"
            mv "$TOOLDIR/$NODE_DIST" "$TOOLDIR/node"
            rm -f "$TOOLDIR/node.tgz"
            # musl 版 node 动态链接 libstdc++/libgcc_s，Alpine 精简容器缺失时补装
            if [ "$NODE_DIST" = "node-v22.12.0-linux-x64-musl" ] && [ ! -e /usr/lib/libstdc++.so.6 ] && [ ! -e /lib/libstdc++.so.6 ]; then
              if command -v apk >/dev/null 2>&1; then
                apk add --no-cache libstdc++ libgcc || echo "::warning::libstdc++/libgcc 安装失败，node 可能无法启动"
              fi
            fi
          fi
          # 验证二进制可执行，失败则清缓存并报错（避免坏缓存被后续 run 复用）
          if ! "$TOOLDIR/node/bin/node" --version; then
            echo "::error::Node 二进制无法执行，请检查下载源与 libc 匹配"
            rm -rf "$TOOLDIR"
            exit 1
          fi
          echo "PATH=$TOOLDIR/node/bin:$PATH" >> "$GITEA_ENV"

      - name: Verify Node runtime
        run: node --version

      - name: Install dependencies
        working-directory: mini
        run: npm ci

      - name: Run tests
        working-directory: mini
        run: npm test

      - name: Configure production API and app version
        working-directory: mini
        env:
          MINIAPP_BUILD_NUMBER: ${{ gitea.run_number }}
          GITEA_SHA: ${{ gitea.sha }}
        run: |
          test -n "$MINIAPP_BUILD_NUMBER"
          VERSION="$(node -e 'const {resolveVersion}=require("./ci/resolve-version.cjs"); process.stdout.write(resolveVersion(process.cwd()))')"
          test -n "$VERSION"
          mkdir -p ci-artifacts
          printf '%s\n' "$VERSION" > ci-artifacts/resolved-version.txt
          echo "Resolved Mini Program version for build+upload: $VERSION"

          # 注入版本号到 utils/version.js
          BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
          COMMIT_SHORT="${GITEA_SHA:0:7}"
          cat > utils/version.js <<EOF
          /**
           * 版本号配置文件
           * 此文件由 CI 构建时自动生成，请勿手动修改
           */

          module.exports = {
            version: "$VERSION",
            buildNumber: "$MINIAPP_BUILD_NUMBER",
            buildTime: "$BUILD_TIME",
            commitSha: "$COMMIT_SHORT",
          };
          EOF
          echo "Version info injected into utils/version.js:"
          cat utils/version.js

      - name: Materialize the upload key
        env:
          WECHAT_CI_PRIVATE_KEY: ${{ secrets.WECHAT_CI_PRIVATE_KEY }}
        run: |
          test -n "$WECHAT_CI_PRIVATE_KEY"
          umask 077
          printf '%s' "$WECHAT_CI_PRIVATE_KEY" > "$RUNNER_TEMP/wechat-ci.key"

      - name: Upload WeChat development version
        working-directory: mini
        env:
          MINIAPP_APPID: ${{ vars.MINIAPP_APPID }}
          MINIAPP_ROBOT: ${{ vars.MINIAPP_ROBOT }}
          # Prefer the version resolved before build so UI label matches WeChat upload.
          MINIAPP_BUILD_NUMBER: ${{ gitea.run_number }}
          WECHAT_CI_PRIVATE_KEY_PATH: ${{ runner.temp }}/wechat-ci.key
          MINIAPP_UPLOAD_RESULT: ${{ gitea.workspace }}/mini/ci-artifacts/upload-result.json
          MINIAPP_VERSION_LABEL: ${{ gitea.sha }}
          MINIAPP_UPLOAD_DESC_ITEMS: "3"
          MINIAPP_UPLOAD_DESC_MAX: "100"
        run: |
          test -n "$MINIAPP_APPID"
          test -n "$MINIAPP_BUILD_NUMBER"
          test -s ci-artifacts/resolved-version.txt
          RESOLVED_VERSION="$(tr -d '\n' < ci-artifacts/resolved-version.txt)"
          # Force upload to the same version that was baked into the JS bundle.
          export MINIAPP_VERSION_OVERRIDE="$RESOLVED_VERSION"
          mkdir -p ci-artifacts
          node -e 'const c=require("./project.config.json"); if(c.appid!==process.env.MINIAPP_APPID){console.error("MINIAPP_APPID mismatch with project.config.json", process.env.MINIAPP_APPID, c.appid); process.exit(1)}'
          node -e 'const {resolveVersion}=require("./ci/resolve-version.cjs"); console.log("Resolved Mini Program version:", resolveVersion(process.cwd()))'
          node -e 'const {resolveUploadDesc}=require("./ci/resolve-upload-desc.cjs"); console.log("Resolved upload remark:", resolveUploadDesc({repoRoot:require("path").resolve(".."), versionLabel:process.env.MINIAPP_VERSION_LABEL}))'
          # Normalize PEM newlines that may be flattened when stored in Secrets.
          # 用 node 处理（部分 runner 无 python3）
          node - <<'JS'
          const fs = require("fs");
          const p = process.env.WECHAT_CI_PRIVATE_KEY_PATH;
          let t = fs.readFileSync(p, "utf8");
          if (t.includes("\\n") && t.includes("BEGIN")) t = t.split("\\n").join("\n");
          t = t.split("\r\n").join("\n").trim() + "\n";
          fs.writeFileSync(p, t);
          if (!/PRIVATE KEY/.test(t)) { console.error("WECHAT_CI_PRIVATE_KEY is not a PEM private key"); process.exit(1); }
          console.log("private_key_lines=", t.split("\n").length);
          JS
          node ci/upload-ci.cjs
          test -s ci-artifacts/upload-result.json
          cat ci-artifacts/upload-result.json

      - name: Send success notification
        if: success()
        env:
          TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
          DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
        run: |
          VERSION="$(tr -d '\n' < mini/ci-artifacts/resolved-version.txt)"
          bash .gitea/scripts/notify.sh success "小程序开发版上传成功" "版本: ${VERSION}" || echo "通知发送失败，不影响主流程"

      - name: Send failure notification
        if: failure()
        env:
          TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
          DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
        run: |
          bash .gitea/scripts/notify.sh failure \
            "小程序开发版上传失败" \
            "请检查 Actions 日志了解失败原因" || echo "通知发送失败"

      - name: Remove temporary credentials
        if: always()
        run: rm -f "$RUNNER_TEMP/wechat-ci.key"
