From 3fa4501c68199ee4a74bf2a694e40ad1f8d747e0 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Tue, 29 Sep 2026 06:18:40 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=20Jenkins=20=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E8=AF=81=E6=8D=AE=E5=85=B3=E9=97=AD=E6=B8=A0=E9=81=93=E5=8C=96?= =?UTF-8?q?=E7=AC=AC=205=20=E6=9D=A1=EF=BC=8C=E5=B9=B6=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=20dev-mac=20=E5=8D=A1=E5=9C=A8=E7=A6=BB=E7=BA=BF=20mac=20?= =?UTF-8?q?=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 渠道化里程碑:四渠道全量只读核对(dev-win 0.1.158、release-win 0.1.159、release-mac 0.1.139、dev-mac 0.1.142)与真实 Jenkins 日志/归档取证,条目 5「凭据不落日志、不进归档」勾选关闭 - macOS 里程碑:记录节点 genarrative-agc-macos-01 自 2026-09-24 19:54:51 +08:00 起 ChannelTermination 离线(JNLP 只能在 Mac 本机启动)、离线期间不产出半成品,以及日调度 Job 因此连续失败 - pitfalls:新增「AGC 发布类 Job 变红或卡住先看构建节点在线状态」的排查口径与只读检查命令 --- ...里程碑】AGC macOS渠道更新落地-2026-09-17.md | 18 ++++++++++++++++- ...里程碑】AGC更新发布管线渠道化-2026-09-17.md | 20 ++++++++++++++++--- docs/project-memory/shared-memory/pitfalls.md | 8 ++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/docs/project-memory/plans/【里程碑】AGC macOS渠道更新落地-2026-09-17.md b/docs/project-memory/plans/【里程碑】AGC macOS渠道更新落地-2026-09-17.md index be6feca6d..ce97b69c4 100644 --- a/docs/project-memory/plans/【里程碑】AGC macOS渠道更新落地-2026-09-17.md +++ b/docs/project-memory/plans/【里程碑】AGC macOS渠道更新落地-2026-09-17.md @@ -68,4 +68,20 @@ - 根因与修复(本轮已落地,见 decision-log 2026-09-28 条目): - 根因:`build-macos-ci.mjs` 之前只删「本轮要写的文件名」,构建目录里上一轮遗留的 `陶泥儿 Release.app.tar.gz` 不会被清掉;而 `generateUpdateManifest()` 是**扫描目录按优先级挑产物**,于是挑走了那个残留文件。 - 修复:构建前按后缀清空 `macos/` 下的 `*.app.tar.gz`、`*.app.tar.gz.sig`、`*.dmg`、`*.dmg.sha256`;构建后读产物 `Contents/Info.plist` 断言版本与渠道身份;生成清单后再断言清单选中的就是本轮更新包。守卫逻辑在 `apps/ai-game-creator-shell/scripts/macos-release-identity.mjs`,单测 `macos-release-identity.test.mjs` 用线上那份 0.1.139 release 身份包做回归(`node --test` 59 passed,含新增 6 条)。 -- 仍未完成:需要用修好的 mac 管线重新发布一次 `dev-mac`(把 `latest.json` 指到本轮的新对象),然后重跑 `npm run check:agc-update-channel-manifests` 才能把第 1 条勾上。发布需要 Jenkins 凭据与授权,本地无法执行。 +- 仍未完成:需要用修好的 mac 管线重新发布一次 `dev-mac`(把 `latest.json` 指到本轮的新对象),然后重跑 `npm run check:agc-update-channel-manifests` 才能把第 1 条勾上。发布需要 mac 构建节点在线(见下一节:节点自 2026-09-24 起离线)。 + +## 本轮核对(2026-09-29,为什么 dev-mac 一直没重发) + +有 Jenkins 只读凭据后直接查了构建侧,结论是**流水线没坏,是 mac 节点离线**,dev-mac 无法重发: + +- **节点状态**:`genarrative-agc-macos-01` `offline=true`,`offlineCause` 是 `hudson.slaves.OfflineCause$ChannelTermination`,时间戳 `1790250891406` → **2026-09-24 19:54:51 +08:00**(正好在最后一次成功的 mac 构建 #67 结束之后)。 +- **节点启动方式决定本地救不了**:该节点是 `JNLPLauncher`(inbound WebSocket,`remoteFS=/Users/suzmii/Library/Jenkins/agents/genarrative-agc-macos-local`),必须由那台 Mac 本机启动 agent;Jenkins 侧没有可远程拉起它的入口,其它节点(`win2022-agent`、`genarrative-release-deploy-01`)与 Built-In Node 都在线。 +- **排队与清队**:`Genarrative-Agc-MacOS-Build` #68 从 `2026-09-24T12:11:28Z` 起一直在等(日志逐行 `Still waiting to schedule task` / `'genarrative-agc-macos-01' is offline`),直到 `2026-09-28T22:14:47Z` 被清队中止(约 4 天 9 小时);#69(`22:14:48Z`)、#70(`22:15:12Z`)随后也被中止。三次都停在 `// node` 之前,Post Action 明确 echo「构建未走到归档阶段时不会有任何产物,也不会写 OSS」——**离线期间不会产生半成品清单或半上传**,这点是设计内行为,不是这次要修的问题。 +- **同一根因的连带影响**:日调度 `Genarrative-Scheduled-Release-Trigger` 从 #7(2026-09-25)到 #11(2026-09-29)连续 FAILURE。以 #11 日志为例:`Genarrative-Full-Build-And-Deploy` #406 SUCCESS、`Genarrative-Agc-Windows-Build` #174 SUCCESS(放出 release-win 0.1.159),mac 分支等满 `2 hr` 后 `Cancelling nested steps due to timeout` → `Genarrative-Agc-MacOS-Build 等待失败: Build of Genarrative-Agc-MacOS-Build was cancelled` → 整条调度红。也就是说**这条红是 mac 节点离线的下游症状**,排查时先看节点在线状态,别去翻构建脚本。 +- **修复代码本身已经在位**:上一轮记的残留包挑中问题,其守卫(构建前按后缀清空 `macos/*.app.tar.gz` 等、构建后读 `Contents/Info.plist` 断言版本与渠道身份)已在 `build-macos-ci.mjs` + `macos-release-identity.mjs` 落地;本轮用四渠道全量核对反证守卫有效——`check:agc-update-channel-manifests` 现在会精确报出 `dev-mac` 的 `bundle=0.1.139 manifest=0.1.142`、身份 release,以及「渠道隔离」下 `dev-mac = release-mac`(同一 sha256),不再需要人工比对。 +- **解除阻塞需要的动作**(都在 Mac 本机,仓库侧无法代做):① 让那台 Mac 上的 Jenkins agent 重新连上(节点恢复 online);② 触发一次 `Genarrative-Agc-MacOS-Build`(渠道 `dev`、`AGC_RELEASE_VERSION` 用统一总号、`SKIP_IF_SUPERSEDED=true`);③ 发布完成后在仓库侧重跑 `AGC_UPDATE_CHANNELS=dev-win,dev-mac,release-win,release-mac AGC_UPDATE_VERIFY_DOWNLOAD=1 npm run check:agc-update-channel-manifests`,四条全绿即可勾掉本文件第 1 条与渠道化里程碑的「渠道隔离」。 + +## 本轮核对(2026-09-29,MacOS-Build #67 归档与日志) + +- 最后一次成功构建 #67(2026-09-24,耗时 31 min)的归档:`.jenkins-source-commit`、`artifacts/build-manifest.json`、`artifacts/latest.json`、`artifacts/release-notes.txt`、`artifacts/陶泥儿 Release.app.tar.gz.sig`、`artifacts/陶泥儿开发版_0.1.142_aarch64.dmg`、`artifacts/陶泥儿开发版_0.1.142_aarch64.dmg.sha256`。**同一轮里 DMG 是「开发版」、updater 归档却是「陶泥儿 Release…」**,正是残留包被挑中的现场证据(updater 归档现在由身份断言把关)。 +- 日志侧同时确认:归档里没有私钥/证书文件,且 Jenkins 会把凭据派生值打成 `****`(`+ export 'GIT_SSH_COMMAND=ssh -i "****" …'`)——这条证据也被渠道化里程碑条目 5 引用。 diff --git a/docs/project-memory/plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md b/docs/project-memory/plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md index 4c5877189..30f56b516 100644 --- a/docs/project-memory/plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md +++ b/docs/project-memory/plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md @@ -37,7 +37,7 @@ - [x] 渠道与目标平台不匹配、缺少签名私钥或私钥密码错误时发布失败关闭,不产生半成品清单。 - [x] 发布后 OSS 上安装包、签名与渠道清单三者一致:清单内地址指向已存在的对象,签名与安装包匹配(2026-09-28 只读核对已发布的 `dev-win` / `dev-mac` 渠道,两个渠道的产物都下载后验签通过,见文末「本轮核对」)。 - [x] universal macOS 产物的两个平台键指向同一对象同一签名,不存在只挂单一架构键或指向不存在对象的情况。(清单构建器对 universal 目标仍按此契约工作;但 2026-09-21 决策已把 macOS 发行改成 arm64 单架构,线上 `dev-mac` 按该决策只登记 `darwin-aarch64`,这条的线上口径以 macOS 里程碑第 1 条为准。) -- [ ] Jenkins 归档与日志中不出现签名私钥内容,凭据只注入构建进程。(静态核对:两个 AGC 打包管线都用 `withCredentials` 把 `AgcUpdaterSigningKey` / `...Password` 绑到 `TAURI_SIGNING_PRIVATE_KEY` / `..._PASSWORD`,归档 glob 只含安装包/签名/清单/说明/commit,没有私钥文件。2026-09-29 把这条静态口径收进仓库门禁 `npm run check:production-ops`:缺凭据绑定、或出现 `echo $TAURI_SIGNING_PRIVATE_KEY`、`Write-Host $env:...`、`%TAURI_SIGNING_PRIVATE_KEY%`、`set -x` 之类会打日志的写法、或归档 glob 出现 `*.pem|*.key|*.pfx|*.p12` 都判失败;变异验证:往 mac 管线插一行 `echo $TAURI_SIGNING_PRIVATE_KEY` 后门禁立刻以「不得把签名凭据打印到构建日志」失败退出。真实 Jenkins 运行的日志脱敏仍需一次 CI 构建取证。) +- [x] Jenkins 归档与日志中不出现签名私钥内容,凭据只注入构建进程。(静态核对:两个 AGC 打包管线都用 `withCredentials` 把 `AgcUpdaterSigningKey` / `...Password` 绑到 `TAURI_SIGNING_PRIVATE_KEY` / `..._PASSWORD`,归档 glob 只含安装包/签名/清单/说明/commit,没有私钥文件。2026-09-29 把这条静态口径收进仓库门禁 `npm run check:production-ops`:缺凭据绑定、或出现 `echo $TAURI_SIGNING_PRIVATE_KEY`、`Write-Host $env:...`、`%TAURI_SIGNING_PRIVATE_KEY%`、`set -x` 之类会打日志的写法、或归档 glob 出现 `*.pem|*.key|*.pfx|*.p12` 都判失败;变异验证:往 mac 管线插一行 `echo $TAURI_SIGNING_PRIVATE_KEY` 后门禁立刻以「不得把签名凭据打印到构建日志」失败退出。运行时侧已于 2026-09-29 用真实 Jenkins 构建日志与归档取证,见文末「本轮 Jenkins 取证(2026-09-29)」。) - [x] 未显式指定渠道时按目标平台取默认渠道,且 `--no-bundle` smoke 路径仍不读远端版本、不改版本、不生成清单。 ## 证据要求 @@ -87,5 +87,19 @@ - 产物身份与版本属于本渠道:PE `FileVersion=0.1.157.0`/`0.1.157` 与清单 0.1.157 一致,PE `ProductName=陶泥儿开发版`。 - 统一总号 `0.1.157`(`updatedAt=2026-09-28T16:11:20Z`、`channel=dev-win`),渠道清单不高于总号。 - 结论:「地址 → 签名 → 产物身份 → 版本」四层在线上对同一次发布同时成立,`assertArtifactVersionMatches()` 在真实 CI 里既不误伤也不漏放;上一轮记的「这条守卫还没经过真实 Windows 构建」未验证项关闭。 -- 本轮跳过 1 项「不同渠道的更新包互不相同(渠道隔离)」,因为只指定了单个渠道;该条由 `AGC_UPDATE_CHANNELS=dev-win,dev-mac,release-win,release-mac` 全量核对覆盖,仍受 `dev-mac` 未重发(0.1.142 指向 release 身份包)阻塞。 -- 条目 5(Jenkins 归档与日志不含私钥)仍未勾选:静态口径已进仓门禁,真实 Jenkins 运行日志仍需要一次 CI 构建取证。 +- 本节第一次跑只指定了单个渠道,「不同渠道的更新包互不相同(渠道隔离)」被跳过;已由下面的四渠道全量核对补齐。 + +## 本轮核对(2026-09-29,四渠道全量 + 真实 Jenkins 取证) + +- **四渠道全量只读核对**:`AGC_UPDATE_CHANNELS=dev-win,dev-mac,release-win,release-mac AGC_UPDATE_VERIFY_DOWNLOAD=1 npm run check:agc-update-channel-manifests`(每渠道都下载产物、解包核身份、用产物内公钥验签): + - `dev-win` **0.1.158**(`commit=e1dacccec5`,165,955,772 字节):地址、`.sig`、旧协议指针 `sha256=bb3a572ea478…`、验签(`alg=ED`/`keyId=cb883447e3e87c4e`)、`pe=0.1.158.0/0.1.158`、PE `ProductName=陶泥儿开发版` 全部 PASS —— 这是 `assertArtifactVersionMatches()` 之后的**第二个**真实 Windows 渠道构建,连续两次都不误伤。 + - `release-win` **0.1.159**(`commit=e1dacccec5`,166,037,839 字节):清单、签名、验签、`pe=0.1.159.0/0.1.159`、PE `ProductName=陶泥儿 Release` 全部 PASS;并断言「没有改写 dev 渠道的旧协议迁移指针」(桥接仍指向 `dev-win/0.1.158`),验证了本管线的渠道分区隔离。 + - `release-mac` **0.1.139**:清单自洽、验签通过,包内 `0.1.139` + `world.genarrative.ai-game-creator.release` / `陶泥儿 Release` 与本渠道一致(PASS)。 + - `dev-mac` **0.1.142**:2 项 FAIL(包内 `0.1.139`、包内身份是 release),并因此让「渠道隔离」这条也 FAIL(`dev-mac = release-mac`,同一 sha256)。**核对脚本现在会自动拦住这个线上缺陷**,不再是人工发现;修复需要 mac 节点重新构建发布(见 macOS 里程碑:节点离线)。 + - 统一总号 `0.1.159`(`channel=release-unified`,取号时间 `2026-09-28T20:00:19Z`,与调度 Job 日志里的「本轮 AGC release 客户端总版本号: 0.1.159」一致);四个渠道清单都不高于总号。 +- **真实 Jenkins 日志/归档取证(条目 5 关闭)**:只读读取生成构建的 `consoleText` 与归档清单,全程没有打印任何凭据内容。 + - `Genarrative-Agc-Windows-Build` #174 / #173(各约 66–67 KB 日志、约 970 行):私有钥材料标志(`untrusted comment`、`BEGIN … PRIVATE KEY`、`minisign encrypted secret key`、`TAURI_SIGNING_PRIVATE_KEY=`、`…_PASSWORD=`、凭据 ID `AgcUpdaterSigningKey`)**全部 0 命中**;唯一命中 `minisign` 的一行是 cargo 的 `Compiling minisign-verify v0.2.5`,与凭据无关。 + - `Genarrative-Agc-MacOS-Build` #67(最后成功的一次 mac 构建,约 70 KB 日志):同样 0 命中私有钥材料;日志里出现 2 行 `+ export 'GIT_SSH_COMMAND=ssh -i "****" …'`,说明 Jenkins 会把凭据派生值在日志里替换成 `****` —— 这就是「凭据只注入构建进程、不进日志」的运行时证据。 + - 归档清单(`/api/json?tree=artifacts`):Windows #174 只有 `.jenkins-source-commit`、`latest.json`、`陶泥儿 Release_0.1.159_x64-setup.exe`、同名 `.sig`、`release-notes.txt`;mac #67 只有 `.jenkins-source-commit`、`build-manifest.json`、`latest.json`、`release-notes.txt`、`陶泥儿 Release.app.tar.gz.sig`、`陶泥儿开发版_0.1.142_aarch64.dmg`、同名 `.sha256`。两边都**没有任何私钥/证书文件**。 + - 结论:仓库门禁(静态)与真实流水线(运行时)两侧一致,条目 5 关闭。 +- 仍未勾选:只有「发布后触发一次客户端更新闭环」这类运行时验收,属客户端更新里程碑与 macOS 里程碑的范围。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index d266f74a9..da439c14e 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -6145,3 +6145,11 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/` - **现象/风险**:AGC 里 `MAX_PROJECT_EXPORT_PACKAGE_BYTES = 512 MiB` 管的是"本地导出/暂存的体积安全线",而平台发布上限是 **200 MiB**(`module-game-distribution` 的 `MAX_PACKAGE_BYTES`,2026-09-23 决策从 100 MiB 放宽而来)。两者不是一个概念,拿前者当发布前检查会让 200–512 MiB 的包一路读盘 + 暂存,最后在建版本时吃服务端 413,作者白等一轮。 - **现状(2026-09-29 已修)**:上限单一来源落到 `shared_contracts::game_distribution::GAME_DISTRIBUTION_MAX_PACKAGE_BYTES`;AGC 在读包阶段就做 `ensure_within_platform_package_limit()` 预检并给出「发行包 X MiB 超过平台上限 200 MiB;请精简资源后重新导出再发布」。服务端领域 crate 不依赖 `shared-contracts`,仍保留自己的常量,由 api-server 的 `publish_package_limit_matches_the_shared_contract` 锁成一致。 - **判据**:任何"客户端先检查、服务端再校验"的额度都要问一句"这两处是同一个数吗、谁保证不漂移";本地安全线(防呆)与平台业务额度(可对外承诺)要分开命名,别混用。 + +## 2026-09-29 AGC 发布类 Job 变红或卡住,先看构建节点在线状态(不是构建脚本) + +- **现象**:`Genarrative-Scheduled-Release-Trigger` 连续 FAILURE,日志尾部是 `Cancelling nested steps due to timeout` + `Genarrative-Agc-MacOS-Build 等待失败: Build of Genarrative-Agc-MacOS-Build was cancelled`(等满 2 hr);`Genarrative-Agc-MacOS-Build` 自己的日志是 ABORTED,且停在 `// node` 之前、只有 `Still waiting to schedule task` / `'genarrative-agc-macos-01' is offline`。两者是同一条根因的不同表现,别去翻构建脚本或产物验证逻辑。 +- **先查这个**:`GET https://jenkins.genarrative.world/jenkins/computer/api/json?tree=computer[displayName,offline,offlineCauseReason]`(注意实例挂在 **`/jenkins` 上下文路径**下,用根路径会 302/403)。`offlineCause` 为 `OfflineCause$ChannelTermination` 表示 agent 连接断开(机器关机/休眠/agent 进程退出/网络中断),此时 `GET /jenkins/computer/<节点名>/config.xml` 里的 `launcher` 决定能不能远程拉起。 +- **不能远程拉起的形态**:`genarrative-agc-macos-01` 是 `JNLPLauncher`(inbound WebSocket,`remoteFS=/Users/suzmii/Library/Jenkins/agents/genarrative-agc-macos-local`),只能在那台 Mac 本机把 agent 起回来;Jenkins 侧没有可用入口。所以「macOS 渠道一直没有新版本」这类问题的第一问是:那台 Mac 是否开机、agent 是否在跑。(该节点 2026-09-24 19:54:51 +08:00 起因 `ChannelTermination` 离线,`dev-mac` 的 `latest.json` 因此一直停在 0.1.142。) +- **离线期间不会产生半成品**:mac 构建在 `// node` 之前就被中止,Post Action 明确「未走到归档阶段时不会有任何产物,也不会写 OSS」;`Jenkinsfile.scheduled-release-trigger` 里给 mac 分支传 `SKIP_IF_SUPERSEDED=true`,节点回来后排队中的旧构建会自行让位。 +- **同时在查的东西**:`dev-mac/0.1.142` 清单指向的是 `0.1.139` 的 release 身份包(构建目录里上一轮的 `<产品名>.app.tar.gz` 残留被扫描式产物选择器挑走)。修复已在 `build-macos-ci.mjs` + `macos-release-identity.mjs` 落地;`npm run check:agc-update-channel-manifests` 现在会自动报出 `bundle=0.1.139 manifest=0.1.142`、身份 release 以及「渠道隔离」下的 `dev-mac = release-mac`(同一 sha256),不需要人工比对。