修复 macOS 渠道发布挑中残留产物,改为构建期核对包内版本与渠道身份
Project CI / AI game creator shell Rust crates (push) Successful in 1m45s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m4s
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Successful in 1m45s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m4s
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
- build-macos-ci:构建前按后缀清空 macos/ 下的 *.app.tar.gz / *.sig / *.dmg / *.dmg.sha256,构建后读 Info.plist 核对版本与渠道身份,生成清单后再断言选中本轮更新包 - 新增 macos-release-identity.mjs 与单测:回归用例直接用线上 dev-mac 里那份 0.1.139 release 身份包 - prepare-macos-codex.test:残留清理断言改成按后缀全覆盖,并校验清理在构建前、身份核对在验签前 - Jenkinsfile.ai-game-creator-shell-macos-build:mac Job 增加身份守卫用例 - check:agc-update-channel-manifests:下载后解出 mac 包内 Info.plist 核对版本与身份,并按 2026-09-21 决策只要求 darwin-aarch64 - 文档:两份里程碑按现行决策修正口径并记录线上缺陷,decision-log 记决策与边界,pitfalls 记录「按目录扫描挑产物会选中残留」
This commit is contained in:
@@ -9697,4 +9697,15 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 决策(错误 envelope 与成功 envelope 同一份 meta):`AppError::into_response` 之前固定按「无请求上下文」构造错误响应,错误 envelope 里没有 `meta.requestId` / `meta.operation`,而成功 envelope 有,客户端在报错时拿不到可用于排查的 requestId。现在 `attach_request_context` 用 `tokio::task_local!` 的 `CURRENT_REQUEST_CONTEXT` 把上下文作用域套住整个 handler,错误转换读同一份上下文;脱离请求任务(单测、后台任务)时退回无上下文形状。取证:`check:game-distribution-owner-isolation` 新增成功/失败 envelope 两条断言,修复前 `requestId=` 为空失败、修复后 23 项 PASS。
|
||||
- 决策(envelope 一致性按可消费取证):TS 侧没有 envelope 的类型镜像,只有 `packages/shared/src/http.ts` 与 `src/services/apiClient.ts` 的运行时守卫,因此这一条按「客户端能一致消费真实 envelope」取证(字段名 `ok` / `data` / `error.code` / `meta.apiVersion` / `meta.requestId`),不是类型级镜像;字段门禁比对的是名字而不是值类型。- 影响范围:`server-rs/crates/api-server/src/modules/game_distribution.rs`、`scripts/check-game-distribution-owner-isolation.mjs`、`scripts/check-game-distribution-upload-safety.mjs`、`scripts/check-game-distribution-upload-resume.mjs`、`package.json`、游戏分发里程碑取证。
|
||||
- 决策(写入必须显式下发对象级 ACL):`platform-oss` 的 `OssObjectAccess` 之前只用于日志,对象继承 bucket 默认 ACL,公共读 bucket 上「private」对象可被匿名直取。现在内部 PUT、分片追加与直传 policy / 表单三处都下发 `x-oss-object-acl`(`Private` → `private`,`Public` → `public-read`);`OssAppendInternalObjectRequest` 新增 `access`,`DirectUploadTicketFormFields` 新增 `x-oss-object-acl`。验证:`cargo test -p platform-oss` 76 passed;`E2E_REQUIRE_PRIVATE_BUCKET=1` 的媒体链路 E2E 里直传封面与发行包对象匿名直取都 403(65 项 PASS)。- 验证:本地真实栈三个脚本全部 PASS(越权隔离 23 项、上传安全 24 项、分片续传 prepare 8 项 + resume 9 项、媒体链路 44 项);分片续传中途杀掉 api-server 进程(PID `53844` → 重启 `7728`)后仍从 `8,388,608` 偏移续传成功;`cargo test -p api-server -- package_` 6 passed 与 `game_distribution` 27 passed、`npm run lint`、`check:encoding`、`check:doc-index`、`git diff --check`。
|
||||
- 边界:证据来自本机 dev 栈与 dev bucket;生产域名、CDN 缓存窗口与真实客户端安装版的自动上传仍未验证。
|
||||
- 边界:证据来自本机 dev 栈与 dev bucket;生产域名、CDN 缓存窗口与真实客户端安装版的自动上传仍未验证。
|
||||
|
||||
## 2026-09-28 macOS 渠道发布必须核对「包内版本 + 渠道身份」,不能只验签
|
||||
|
||||
- 背景:用只读核对脚本 `scripts/check-agc-update-channel-manifests.mjs` 检查**已发布**的 OSS 渠道清单时发现,线上 `dev-mac/latest.json`(`version=0.1.142`、`commit=c07c10c0c`)指向的更新包解开后是 `CFBundleShortVersionString=0.1.139`、`CFBundleIdentifier=world.genarrative.ai-game-creator.release`、`CFBundleName=陶泥儿 Release`。签名验签、对象存在、`.sig` 与清单文本一致这些都对——错的是**版本与渠道身份**:dev 渠道的 arm64 客户端会被指向一个旧版的 release 身份包。
|
||||
- 根因:`build-macos-ci.mjs` 以前只清理「本轮要写的确切文件名」,mac 构建目录里上一轮/其它渠道身份留下的 `*.app.tar.gz` 不会被删;`generateUpdateManifest()` 是按目录扫描 + 优先级选产物,于是选中了残留文件。mac 构建机复用 workspace,这类残留会长期存在。
|
||||
- 决策(构建期失败关闭):mac 发布入口在构建前按后缀清空 `macos/` 下的 `*.app.tar.gz`、`*.app.tar.gz.sig`、`*.dmg`、`*.dmg.sha256`;构建后读 `.app/Contents/Info.plist`,断言 `CFBundleShortVersionString` 等于本轮发布版本、`CFBundleIdentifier`/`CFBundleName` 等于该渠道安装身份;生成清单后再断言清单选中的更新包就是本轮那一个。任一不符直接中止,不写 OSS。
|
||||
- 决策(只读核对也要看包内身份):`check:agc-update-channel-manifests` 在 `AGC_UPDATE_VERIFY_DOWNLOAD=1` 时下载 mac 更新包、解出 `Info.plist` 做同样断言;同时按 2026-09-21 决策断言 mac 渠道只登记 `darwin-aarch64`(不再要求 universal 双键)。
|
||||
- 决策(口径回归):macOS 现行契约是 arm64 单架构(2026-09-21 决策),里程碑里「两个 macOS 平台键指向 universal 产物」的旧文字按现行决策改写;不得据此重新切回 universal,除非按该决策给出的恢复路径补齐按架构的 Node 运行时。
|
||||
- 影响范围:`apps/ai-game-creator-shell/scripts/build-macos-ci.mjs`、新增 `apps/ai-game-creator-shell/scripts/macos-release-identity.mjs` 与其 `.test.mjs`、`apps/ai-game-creator-shell/scripts/prepare-macos-codex.test.mjs`、`jenkins/Jenkinsfile.ai-game-creator-shell-macos-build`、`scripts/check-agc-update-channel-manifests.mjs`、两份里程碑与本文件、pitfalls。
|
||||
- 验证:`node --test macos-release-identity.test.mjs prepare-macos-codex.test.mjs verify-updater-signature.test.mjs build-release.test.mjs cargo-features.test.mjs` → 59 passed(新增 6 条,回归用例直接喂线上那份 0.1.139 release 身份 plist,必须抛错);`npm run check:production-ops`、`check:encoding`、`check:doc-index`、prettier、eslint、`git diff --check` 通过;只读核对对线上 `dev-win` 全 PASS(含 158 MiB 产物下载验签与旧协议 sha256 一致),对线上 `dev-mac` 精确报出上面两条 FAIL。
|
||||
- 边界(未完成):修复只保证「以后再发不会再错」,线上 `dev-mac/latest.json` 仍指向那份坏包;需要一次带 Jenkins 凭据与授权的 mac 重新发布,然后重跑只读核对才算了结。Apple 代码签名与公证仍是 `adhoc`。
|
||||
|
||||
@@ -6099,9 +6099,10 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **根因**:这个 harness 用桩 `systemctl` 驱动巡检脚本,Windows 上 `spawn systemctl` 直接 `ENOENT`,服务态检查不可能通过;它验证的是 Linux + systemd 的生产形态。
|
||||
- **做法**:本机只跑 `npm run check:production-health-patrol-env`(本轮 OK)确认巡检变量口径;`check:production-health-patrol` 留给服务器/CI 复核,别据此判定巡检脚本本身坏了。
|
||||
|
||||
## 2026-09-28 渠道清单的单元测试绿不等于线上清单符合契约:`dev-mac` 线上仍是单架构
|
||||
## 2026-09-28 线上 dev-mac 的更新包是「上一版 + 另一个渠道身份」:清单扫描选中了构建目录残留产物
|
||||
|
||||
- **现象**:`build-release` 的单元测试里 `universal uses the Mac channel and the same signed artifact for both architectures` 一直是绿的,但线上 `https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/dev-mac/latest.json`(`version=0.1.142`、`pub_date=2026-09-24`、`commit=c07c10c0c`)的 `platforms` 只有 `darwin-aarch64`。测试证明的是「代码会这么发」,不能证明「线上已经这么发」。
|
||||
- **判据**:用只读脚本核对已发布对象,而不是看单测——`npm run check:agc-update-channel-manifests`(`AGC_UPDATE_VERIFY_DOWNLOAD=1` 时下载产物验签)。本轮实测:`dev-win` 与 `dev-mac` 的安装包都存在、`.sig` 与清单签名文本一致、两个产物都能用 `tauri.conf.json` 的公钥验签通过(`alg=ED`,`keyId=cb883447e3e87c4e`),但 `dev-mac` 的「两个 macOS 平台键」判 FAIL。
|
||||
- **做法**:任何「线上对象/清单契约」条款都用只读核对脚本取证;发现线上落后时,先确认这次发布对应的 commit 与时间(本轮 mac 发布早于 universal 改造),再用目标平台的构建机重发,不要在 Windows 上伪造 mac 清单。
|
||||
- **附注**:该 `dev-mac` 更新产物叫「陶泥儿 Release.app.tar.gz」,而渠道首装 DMG 叫「陶泥儿开发版_0.1.142_aarch64.dmg」;`dev` 渠道展示名约定是「陶泥儿开发版」,重发时一并核对 mac 更新产物的渠道身份。
|
||||
- **现象**:线上 `https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/dev-mac/latest.json`(`version=0.1.142`、`commit=c07c10c0c`)把更新包指向 `陶泥儿 Release.app.tar.gz`;下载解开看 `Contents/Info.plist`:`CFBundleShortVersionString=0.1.139`、`CFBundleIdentifier=world.genarrative.ai-game-creator.release`、`CFBundleName=陶泥儿 Release`。同一份清单的首装 DMG 却是 `陶泥儿开发版_0.1.142_aarch64.dmg`。也就是说签名是真的、对象也在,但**版本与渠道身份都是错的**。
|
||||
- **根因**:`build-macos-ci.mjs` 以前只 `rmSync` 「本轮要写的确切文件名」,构建目录里上一轮(或其它渠道身份)留下的 `*.app.tar.gz` 不会被清;而 `generateUpdateManifest()` 是**扫描构建目录、按优先级挑产物**(同名优先级再按字典序),于是挑走了残留的 release 身份包。mac 构建机是复用 workspace 的,这类残留会长期存在。
|
||||
- **判据**:只读核对要**打开产物看身份**,不能只看「地址存在 + 签名匹配」。`npm run check:agc-update-channel-manifests`(`AGC_UPDATE_VERIFY_DOWNLOAD=1`)现在会解出 mac 包的 `Info.plist`,断言「包内版本 == 清单版本」且「包内 identifier/产品名 == 本渠道身份」;本轮对线上取样得到两条 FAIL,正是这个缺陷。
|
||||
- **处理(2026-09-28 已修)**:构建前按后缀清空 `macos/` 下的 `*.app.tar.gz`、`*.app.tar.gz.sig`、`*.dmg`、`*.dmg.sha256`;构建后读 `.app/Contents/Info.plist` 断言版本/identifier/产品名;生成清单后再断言 `release.artifact` 就是本轮那一个。守卫在 `apps/ai-game-creator-shell/scripts/macos-release-identity.mjs`,回归用例直接用线上那份 0.1.139 release 身份包(`node --test` 59 passed)。
|
||||
- **教训**:凡是「按目录扫描挑产物」的发布步骤,都要么先清空同类产物、要么按本轮预期路径断言;只删「本轮要写的名字」等于把上一轮的坏包留在候选集里。还有一条更一般的:核对线上清单时,先看 decision-log 的现行口径(这里 macOS 已是 arm64 单架构),别拿过期里程碑文字当契约。
|
||||
|
||||
Reference in New Issue
Block a user