ci(windows): GitHub 云构建 Windows 安装器暂存 NAS + 发版可选来源(home/github)
家里 Windows runner 关机时的备用构建源。 - windows.yml: 构建 Inno 安装器(复用 compile-windows.sh)→ 暂存 NAS gitea winstage-v<版本> - deploy-client.yml: build-windows 加 home/github 开关(参数 windows_source 优先,变量 WINDOWS_SOURCE 兜底),新增 build-windows-ci 从 NAS 按版本拉取 - stage/fetch 脚本: 版本强匹配,拉不到硬失败 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# fetch-windows-staged.sh <tag> — 发版时从 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 <<MSG
|
||||
ERROR: NAS 上找不到暂存 Release ${STAGE_TAG}。
|
||||
WINDOWS_SOURCE=github 需要先在 GitHub 构建**版本 ${VER}** 的 Windows 包:
|
||||
gh workflow run windows.yml -f ver=${VER} (仓库 bj-wangjia/jiu)
|
||||
构建完成会把安装器暂存到 NAS,再重跑本次发版即可。
|
||||
MSG
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 找到同名资产 id 并下载(版本已由 STAGE_TAG 精确锁定)。JSON 经 stdin 传入避免转义问题。
|
||||
aid=$(printf '%s' "$info" | python3 -c "import sys,json; print(next((a['id'] for a in json.load(sys.stdin).get('assets',[]) if a['name']=='${NAME}'), ''))")
|
||||
rid=$(printf '%s' "$info" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
||||
|
||||
if [ -z "$aid" ]; then
|
||||
echo "ERROR: ${STAGE_TAG} 存在但缺少 ${NAME} 资产(GitHub 构建可能失败)。" >&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} 匹配)"
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# stage-windows-forgejo.sh <tag> — 把 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)。
|
||||
# <tag> 接受 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})"
|
||||
Reference in New Issue
Block a user