diff --git a/.gitea/workflows/deploy-client.yml b/.gitea/workflows/deploy-client.yml index 79dcb47..d7a1610 100644 --- a/.gitea/workflows/deploy-client.yml +++ b/.gitea/workflows/deploy-client.yml @@ -4,6 +4,16 @@ on: push: tags: - 'client-v[0-9]*.[0-9]*.[0-9]*' + # 手动运行(在 tag 上 Run workflow)时可指定 Windows 构建来源,无需改仓库变量。 + # 走 github 模式:选该版本的 tag 作为 ref 运行 + windows_source=github。 + workflow_dispatch: + inputs: + windows_source: + description: 'Windows 构建来源:home=家里 runner(默认) / github=从 NAS 拉 GitHub 云构建' + required: false + default: home + type: choice + options: [home, github] concurrency: group: deploy-client @@ -111,7 +121,11 @@ jobs: APPSTORE_API_KEY_P8_BASE64: ${{ secrets.APPSTORE_API_KEY_P8_BASE64 }} run: sh scripts/ci/compile-ios.sh "${{ gitea.ref_name }}" + # 默认:家里那台 Windows runner 构建(回家后照常用)。 + # 仅当 gitea 仓库变量 WINDOWS_SOURCE=github 时跳过,改由 build-windows-ci 从 NAS 拉云构建。 build-windows: + # 来源判定:运行参数 windows_source 优先,其次仓库变量 WINDOWS_SOURCE,默认 home。 + if: ${{ (github.event.inputs.windows_source || vars.WINDOWS_SOURCE || 'home') != 'github' }} runs-on: windows env: GOPROXY: https://goproxy.cn,direct @@ -135,8 +149,39 @@ jobs: name: windows path: dist/ + # 特殊:家里 Windows 关机/连不上时,从 NAS 暂存拉 GitHub 云构建的安装器(版本强匹配)。 + # 启用:gitea 仓库设变量 WINDOWS_SOURCE=github,并先在 GitHub 跑 windows.yml 构建该版本。 + # 产出同名 windows artifact,下游 release-deploy-client 无需改动。 + build-windows-ci: + if: ${{ (github.event.inputs.windows_source || vars.WINDOWS_SOURCE || 'home') == 'github' }} + runs-on: mac + steps: + - uses: actions/checkout@v4 + + - name: Fetch staged Windows installer from NAS + env: + FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} + FORGEJO_URL: ${{ secrets.FORGEJO_URL }} + GITEA_REPOSITORY: ${{ gitea.repository }} + run: sh scripts/ci/fetch-windows-staged.sh "${{ gitea.ref_name }}" + + - name: Upload windows artifacts + uses: actions/upload-artifact@v3 + with: + name: windows + path: dist/ + release-deploy-client: - needs: [build-client-web, build-macos, build-android, build-ios, build-windows] + needs: [build-client-web, build-macos, build-android, build-ios, build-windows, build-windows-ci] + # 家里/云 两条 Windows 腿只会跑一条(另一条 skipped)。用 always() + 显式成功判定, + # 只要其中一条 windows 成功、且其余端全绿就继续发版。 + if: | + always() && + needs.build-client-web.result == 'success' && + needs.build-macos.result == 'success' && + needs.build-android.result == 'success' && + needs.build-ios.result == 'success' && + (needs.build-windows.result == 'success' || needs.build-windows-ci.result == 'success') runs-on: mac env: GOPROXY: https://goproxy.cn,direct diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 6367096..0d12029 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,19 +1,32 @@ -name: Build Windows (jiu client) +name: Build & Stage Windows (jiu client) +# GitHub 云构建 Windows 桌面安装器(Inno),并暂存到 NAS gitea 的 winstage-v<版本>。 +# 用途:家里那台 Windows runner 关机/连不上时的备用构建源。发版时把 gitea 仓库变量 +# WINDOWS_SOURCE 设为 github,deploy-client 就会按同版本从 NAS 拉这里暂存的安装器。 +# 版本匹配:workflow_dispatch 填 ver(发版要 tag 的版本);push 触发则取 client/pubspec.yaml。 on: workflow_dispatch: + inputs: + ver: + description: '版本号 (如 1.2.3;留空取 client/pubspec.yaml)。暂存到 winstage-v<版本>,发版按同版本拉取。' + required: false + default: '' push: branches: [ main ] paths: - 'client/**' - '.github/workflows/windows.yml' + - 'scripts/ci/compile-windows.sh' + - 'deploy/windows/**' jobs: - build-windows: + build-stage-windows: runs-on: windows-latest - defaults: - run: - working-directory: client + env: + # 覆盖 _env.sh 的中国镜像默认值:GitHub runner 在美国,用国际源更快更稳。 + PUB_HOSTED_URL: https://pub.dev + FLUTTER_STORAGE_BASE_URL: https://storage.googleapis.com + GOPROXY: https://proxy.golang.org,direct steps: - uses: actions/checkout@v4 @@ -21,25 +34,40 @@ jobs: with: channel: stable - - run: flutter --version - - run: flutter config --enable-windows-desktop - - run: flutter pub get - - - run: flutter build windows --release --dart-define=BASE_URL=https://jiu.51yanmei.com --dart-define=PUBLIC_URL=https://jiu.51yanmei.com - - - name: Package Release + - name: Install Inno Setup (idempotent) shell: pwsh - run: | - $rel = Get-ChildItem -Path build/windows -Recurse -Directory -Filter Release | - Where-Object { $_.FullName -match 'runner' } | Select-Object -First 1 - if (-not $rel) { throw 'Release dir not found' } - Write-Host "Release dir: $($rel.FullName)" - Compress-Archive -Path "$($rel.FullName)/*" -DestinationPath jiu-windows-x64.zip -Force + run: powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ci/install-innosetup.ps1 - - uses: actions/upload-artifact@v4 + - name: Resolve version + id: ver + shell: bash + run: | + v="${{ inputs.ver }}" + if [ -z "$v" ]; then + v=$(grep -E '^version:' client/pubspec.yaml | head -1 | sed -E 's/^version:[[:space:]]*//; s/\+.*//') + fi + echo "ver=$v" >> "$GITHUB_OUTPUT" + echo "解析版本: $v" + + # 复用与家里 runner 完全相同的构建脚本 → 产出 dist/jiu-windows-x64-setup.exe, + # 保证云构建与家里构建出的安装器一致(BASE_URL/PUBLIC_URL=https://jiu.51yanmei.com)。 + - name: Build Windows installer (Inno) + shell: bash + run: bash scripts/ci/compile-windows.sh "client-v${{ steps.ver.outputs.ver }}" + + - name: Upload installer artifact (GitHub 备份,可直接下载) + uses: actions/upload-artifact@v4 with: - name: jiu-windows-x64 - path: client/jiu-windows-x64.zip + name: jiu-windows-x64-setup + path: dist/jiu-windows-x64-setup.exe if-no-files-found: error + + - name: Stage installer → NAS gitea (winstage-v<版本>) + shell: bash + env: + FORGEJO_URL: ${{ secrets.FORGEJO_URL }} + FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} + GITEA_REPOSITORY: ${{ vars.GITEA_REPOSITORY || 'wangjia/jiu' }} + run: bash scripts/ci/stage-windows-forgejo.sh "client-v${{ steps.ver.outputs.ver }}" diff --git a/scripts/ci/fetch-windows-staged.sh b/scripts/ci/fetch-windows-staged.sh new file mode 100755 index 0000000..7de155e --- /dev/null +++ b/scripts/ci/fetch-windows-staged.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# fetch-windows-staged.sh — 发版时从 NAS gitea 暂存 Release 拉 Windows 安装器。 +# +# 用途:deploy-client.yml 的 build-windows-ci job(仅当 WINDOWS_SOURCE=github 时启用) +# 调用此脚本,按发版 tag(client-vX.Y.Z) 的**同一版本号**去找 `winstage-vX.Y.Z`, +# 把 jiu-windows-x64-setup.exe 下到 dist/,供后续 upload-artifact → release/deploy 使用。 +# +# **版本强匹配**:找不到对应版本的暂存包 → 硬失败并提示先在 GitHub 构建该版本, +# 绝不静默发出旧版/错版 Windows 包。 +# +# 需要 env:FORGEJO_URL、FORGEJO_TOKEN、GITEA_REPOSITORY。 +set -euo pipefail + +. "$(dirname "$0")/lib-forgejo.sh" + +TAG="$1" +VER="$(ver_from_tag "$TAG")" +STAGE_TAG="winstage-v${VER}" +NAME="jiu-windows-x64-setup.exe" + +echo "==> fetch-windows-staged: 发版版本=${VER},找暂存 ${STAGE_TAG}" +base="${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}" +mkdir -p dist + +if ! info=$(curl -kfsS -H "Authorization: token ${FORGEJO_TOKEN}" "${base}/releases/tags/${STAGE_TAG}" 2>/dev/null); then + cat >&2 <&2 + exit 1 +fi + +echo "==> fetch-windows-staged: 下载 asset id=${aid} → dist/${NAME}" +curl -kfsS -H "Authorization: token ${FORGEJO_TOKEN}" \ + "${base}/releases/${rid}/assets/${aid}" -o "dist/${NAME}" +ls -lh "dist/${NAME}" +echo "==> fetch-windows-staged: done(版本 ${VER} 匹配)" diff --git a/scripts/ci/stage-windows-forgejo.sh b/scripts/ci/stage-windows-forgejo.sh new file mode 100755 index 0000000..9f50777 --- /dev/null +++ b/scripts/ci/stage-windows-forgejo.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# stage-windows-forgejo.sh — 把 GitHub 构建出的 Windows 安装器暂存到 NAS gitea。 +# +# 用途:GitHub Actions(云) 构建 dist/jiu-windows-x64-setup.exe 后调用此脚本, +# 经 https://git.51yanmei.com Forgejo API 上传到一个**暂存 Release** `winstage-v<版本>`。 +# 之后 gitea 发版流水线(deploy-client.yml 的 build-windows-ci job,当 +# WINDOWS_SOURCE=github)按同一版本号从这里拉回,实现「家里 Windows 关机时用云构建」。 +# +# 暂存 tag 用 `winstage-` 前缀(**不是** `winbuild-`)以免误触发 gitea 的 +# build-windows.yml(on: push tags winbuild*)。 +# +# 需要 env:FORGEJO_URL(=https://git.51yanmei.com)、FORGEJO_TOKEN、GITEA_REPOSITORY(=wangjia/jiu)。 +# 接受 client-vX.Y.Z / vX.Y.Z / X.Y.Z,统一归一到裸版本号。 +set -euo pipefail + +. "$(dirname "$0")/lib-forgejo.sh" + +TAG="$1" +VER="$(ver_from_tag "$TAG")" +STAGE_TAG="winstage-v${VER}" +ASSET="dist/jiu-windows-x64-setup.exe" + +echo "==> stage-windows: version=${VER} stage_tag=${STAGE_TAG}" +[ -f "$ASSET" ] || { echo "ERROR: ${ASSET} 不存在,先跑 compile-windows.sh" >&2; exit 1; } + +api() { curl -kfsS -H "Authorization: token ${FORGEJO_TOKEN}" "$@"; } +base="${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}" + +# --- upsert: 若 winstage-v<版本> 已存在则复用其 id 并清掉同名旧资产,否则新建 --- +name="$(basename "$ASSET")" +if info=$(api "${base}/releases/tags/${STAGE_TAG}" 2>/dev/null); then + RELEASE_ID=$(printf '%s' "$info" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])") + echo "==> stage-windows: 复用已有暂存 Release id=${RELEASE_ID},清理同名旧资产" + # 列出同名旧资产 id(JSON 经 stdin 传入,避免嵌字符串的转义问题),逐个删除以便重传。 + printf '%s' "$info" | python3 -c "import sys,json; [print(a['id']) for a in json.load(sys.stdin).get('assets',[]) if a['name']=='${name}']" | + while read -r aid; do + [ -n "$aid" ] && api -X DELETE "${base}/releases/${RELEASE_ID}/assets/${aid}" >/dev/null && echo " 删除旧资产 id=${aid}" + done +else + echo "==> stage-windows: 新建暂存 Release ${STAGE_TAG}" + body=$(python3 -c "import json; print(json.dumps('GitHub 云构建的 Windows 安装器暂存(供 deploy-client 在家里 Windows 不可用时拉取)'))") + create_release "$STAGE_TAG" "$body" # 设置 RELEASE_ID +fi + +upload_asset "$ASSET" +echo "==> stage-windows: done — ${STAGE_TAG} 已带 jiu-windows-x64-setup.exe(版本 ${VER})"