macOS universal 管线接入双架构 Node 运行时前置
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled

- Jenkinsfile.ai-game-creator-shell-macos-build 增加 AGC_NODE_RUNTIME_DARWIN_X64_HOME,并在 Toolchain 阶段编译前核对 x86_64 发行版存在且与构建 Node 同版本
- Package 阶段导出 AGC_NODE_RUNTIME_DARWIN_X64_PATH / _LICENSE_PATH,供 universal staging 取另一架构来源
- 开发运维文档记录节点前置:官方 darwin-x64 发行版安装与校验命令、同版本要求、按架构并列的资源布局、Rust 侧按运行架构选择、约 260 MB 解包体积代价,以及本机跨架构诊断构建的取值方式
- 实施计划与更新检查方案文档同步 universal 携带两套架构运行时的契约
This commit is contained in:
2026-09-21 11:58:22 +08:00
parent 6a0b75779b
commit 91f1554ac7
4 changed files with 47 additions and 2 deletions
@@ -102,7 +102,7 @@
- 对象布局:清单固定写成 `agc/<channel>-win|mac/latest.json`;安装包与签名写成同一分区的 `<version>/<file>``<file>.sig`
- macOS 正式交付使用 universal 主程序:两个平台键指向同一个 `.app.tar.gz` 与签名,一份产物同时服务 Apple Silicon 与 Intel。单架构目标(`aarch64-apple-darwin` / `x86_64-apple-darwin`)只用于本机诊断,不登记正式分区清单——单架构构建不可轮流覆盖同一个 `latest.json` 并宣称双架构均可更新。
- universal 主程序同时携带分目录的 arm64/x64 原生 Codex 组件:每个组件保持上游单架构布局与独立 SHA-256 清单,运行中的主程序切片只选择同架构目录,不得把两套原生包的元数据或辅助程序混装。
- universal 主程序同时携带分目录的 arm64/x64 原生 Codex 组件:每个组件保持上游单架构布局与独立 SHA-256 清单,运行中的主程序切片只选择同架构目录,不得把两套原生包的元数据或辅助程序混装。随包 Node 运行时同样按架构分目录(`game-runtime/node/darwin-arm64/``darwin-x64/`,各带 `agc-node-runtime.v1` 清单):两套都来自节点上同版本的官方发行版,构建时缺一份或版本不一致即在写出资源前失败,运行切片只采用与当前架构匹配的那一份,单架构构建保持扁平目录不变。
- 构建期要求:打开 `bundle.createUpdaterArtifacts` 以生成 `.sig`;构建环境提供签名私钥与密码(私钥内容不得入库);公钥写入客户端配置。公钥在首个带更新能力的版本发布后不可更换,更换等于放弃自动更新(只能手动重装)。
- 版本递增按渠道及系统分区独立进行:发布脚本读取该分区远端 `latest.json``version`,与本地版本取较高者递增 patch;不同分区的远端版本互不影响。
- 版本高水位:仅 dev 的 Windows 分区在迁移窗口内取「分区清单版本」与「旧协议迁移指针版本」较大值再递增,避免已发布旧客户端版本倒退。迁移窗口结束(旧指针 404)后只读分区清单;release、自定义渠道与所有 Mac 分区均不参与旧指针比较。
@@ -71,7 +71,7 @@
### 环境与工作流
- 客户端交付配套 Node/npm;发布包从本机已安装且与目标平台/架构一致的工具链制作受校验资源,保留许可并校验内容摘要。安装态不依赖系统 PATH 的 Node;开发态可使用已验证的宿主运行时。不得从项目或相对 PATH 加载伪造运行时。
- 客户端交付配套 Node/npm;发布包从本机已安装且与目标平台/架构一致的工具链制作受校验资源,保留许可并校验内容摘要。安装态不依赖系统 PATH 的 Node;开发态可使用已验证的宿主运行时。不得从项目或相对 PATH 加载伪造运行时。universal 包携带**两套**架构运行时并按架构并列存放(`game-runtime/node/<platform>-<arch>/`,各自一份清单),运行切片只采用与当前架构一致的那一份;两套必须来自同一 Node 版本,缺一份或版本不一致时构建在写出任何资源之前失败,不能用「少带一份」或退回系统 Node 充数。
- 新建 Web 游戏在生图和大量实现前执行客户端环境预检,检查 Node/npm 的实际版本、浏览器启动和 CDP 可用性。报告只包含安全状态、版本、耗时和错误码。缺失或异常必须尽早返回阻塞,不能指示模型改宿主环境、全盘搜索或自行下载一套运行时。编辑器工程不强制 Web 工具链。
- 预检不安装依赖、不修改项目 revision、不请求平台生成;构建仍执行项目自己的 npm 脚本。Codex 隔离 HOME 与平台凭据边界保持不变,客户端把已验证的运行时加入执行 PATH,不能把宿主凭据目录交给模型。
- 第一轮先明确本次必需玩法、素材和验收项。同批独立读取尽量合并,必需图片一次规划;已有且可用的资产复用。已有目标全部通过后给出交付结果,非阻塞的新点子列为后续工作,不在收尾时主动开启新的生产链。
@@ -736,6 +736,23 @@ Job 名为 `Genarrative-Agc-MacOS-Build`SCM 直接读取仓库内上述 Jenki
发布凭据全部走 Jenkins 全局凭据,并在 `withCredentials` 内注入当前进程:`AgcUpdaterSigningKey`(与 `AgcUpdaterSigningKeyPassword`)映射为 `TAURI_SIGNING_PRIVATE_KEY` / `TAURI_SIGNING_PRIVATE_KEY_PASSWORD``AliyunAccessKeyId` / `AliyunaccessKeySecret` 映射为 `AGC_OSS_ACCESS_KEY_ID` / `AGC_OSS_ACCESS_KEY_SECRET`;私钥与凭据不写入 workspace、日志或归档产物。上传顺序为更新包、签名、首装包,三者全部成功后才覆盖 `agc/<channel>-mac/latest.json` 指针;`AGC_RELEASE_DRY_RUN` 与 Windows 渠道对称:默认关闭即真发布,勾选后才退化为演练(只打印将上传的对象、不写任何 OSS 对象)。本 Job 是正式发布入口,调度器在发号后与 Windows 一起触发它,并额外传 `SKIP_IF_SUPERSEDED=true`——Mac 节点是日常办公机,离线期间排队的旧构建在节点回来后若已被源码分支推进,直接跳过而不发布过期版本。Mac 节点需要 `ossutil`(实测 1.7.19 原生 arm64 可用,装在 `~/.local/bin`,已在 Job 的 PATH 内),可用 `OSSUTIL_BIN` 指定命令名或绝对路径。首次发布建议显式指定 `AGC_RELEASE_VERSION`,避免按渠道高水位递增时出现版本链回退。
随包 Node 运行时也是节点前置,且 universal 需要**两套架构各一份**:客户端发布包内嵌 Node + npm 作为受校验资源(`agc-node-runtime.v1` 清单 + 逐文件 SHA-256),构建时只从**本机已安装、且与目标平台/架构一致**的工具链取材,缺文件、版本不符或目标不匹配立即失败,禁止回退系统 Node、也不允许从项目或相对 PATH 里借用运行时。宿主架构那一份直接取构建 Node(Mac 节点为 arm64,即 PATH 上的 `node`);另一架构必须由节点显式提供:预装官方 darwin-x64 发行版到 `~/Library/Jenkins/node/darwin-x64`(位置可用 `AGC_NODE_RUNTIME_DARWIN_X64_HOME` 覆盖),保留 `LICENSE``lib/node_modules/npm`Job 会把它们导出为 `AGC_NODE_RUNTIME_DARWIN_X64_PATH` / `AGC_NODE_RUNTIME_DARWIN_X64_LICENSE_PATH`。两套必须**同版本**(首次接入时均为 `v22.23.2`):缺一份、架构不符或版本不一致,staging 都会在写出任何运行时目录之前失败关闭,不会生成半套资源的更新包;版本核对同时是 Job 的编译前预检。预装与核对命令(官方 dist,校验 `SHASUMS256.txt`,再确认架构与版本):
```sh
version=22.23.2
curl -fsSLO "https://nodejs.org/dist/v${version}/node-v${version}-darwin-x64.tar.gz"
curl -fsSLO "https://nodejs.org/dist/v${version}/SHASUMS256.txt"
grep " node-v${version}-darwin-x64.tar.gz\$" SHASUMS256.txt | shasum -a 256 -c -
mkdir -p "$HOME/Library/Jenkins/node/darwin-x64"
tar -xzf "node-v${version}-darwin-x64.tar.gz" -C "$HOME/Library/Jenkins/node/darwin-x64" --strip-components=1
/usr/bin/lipo -archs "$HOME/Library/Jenkins/node/darwin-x64/bin/node" # 期望 x86_64
/usr/bin/arch -x86_64 "$HOME/Library/Jenkins/node/darwin-x64/bin/node" --version # 期望与构建 Node 相同
```
universal 包的随包 Node 按架构并列存放:`Contents/Resources/game-runtime/node/darwin-arm64/``darwin-x64/`,各自带独立 `manifest.json`;Rust 侧按当前运行架构选择对应目录(两个候选都要过清单的平台/架构校验,存在但损坏则失败关闭,绝不退回系统 Node),架构校验脚本对两份都做完整性核对与 `lipo -archs` 架构核对,只执行与本次 smoke 架构一致的那一份,另一份由另一次架构的 smoke 覆盖。单架构目标仍写扁平目录 `game-runtime/node/`,行为不变。体积代价:官方 Node + npm 解包后约 130 MB/份(实测 arm64 `node` 112.9 MB + npm 16.3 MBx64 `node` 115.4 MB + npm 15.6 MB),universal 两份合计约 260 MBDMG 压缩后增量预计在 50 MB 量级——这是双架构必须付的代价,不通过「Mac 跳过随包 Node」来省。
同一条规则适用于本机跨架构诊断构建:宿主架构以外的目标(例如 Apple Silicon 上打 `x86_64-apple-darwin`)必须用 `AGC_NODE_RUNTIME_<PLATFORM>_<ARCH>_PATH` 指定来源,缺失时报「缺少 <platform>/<arch> 的 Node 运行时」并给出该变量名,不再退化成含义模糊的「平台/架构不一致」。
产物边界:macOS 代码签名与公证暂缺,构建通过剥离 `APPLE_*` 凭据让 Tauri 跳过 Apple 签名,**不得使用 `--no-sign`**——该标志会连带跳过 updater 的 minisign 签名,产物缺少 `.sig` 会直接卡在验签门禁(首次实跑即命中该坑)。构建清单按 `codesign -dv` 实测记录 `appleSigned` / `appleSignatureKind`(如 `adhoc`),并固定记录 `notarized=false`。用户首次安装需要在 Gatekeeper 中手动放行;更新包校验本身只依赖 minisign 签名,因此未签名不阻断自动更新的校验环节,但「安装 → 重启接管新版本」的实机闭环仍未验证,不得以构建成功替代。
Job 描述与节点描述是 Jenkins 侧元数据,**不随仓库同步**:Jenkinsfile 只回写参数定义,描述必须手工维护,否则会停留在建 Job 时的口径(2026-09-20 就出现过描述还写着「只构建归档、不签名不上传」,而实际已经是含签名、分区清单、验签与 OSS 上传的发布管线)。当前口径:Job 描述说明「构建 universal → 双架构 smoke → DMG → `<channel>-mac` 分区清单 → 更新包验签 → 按 dry-run 决定上传,未做 Apple 签名与公证」;节点描述说明「`genarrative-agc-macos`、EXCLUSIVE 单 executor、仅手动触发、独立 workspace、sccache 在 HOME」。维护手法:该 Job 上 `POST job/<name>/config.xml` 会返回 500,节点侧同接口正常;因此改 Job 描述用脚本接口原地更新(保留构建历史),不要为了改描述删 Job 重建。
@@ -34,6 +34,10 @@ pipeline {
CARGO_INCREMENTAL = '0'
// 不把节点用户名写进仓库:PATH 在下面的 shell 步骤里按 $HOME 展开。
AGC_EXTRA_PATH = '/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin'
// universal 需要两份**同版本**的架构运行时:宿主(arm64)取自 PATH 里的构建 Node
// x86_64 由节点预装的官方发行版提供。只写相对 HOME 的目录,绝对路径在 shell 步骤里拼。
// 节点没有它时构建前即失败,绝不静默跳过或让更新包缺一份运行时。
AGC_NODE_RUNTIME_DARWIN_X64_HOME = 'Library/Jenkins/node/darwin-x64'
}
stages {
stage('Checkout') {
@@ -116,6 +120,24 @@ pipeline {
arch -x86_64 /usr/bin/uname -m
if command -v sccache >/dev/null 2>&1; then sccache --version; else echo '[agc-macos] 未找到 sccache;本次回退到 rustc 直接构建。'; fi
rustup target add aarch64-apple-darwin x86_64-apple-darwin
# 随包 Node 的两套架构运行时:宿主那一份就是 PATH 里的构建 Node,
# x86_64 那一份必须是节点上预装的官方发行版(含 LICENSE 与 lib/node_modules/npm)。
# 两份版本必须一致,否则 staging 会在写出任何资源前失败关闭;这里提前到编译前暴露。
x64_root="${AGC_NODE_RUNTIME_DARWIN_X64_HOME:-Library/Jenkins/node/darwin-x64}"
case "$x64_root" in /*) ;; *) x64_root="${HOME:?HOME 不能为空}/$x64_root" ;; esac
if [ ! -x "${x64_root}/bin/node" ]; then
echo "[agc-macos] 缺少 x86_64 Node 运行时:${x64_root}/bin/node"
echo '[agc-macos] 安装官方 darwin-x64 发行版到该目录(保留 LICENSE 与 lib/node_modules/npm)后重试;也可以覆盖 AGC_NODE_RUNTIME_DARWIN_X64_HOME'
exit 1
fi
host_node_version="$(node --version)"
x64_node_version="$(/usr/bin/arch -x86_64 "${x64_root}/bin/node" --version)"
if [ "$host_node_version" != "$x64_node_version" ]; then
echo "[agc-macos] 两套 Node 运行时版本不一致:arm64=${host_node_version} x86_64=${x64_node_version}"
echo '[agc-macos] universal 更新包只接受同版本的两套运行时,请把节点上的 x86_64 发行版升到与构建 Node 相同版本'
exit 1
fi
echo "[agc-macos] 随包 Node 运行时:arm64=${host_node_version} x86_64=${x64_node_version}(来源 ${x64_root}"
npm ci --no-audit --no-fund
node apps/ai-game-creator-shell/scripts/prepare-macos-codex.mjs
'''
@@ -143,6 +165,12 @@ pipeline {
sh '''
set -eu
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:${AGC_EXTRA_PATH}"
# universal staging 需要另一架构的 Node 来源;路径与 Toolchain 阶段同一口径,
# 缺失/版本不一致由 staging 在任何落盘前再次失败关闭。
x64_root="${AGC_NODE_RUNTIME_DARWIN_X64_HOME:-Library/Jenkins/node/darwin-x64}"
case "$x64_root" in /*) ;; *) x64_root="${HOME:?HOME 不能为空}/$x64_root" ;; esac
export AGC_NODE_RUNTIME_DARWIN_X64_PATH="${x64_root}/bin/node"
export AGC_NODE_RUNTIME_DARWIN_X64_LICENSE_PATH="${x64_root}/LICENSE"
echo "[agc-macos] 渠道=${AGC_UPDATE_CHANNEL} 分区=${AGC_UPDATE_CHANNEL}-mac 目标=universal-apple-darwin dry-run=${AGC_RELEASE_DRY_RUN}"
ossutil_bin="${OSSUTIL_BIN:-ossutil}"
if command -v "${ossutil_bin}" >/dev/null 2>&1; then