From e53992fdc2c52cf54b98d224383bb33606ff92ac Mon Sep 17 00:00:00 2001 From: suzmii Date: Mon, 21 Sep 2026 12:30:56 +0800 Subject: [PATCH 1/2] =?UTF-8?q?macOS=20=E5=8F=91=E5=B8=83=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=8F=AA=E5=87=BA=20arm64=20=E5=8D=95=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=20-=20build-macos-ci=20=E5=9B=BA=E5=AE=9A=20aarch64-a?= =?UTF-8?q?pple-darwin=EF=BC=9A=E5=8D=95=E6=9E=B6=E6=9E=84=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E3=80=81=E5=8F=AA=E8=B7=91=20arm64=20=E9=9A=94?= =?UTF-8?q?=E7=A6=BB=20smoke=E3=80=81=E9=A6=96=E8=A3=85=E5=8C=85=E5=91=BD?= =?UTF-8?q?=E5=90=8D=20<=E4=BA=A7=E5=93=81=E5=90=8D>=5F<=E7=89=88=E6=9C=AC?= =?UTF-8?q?>=5Faarch64.dmg=EF=BC=8C=E6=9E=84=E5=BB=BA=E6=B8=85=E5=8D=95?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=20smokes=20=E4=B8=8E=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E6=B8=85=E5=8D=95=E5=B9=B3=E5=8F=B0=E9=94=AE=20-=20stage-node-?= =?UTF-8?q?runtime=20=E7=9A=84=20universal-apple-darwin=20=E6=81=A2?= =?UTF-8?q?=E5=A4=8D=E5=A4=B1=E8=B4=A5=E5=85=B3=E9=97=AD=E5=B9=B6=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E5=8E=9F=E5=9B=A0=EF=BC=9A=E9=9A=8F=E5=8C=85=20Node?= =?UTF-8?q?=20=E5=8F=AA=E6=9C=89=E5=8D=95=E6=9E=B6=E6=9E=84=E5=AE=98?= =?UTF-8?q?=E6=96=B9=E5=8F=91=E8=A1=8C=E7=89=88=EF=BC=8C=E6=8C=89=E5=AE=BF?= =?UTF-8?q?=E4=B8=BB=E6=9E=B6=E6=9E=84=E6=94=BE=E8=A1=8C=E4=BC=9A=E8=AE=A9?= =?UTF-8?q?=20Intel=20=E4=B8=8A=E8=BF=99=E4=BB=BD=E4=BE=A7=E8=BD=A6?= =?UTF-8?q?=E4=B8=8D=E5=8F=AF=E6=89=A7=E8=A1=8C=EF=BC=8C=E4=B8=94=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E5=8C=85=E8=87=AA=E6=A3=80=E6=8C=89=20process.arch=20?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=8A=93=E4=B8=8D=E5=88=B0=20-=20=E5=AE=88?= =?UTF-8?q?=E5=8D=AB=E7=94=A8=E4=BE=8B=E6=94=B9=E4=B8=BA=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E5=9B=BA=E5=AE=9A=20arm64=20=E7=9B=AE?= =?UTF-8?q?=E6=A0=87=E4=B8=8E=20=5Faarch64.dmg=20=E5=90=8E=E7=BC=80?= =?UTF-8?q?=E3=80=81universal=20=E7=9B=AE=E6=A0=87=E5=BF=85=E9=A1=BB?= =?UTF-8?q?=E6=8A=9B=E9=94=99=20-=20Jenkinsfile=20=E4=B8=8E=E4=B8=89?= =?UTF-8?q?=E4=BB=BD=E6=8A=80=E6=9C=AF/=E8=BF=90=E7=BB=B4=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E5=90=8C=E6=AD=A5=E5=8D=95=E6=9E=B6=E6=9E=84=E5=8F=A3?= =?UTF-8?q?=E5=BE=84=EF=BC=9A=E6=B8=85=E5=8D=95=E5=8F=AA=E7=99=BB=E8=AE=B0?= =?UTF-8?q?=20darwin-aarch64=EF=BC=8CIntel=20=E9=9D=9E=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=EF=BC=8C=E6=81=A2=E5=A4=8D=20universal=20=E7=9A=84=E5=89=8D?= =?UTF-8?q?=E7=BD=AE=E6=9D=A1=E4=BB=B6=E5=86=99=E5=9C=A8=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=87=8C=20-=20=E5=85=B1=E4=BA=AB=E8=AE=B0=E5=BF=86=E8=A1=A5?= =?UTF-8?q?=202026-09-21=20=E5=86=B3=E7=AD=96=E8=AE=B0=E5=BD=95=EF=BC=8Cpi?= =?UTF-8?q?tfalls=20=E8=AE=B0=E4=B8=8B=E3=80=8C=E5=8F=AA=E5=B8=A6=E5=AE=BF?= =?UTF-8?q?=E4=B8=BB=E6=9E=B6=E6=9E=84=E7=9A=84=E9=80=9A=E7=94=A8=E5=8C=85?= =?UTF-8?q?=E3=80=8D=E9=99=B7=E9=98=B1=EF=BC=8C=E9=81=BF=E5=85=8D=E5=86=8D?= =?UTF-8?q?=E6=AC=A1=E7=94=A8=E8=BF=87=E6=B8=A1=E5=AE=9E=E7=8E=B0=E7=B3=8A?= =?UTF-8?q?=E8=BF=87=E5=8E=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scripts/build-macos-ci.mjs | 39 +++++++++++-------- .../scripts/prepare-macos-codex.test.mjs | 8 +++- .../scripts/stage-node-runtime.mjs | 28 ++++++------- .../scripts/stage-node-runtime.test.mjs | 16 ++------ .../shared-memory/decision-log.md | 10 +++++ docs/project-memory/shared-memory/pitfalls.md | 4 +- ...方案】AGC客户端更新检查与下载-2026-08-31.md | 12 +++--- ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 4 +- ...发运维】本地开发验证与生产运维-2026-05-15.md | 8 ++-- ...kinsfile.ai-game-creator-shell-macos-build | 10 ++--- 10 files changed, 72 insertions(+), 67 deletions(-) diff --git a/apps/ai-game-creator-shell/scripts/build-macos-ci.mjs b/apps/ai-game-creator-shell/scripts/build-macos-ci.mjs index 442df54d1..49bf93abf 100644 --- a/apps/ai-game-creator-shell/scripts/build-macos-ci.mjs +++ b/apps/ai-game-creator-shell/scripts/build-macos-ci.mjs @@ -9,6 +9,7 @@ import { fileURLToPath } from 'node:url'; import { generateUpdateManifest, prepareReleaseVersion, + resolveManifestPlatformKeys, resolveReleaseContext, resolveReleasePartition, runTauriBuild, @@ -20,10 +21,13 @@ import { } from './verify-updater-signature.mjs'; /** - * AGC macOS 分区(`-mac`)发布入口:构建 universal 包 → 双架构 smoke → 生成 universal DMG + * AGC macOS 分区(`-mac`)发布入口:构建 arm64 单架构包 → arm64 隔离 smoke → 生成 arm64 DMG * → 生成分区清单 latest.json → 用产物内烘焙的公钥验签 → 按 dry-run 决定是否上传 OSS。 * * 边界: + * - 只出 Apple Silicon(arm64)单架构:清单只登记 `darwin-aarch64`。Intel 侧要可用,前提是随包 Node + * 也能按架构各带一份(`stage-node-runtime.mjs` 对 universal 目标失败关闭);在实现之前**不得** + * 把 arm64 产物登记成 `darwin-x86_64`,否则 Intel 客户端会装到跑不起来的包。 * - Apple 签名与公证暂缺:本入口剥离 `APPLE_*` 凭据让 Tauri 跳过 Apple 签名,但**不能传 * `--no-sign`** —— 该标志同时会跳过 updater 的 minisign 签名,产物就没有 `.sig`; * 未签名 + 未公证必须显式记录而非静默通过; @@ -94,11 +98,14 @@ process.env.AGC_UPDATE_OSS_BASE_URL ||= `https://${bucket}.${endpoint}/agc`; const dryRun = readReleaseDryRun(); process.env.CARGO_TARGET_DIR = path.join(appRoot, 'src-tauri/target'); -const context = resolveReleaseContext(['--target=universal-apple-darwin']); +// 单架构目标:清单侧 `resolveManifestPlatformKeys` 只为它登记 darwin-aarch64。 +const macTarget = 'aarch64-apple-darwin'; +const context = resolveReleaseContext([`--target=${macTarget}`]); const partition = resolveReleasePartition(context.channel, context.target); const version = await prepareReleaseVersion(context); -// 首装包名必须保持 `<产品名>_<版本>_universal.dmg`:清单侧按该后缀唯一匹配本次产物。 -const firstInstallName = `${productName}_${version}_universal.dmg`; +// 首装包名必须让清单侧的单架构分支唯一匹配:`<产品名>_<版本>_<架构>.dmg`, +// 架构段用 Tauri 的 aarch64 口径(不是 updater 平台键的 arm64 / x86_64)。 +const firstInstallName = `${productName}_${version}_aarch64.dmg`; // 幂等边界:workspace 会保留上一轮产物。先删掉本次将要写出的对象,否则 // 1) hdiutil 会因同名 DMG 已存在直接失败(首次实跑即命中); @@ -117,7 +124,7 @@ for (const stale of [ } const args = [ - '--target=universal-apple-darwin', + `--target=${macTarget}`, '--bundles', 'app', '--ci', @@ -132,16 +139,13 @@ const command = (binary, argv, options = {}) => runTauriBuild(args, context); const app = path.join(context.bundleRoot, 'macos', appBundleName); -for (const architecture of ['arm64', 'x86_64']) { - command(process.execPath, [ - path.join(appRoot, 'scripts/check-macos-bundle.mjs'), - app, - architecture, - '--universal', - ]); -} +command(process.execPath, [ + path.join(appRoot, 'scripts/check-macos-bundle.mjs'), + app, + 'arm64', +]); -// DMG 放在 bundle 根目录下:渠道清单的首装包选择会扫描该目录,命名必须匹配 `__universal.dmg`。 +// DMG 放在 bundle 根目录下:渠道清单的首装包选择会扫描该目录,命名必须匹配 `__aarch64.dmg`。 const dmgDirectory = path.join(context.bundleRoot, 'macos'); fs.mkdirSync(dmgDirectory, { recursive: true }); const dmg = path.join(dmgDirectory, firstInstallName); @@ -170,7 +174,7 @@ const release = await generateUpdateManifest(context); assert.equal( path.resolve(release.downloadArtifact), path.resolve(dmg), - '首装包必须锁定本次生成的 universal DMG', + '首装包必须锁定本次生成的 arm64 DMG', ); // 上传前门禁:用产物里烘焙的公钥复核更新包签名。验不过就停在这里,绝不写 OSS。 @@ -266,8 +270,9 @@ fs.writeFileSync( firstInstallSha256: dmgHash, manifest: 'latest.json', }, - smokes: ['arm64', 'x86_64'], - intelSmoke: process.arch === 'arm64' ? 'Rosetta' : 'native', + // 单架构发布:只跑 arm64 隔离 smoke;Intel 未支持(清单里没有 darwin-x86_64 键)。 + smokes: ['arm64'], + manifestPlatformKeys: resolveManifestPlatformKeys(context.target), }, null, 2, diff --git a/apps/ai-game-creator-shell/scripts/prepare-macos-codex.test.mjs b/apps/ai-game-creator-shell/scripts/prepare-macos-codex.test.mjs index 4caae3656..b62fe76a1 100644 --- a/apps/ai-game-creator-shell/scripts/prepare-macos-codex.test.mjs +++ b/apps/ai-game-creator-shell/scripts/prepare-macos-codex.test.mjs @@ -178,8 +178,12 @@ test('macOS release entry and smoke script derive product names from config and assert.ok(entry.includes('readProductName'), '入口必须从 Tauri 配置读产品名'); assert.ok(!entry.includes('陶泥儿'), 'macOS 发布入口不得写死产品名'); assert.ok( - entry.includes('_${version}_universal.dmg'), - '首装包名必须保留清单侧唯一匹配所需的后缀', + entry.includes("const macTarget = 'aarch64-apple-darwin'"), + 'macOS 发布入口必须固定单架构目标', + ); + assert.ok( + entry.includes('_${version}_aarch64.dmg'), + '首装包名必须保留清单侧单架构分支唯一匹配所需的后缀(Tauri 口径 aarch64)', ); const smoke = fs.readFileSync( diff --git a/apps/ai-game-creator-shell/scripts/stage-node-runtime.mjs b/apps/ai-game-creator-shell/scripts/stage-node-runtime.mjs index 8e6559dac..489ce0b2b 100644 --- a/apps/ai-game-creator-shell/scripts/stage-node-runtime.mjs +++ b/apps/ai-game-creator-shell/scripts/stage-node-runtime.mjs @@ -59,27 +59,23 @@ export function readInstalledNodeLicense( return result.content; } -export function targetRuntime( - target, - { platform = process.platform, arch = process.arch } = {}, -) { +export function targetRuntime(target) { const targets = { 'x86_64-pc-windows-msvc': ['win32', 'x64'], 'aarch64-apple-darwin': ['darwin', 'arm64'], 'x86_64-apple-darwin': ['darwin', 'x64'], }; - if (target === 'universal-apple-darwin') { - // Tauri 通用包含 arm64+x86_64 两个 slice,而这里的 Node 便携运行时是 - // 「构建宿主架构」的单架构发行版:macOS 构建机(当前为 arm64)只能提供 - // 自身架构的官方 Node。真正的通用 Node 需要另行下载另一架构发行版并 - // 用 lipo 合并,属未完成项;在此之前 non-darwin 宿主必须失败关闭。 - if (platform !== 'darwin') { - throw new Error( - `Node 运行时不支持在 ${platform}/${arch} 宿主构建通用 macOS 包:${target}`, - ); - } - return { platform: 'darwin', arch }; - } + // 随包 Node 是**单架构**官方发行版:一份运行时只服务它自己的架构。 + // macOS 发布当前固定为 aarch64-apple-darwin 单架构包(Intel 未支持), + // universal 目标没有正确的运行时来源,必须失败关闭——绝不能退化成 + // 「按宿主架构暂存一份 arm64」:那样通用包自检(按 process.arch)能过, + // 但 Intel 机器上这份运行时不可执行,用户拿到的是坏包。 + if (target === 'universal-apple-darwin') + throw new Error( + 'Node 运行时不支持 universal-apple-darwin:随包 Node 只有单架构发行版,' + + '通用包需按架构各带一份(另行下载另一架构官方发行版)之后才能构建;' + + '当前 macOS 发布固定为 aarch64-apple-darwin 单架构', + ); const value = targets[target]; if (!value) throw new Error(`Node 运行时不支持发布目标:${target}`); return { platform: value[0], arch: value[1] }; diff --git a/apps/ai-game-creator-shell/scripts/stage-node-runtime.test.mjs b/apps/ai-game-creator-shell/scripts/stage-node-runtime.test.mjs index 336a19ec6..c2b52afb1 100644 --- a/apps/ai-game-creator-shell/scripts/stage-node-runtime.test.mjs +++ b/apps/ai-game-creator-shell/scripts/stage-node-runtime.test.mjs @@ -281,20 +281,10 @@ test('native target and macOS dynamic dependency policy reject nonportable Node' platform: 'darwin', arch: 'arm64', }); - assert.deepEqual( - targetRuntime('universal-apple-darwin', { - platform: 'darwin', - arch: 'arm64', - }), - { platform: 'darwin', arch: 'arm64' }, - ); + // universal 必须失败关闭:只带宿主架构那一份运行时,Intel 上不可执行。 assert.throws( - () => - targetRuntime('universal-apple-darwin', { - platform: 'linux', - arch: 'x64', - }), - /不支持在 linux\/x64 宿主构建通用 macOS 包/u, + () => targetRuntime('universal-apple-darwin'), + /universal-apple-darwin/u, ); assertPortableMacNode( '/node:\n\t/usr/lib/libSystem.B.dylib (compatibility version 1)\n', diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 54bb65a08..bb21713d5 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -1,5 +1,15 @@ # 决策记录 +## 2026-09-21 macOS 发布改为只出 arm64 单架构(Intel 暂不支持) + +- 背景:Mac 发布管线按 `universal-apple-darwin` 构建,但随包 Node 便携运行时只有**单架构官方发行版**(`stage-node-runtime.mjs` 从 `process.execPath` 取材),于是 macOS Job #7~#13 连续失败在「Node 运行时不支持发布目标:universal-apple-darwin」。期间出现过一版「按宿主架构放行」的过渡实现,它能骗过通用包自检(`check-macos-bundle.mjs` 按 `process.arch` 校验),但 Intel 上那份 arm64 侧车不可执行,并且已发布的 dev-mac 0.1.86 就带着这个缺陷。 +- 决策:macOS 固定只构建 `aarch64-apple-darwin`,渠道清单只登记 `darwin-aarch64`(不再登记 `darwin-x86_64`,避免把 arm64 产物发给 Intel 客户端);`targetRuntime('universal-apple-darwin')` 保持失败关闭,入口 `build-macos-ci.mjs` 只跑 arm64 隔离 smoke,首装包命名 `<产品名>_<版本>_aarch64.dmg`。 +- 原因:要让 Intel 真正可用,必须让发布包按架构各带一份**同版本**运行时(另下载另一架构官方发行版)+ 通用包自检按架构分别校验,这是一条独立且更大的改动;在 DDL 前用「只带宿主架构」糊过去等于把坏包发给 Intel 用户,比暂不支持更糟。单架构同时把构建时间与产物体积减半。 +- 验证:`node --test apps/ai-game-creator-shell/scripts/*.test.mjs` 92/92(含 `targetRuntime('universal-apple-darwin')` 必须抛错、入口固定 arm64 目标与 `_aarch64.dmg` 后缀的守卫用例);`npm run check:production-ops`、`check:encoding`、`check:doc-index`、prettier、eslint、`git diff --check` 通过;真实端到端由 Jenkins Mac Job 验证(清单只含 `darwin-aarch64`、DMG 与更新包唯一匹配)。 +- 影响范围:`apps/ai-game-creator-shell/scripts/{build-macos-ci.mjs,stage-node-runtime.mjs,prepare-macos-codex.test.mjs}`、`jenkins/Jenkinsfile.ai-game-creator-shell-macos-build`、本仓三份技术/运维文档与共享记忆。未动 Windows 渠道、未动 Rust 侧运行时解析(单架构仍是扁平 `game-runtime/node/`)。 +- 恢复 Intel 的路径:先在 staging 支持按架构各带一份同版本运行时并让通用包自检按架构校验,再切回 universal 目标、把 `darwin-x86_64` 键登记回去并补 Intel 真机验收。 +- 已知未覆盖:Intel Mac 用户的更新体验(清单缺 `darwin-x86_64` 键,客户端会「无可用更新」,未实测其 UI 文案);Mac 包里仍并列携带两套 Codex 原生依赖(只运行 arm64 切片,可按需瘦身)。 + ## 2026-09-21 图集切片上限:客户端结果门从 64 对齐到平台契约的 256 - 背景:现场(项目 `gameagent-6e53c9e8`,2026-09-21 07:54)「AI 生成图标素材」失败:`platform-generation-result-unknown: 异步生成完成结果无法绑定到 operationId:External Editor 旧同步结果的图集切片超过 64 个`。任务账本(`.agent/runtime/asset-generation-tasks/tasks.json`)显示它跑了 99 秒、`assetId` 为空、没有落任何素材;对应的持久化请求(`canvas-generation-requests/manual-canvas-asset-generate/slot-560175669f….json`)是 `sliceMode: connected-components` + `sliceCount: null`(自动切分)。也就是**平台已经生成并切完图了,是客户端在绑定结果这一步把整条结果判失败**,付费产物被丢弃。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 8e22449c3..93856b4c4 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -1,8 +1,8 @@ # 踩坑与排障记录 -## macOS 通用包里的 Node 便携运行时仍是宿主架构 +## macOS 只出 arm64 单架构,universal 必须失败关闭 -`stage-node-runtime.mjs` 只把**构建宿主的 Node** 打成便携运行时(`targetRuntime` 只支持 per-arch 目标),而 `build-macos-ci.mjs` 构建的是 `universal-apple-darwin`:2026-09-21 macOS Job #13 因此在 `stageNodeRuntime` 直接抛 `Node 运行时不支持发布目标:universal-apple-darwin`。当前按「通用包 + 宿主架构(arm64 构建机 → arm64)Node」放行:`check-macos-bundle.mjs` 按 `process.arch` 校验,所以本机自检通过,但**Intel Mac 上该侧车不可执行**。要真正通用需要另行下载另一架构官方 Node 发行版并用 `lipo -create` 合并,同时把 bundle 检查改成接受 `universal` 并核验 `lipo -archs node = arm64 x86_64`;在此之前不得宣称 macOS 通用包在 Intel 上可用。 +`stage-node-runtime.mjs` 只把**构建宿主的 Node**打成便携运行时(官方发行版是单架构,没有 universal 发行版),而 2026-09-21 之前 `build-macos-ci.mjs` 构建的是 `universal-apple-darwin`:macOS Job #7~#13 因此在 `stageNodeRuntime` 直接抛「Node 运行时不支持发布目标:universal-apple-darwin」。期间出现过一版「按宿主架构放行」的过渡实现(`targetRuntime` 对 universal 返回宿主架构),它能骗过通用包自检(`check-macos-bundle.mjs` 按 `process.arch` 校验),但**Intel Mac 上这份 arm64 侧车不可执行**,等于把坏包发出去。当前决策:macOS 固定只构建 `aarch64-apple-darwin`,清单只登记 `darwin-aarch64`,`targetRuntime('universal-apple-darwin')` 保持失败关闭。恢复 Intel 的正确路径是先在 staging 支持按架构各带一份**同版本**运行时(另下载另一架构官方发行版)并让通用包自检按架构分别校验,再切回 universal 目标、把 `darwin-x86_64` 键登记回去;不得用「只带宿主架构」充数,也不得把 arm64 产物登记成 x86_64 键。 ## release 冷备空间不足会把生产留在维护态 diff --git a/docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md b/docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md index 8fa50c4f1..69559ba2a 100644 --- a/docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md +++ b/docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md @@ -98,11 +98,11 @@ | 系统 | 构建目标 | 清单平台键 | 更新包 | 清单地址 | | --------- | ------------------------ | ---------------------------------------------- | ------------------------ | ------------------------------------ | | Windows | `x86_64-pc-windows-msvc` | `windows-x86_64` | NSIS `.exe` + `.exe.sig` | `/agc/-win/latest.json` | -| macOS | `universal-apple-darwin` | `darwin-aarch64` + `darwin-x86_64`(同一对象) | `*.app.tar.gz` + `.sig` | `/agc/-mac/latest.json` | +| macOS | `aarch64-apple-darwin` | `darwin-aarch64` | `*.app.tar.gz` + `.sig` | `/agc/-mac/latest.json` | - 对象布局:清单固定写成 `agc/-win|mac/latest.json`;安装包与签名写成同一分区的 `/` 与 `.sig`。 -- macOS 正式交付使用 universal 主程序:两个平台键指向同一个 `.app.tar.gz` 与签名,一份产物同时服务 Apple Silicon 与 Intel。单架构目标(`aarch64-apple-darwin` / `x86_64-apple-darwin`)只用于本机诊断,不登记正式分区清单——单架构构建不可轮流覆盖同一个 `latest.json` 并宣称双架构均可更新。 -- universal 主程序同时携带分目录的 arm64/x64 原生 Codex 组件:每个组件保持上游单架构布局与独立 SHA-256 清单,运行中的主程序切片只选择同架构目录,不得把两套原生包的元数据或辅助程序混装。 +- macOS 当前只出 Apple Silicon 单架构包(`aarch64-apple-darwin`),清单只登记 `darwin-aarch64`;**不再**登记 `darwin-x86_64`,避免把 arm64 产物发给 Intel 客户端。恢复 Intel(universal)的前置条件是随包 Node 也能按架构各带一份:`stage-node-runtime.mjs` 对 universal 目标失败关闭,因为一台构建机只能提供宿主架构的官方 Node,只带一份会让 Intel 上该运行时不可执行,而通用包自检按 `process.arch` 校验、抓不到这个问题。 +- 主程序当前只出 arm64 单架构,但 `tauri.macos.conf.json` 仍并列映射 `darwin-arm64` / `darwin-x64` 两套原生 Codex 组件:每个组件保持上游单架构布局与独立 SHA-256 清单,运行中的主程序切片只选择同架构目录,不得把两套原生包的元数据或辅助程序混装;随包 Node 只有宿主架构那一份(本地自检会按架构核对并实际执行它)。 - 构建期要求:打开 `bundle.createUpdaterArtifacts` 以生成 `.sig`;构建环境提供签名私钥与密码(私钥内容不得入库);公钥写入客户端配置。公钥在首个带更新能力的版本发布后不可更换,更换等于放弃自动更新(只能手动重装)。 - 版本递增按渠道及系统分区独立进行:发布脚本读取该分区远端 `latest.json` 的 `version`,与本地版本取较高者递增 patch;不同分区的远端版本互不影响。 - 版本高水位:仅 dev 的 Windows 分区在迁移窗口内取「分区清单版本」与「旧协议迁移指针版本」较大值再递增,避免已发布旧客户端版本倒退。迁移窗口结束(旧指针 404)后只读分区清单;release、自定义渠道与所有 Mac 分区均不参与旧指针比较。 @@ -146,7 +146,7 @@ | 条款 | 验收方式 | 证据 | | ---------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | 渠道与端点映射、渠道校验 | `node --test apps/ai-game-creator-shell/scripts/build-release.test.mjs` | 通过(默认渠道、错配失败关闭、未知渠道失败关闭) | -| macOS universal 清单与双架构依赖 | 定向发布脚本与安装包验证 | 两个平台键同 URL/签名;两套资源独立校验,真机验收另记 | +| macOS 单架构清单与随包资源 | 定向发布脚本与安装包验证 | 清单只有 `darwin-aarch64`;DMG/更新包按架构命名并唯一匹配;真机验收另记 | | 缺签名时失败关闭 | 同上 | 通过 | | 开发态不检查更新 | `vitest run apps/ai-game-creator-shell/tests/appUpdate.test.ts` | 通过(开关关闭时不请求清单) | | 旧自研链路整条删除 | 代码检索无残留命令、事件与白名单条目 | 通过(`download_agc_update` / 下载事件 / 清单常量均无残留) | @@ -168,10 +168,10 @@ 已决策: -- macOS 采用 universal 包,两个平台键对应同一更新产物;主程序用 lipo 检查两种架构,Codex 资源分别做原生身份/摘要与启动验证。Rosetta 结果不代替 Intel 真机验收。 +- macOS 当前只验 arm64:主程序用 `lipo` 检查架构,Codex 与随包 Node 做原生身份/摘要与启动验证。恢复 Intel 后必须补回双架构 smoke 与 Intel 真机验收,Rosetta 结果不能替代。 - 旧客户端迁移桥:保留一个版本周期。渠道清单上线后,发布管线同时把旧的 `agc/latest.json`(sha256 格式)指向 `dev-win` 最新安装包,让已发布客户端自动升级到新协议;下个周期整条删除。 - 签名密钥:由本仓库维护者生成并保管,私钥保存在仓库外(`%USERPROFILE%\.tauri\genarrative-agc-updater.key`),只有公钥进入客户端配置;Jenkins 用受保护凭据 `AgcUpdaterSigningKey` 与 `AgcUpdaterSigningKeyPassword` 注入为 Tauri 打包器读取的 `TAURI_SIGNING_PRIVATE_KEY` 与 `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`,本机可用 `TAURI_SIGNING_PRIVATE_KEY_PATH` 指向同一私钥。当前密钥不带密码;首次发布前仍可重新生成,首次发布后不可更换。 -- macOS 发布方式:已接入专用 macOS Jenkins 节点(label `genarrative-agc-macos`,EXCLUSIVE 单 executor),由 `Jenkinsfile.ai-game-creator-shell-macos-build` 执行 `scripts/build-macos-ci.mjs` 完成 universal 构建、双架构隔离 smoke、universal DMG、分区清单生成、更新包验签与 OSS 上传。`AGC_RELEASE_DRY_RUN` 默认为关(与 Windows 渠道对称,即直接发布),只有勾选后才退化为「只打印上传计划、不写 OSS」的演练。 +- macOS 发布方式:已接入专用 macOS Jenkins 节点(label `genarrative-agc-macos`,EXCLUSIVE 单 executor),由 `Jenkinsfile.ai-game-creator-shell-macos-build` 执行 `scripts/build-macos-ci.mjs` 完成 arm64 单架构构建、arm64 隔离 smoke、arm64 DMG(`<产品名>_<版本>_aarch64.dmg`)、分区清单生成、更新包验签与 OSS 上传。`AGC_RELEASE_DRY_RUN` 默认为关(与 Windows 渠道对称,即直接发布),只有勾选后才退化为「只打印上传计划、不写 OSS」的演练。 - macOS 代码签名与公证暂缺:产物为未签名 + 未公证,构建入口剥离 `APPLE_*` 凭据跳过 Apple 签名,不传 `--no-sign`(它还会跳过 updater 的 minisign 签名,产物将没有 `.sig`);构建清单实测记录 `appleSigned` 与签名类型,`latest.json` 侧固定记录 `notarized=false`,首装需用户在 Gatekeeper 中手动放行。该限制作为已知未验证项记录,不静默通过;「安装 → 重启接管新版本」的自动更新闭环仍需实机验收。 - 更新包验签门禁:构建完成、上传 OSS 之前,用产物内烘焙的 `plugins.updater.pubkey` 复核 `<更新包>.sig`(Tauri 使用 minisign 的 `ED` 预哈希模式)。keyId 不一致或校验失败立即失败关闭,禁止上传——客户端校验失败会直接拒绝安装,且公钥发布后不可更换。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 0657da9a2..5417661f7 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -71,7 +71,7 @@ ### 环境与工作流 -- 客户端交付配套 Node/npm;发布包从本机已安装且与目标平台/架构一致的工具链制作受校验资源,保留许可并校验内容摘要。安装态不依赖系统 PATH 的 Node;开发态可使用已验证的宿主运行时。不得从项目或相对 PATH 加载伪造运行时。 +- 客户端交付配套 Node/npm;发布包从本机已安装且与目标平台/架构一致的工具链制作受校验资源,保留许可并校验内容摘要。安装态不依赖系统 PATH 的 Node;开发态可使用已验证的宿主运行时。不得从项目或相对 PATH 加载伪造运行时。随包运行时是**单架构**官方发行版,因此 macOS 当前只构建 `aarch64-apple-darwin` 单架构包;要出 universal 必须先让 staging 支持按架构各带一份同版本运行时,在此之前 universal 目标失败关闭,不得只带宿主架构那一份糊过去。 - 新建 Web 游戏在生图和大量实现前执行客户端环境预检,检查 Node/npm 的实际版本、浏览器启动和 CDP 可用性。报告只包含安全状态、版本、耗时和错误码。缺失或异常必须尽早返回阻塞,不能指示模型改宿主环境、全盘搜索或自行下载一套运行时。编辑器工程不强制 Web 工具链。 - 预检不安装依赖、不修改项目 revision、不请求平台生成;构建仍执行项目自己的 npm 脚本。Codex 隔离 HOME 与平台凭据边界保持不变,客户端把已验证的运行时加入执行 PATH,不能把宿主凭据目录交给模型。 - 第一轮先明确本次必需玩法、素材和验收项。同批独立读取尽量合并,必需图片一次规划;已有且可用的资产复用。已有目标全部通过后给出交付结果,非阻塞的新点子列为后续工作,不在收尾时主动开启新的生产链。 @@ -579,7 +579,7 @@ Supervisor 认领该回执后,由父 run 自己为每个原 delivery 逐一创 - 调度边界:正式 DAG、manifest、Agent task/session/run 身份、队列、锁、委派、all-join、完成门、Provider lifecycle、持久 retry/handoff 与 `needs-reconciliation` 继续由现有 AGC Runtime 掌控。每个被调度节点在 `codex_cli` 模式下直接启动一次非交互 `codex exec` 充当该节点的推理 Agent;Codex 返回当前 Runtime 广告函数的结构化调用,Runtime 仍是唯一 ToolHost,不允许 CLI 自己写项目、执行命令、调用 MCP 或形成第二套 revision / verification 真相。 - 安装包侧车:Windows x64 release 固定随 Tauri resource 打包 `@openai/codex@0.155.1` 的原生 `codex.exe`;Rust build script 从 AGC 子包锁定依赖 stage 到 resource,并写入版本与 SHA-256 清单。固定版本只在 `build_support/codex_bundle.rs` 声明一次,构建脚本、宿主补丁执行器身份、逐次审批协议允许列表和模型目录捕获共同引用它,避免多处字面量漂移。Windows 侧车映射只写入 `tauri.windows.conf.json`,通用 `tauri.conf.json` 不得让 Linux / macOS 构建依赖未生成的 Windows 二进制。运行时只在文件摘要和 `codex-cli` 版本同时匹配清单时优先选内置侧车;缺失、损坏或版本漂移时跳过它,按既有 npm 安装、PATH 顺序回退。安装包同时携带 Apache-2.0 第三方声明;API Key、`auth.json`、Cookie、Token、用户 `CODEX_HOME`、用户配置和项目数据绝不打包。 - Windows x64 release 安装包只生成 NSIS,不生成 MSI:`tauri.windows.conf.json` 的 `bundle.targets` 固定为 `["nsis"]`,通用配置继续保留其它平台的默认打包目标。安装后的产品名、开始菜单 / 桌面快捷方式和 EXE 产品描述统一由 `tauri.conf.json` 的 `productName: "陶泥儿"` 生成;应用 identifier 与内部可执行文件名保持稳定。内置 Codex 资源安装到顶层 `coding-agent/win-x64/`,运行时从同一路径查找 `bin/codex.exe` 与 `manifest.json`;仓库 staging 仍使用 `resources/codex/win-x64/`,包内子目录、组件名、版本和完整性校验保持原合同。 -- macOS 安装包必须携带锁定版本的原生 Codex、`codex-code-mode-host`、`rg`、上游 zsh、`codex-package.json` 和第三方声明,保留上游相对布局;构建时按 Cargo 目标选择 npm 原生依赖,缺文件、版本或目标不匹配立即失败,不借用开发机 PATH 里的 Codex。资源只在 `tauri.macos.conf.json` 映射到 `Contents/Resources/coding-agent/mac-native/darwin-arm64/` 与 `darwin-x64/`。构建与运行共享平台文件白名单,运行时由当前 `.app/Contents/MacOS` 定位相邻 `Resources`,完整性与版本验证通过后优先使用内置组件;失败沿既有外部安装回退,不能运行未校验的内置文件。universal 主程序同时携带两套独立原生资源,运行切片按 Cargo 目标只选择对应目录;macOS 构建必须预备两套锁定原生依赖,不读取全局 Codex。 +- macOS 安装包必须携带锁定版本的原生 Codex、`codex-code-mode-host`、`rg`、上游 zsh、`codex-package.json` 和第三方声明,保留上游相对布局;构建时按 Cargo 目标选择 npm 原生依赖,缺文件、版本或目标不匹配立即失败,不借用开发机 PATH 里的 Codex。资源只在 `tauri.macos.conf.json` 映射到 `Contents/Resources/coding-agent/mac-native/darwin-arm64/` 与 `darwin-x64/`。构建与运行共享平台文件白名单,运行时由当前 `.app/Contents/MacOS` 定位相邻 `Resources`,完整性与版本验证通过后优先使用内置组件;失败沿既有外部安装回退,不能运行未校验的内置文件。macOS 当前只构建 arm64 单架构,但资源映射仍并列携带两套锁定原生 Codex 依赖(运行切片按 Cargo 目标只选择对应目录),恢复 Intel 时无需改动资源布局;随包 Node 只有宿主架构那一份,所以不得构建 universal 包。不读取全局 Codex。 - 内置插件的清单、运行入口与面板同时在 Windows/macOS 随包分发,继续由既有 PluginHost 的应用资源目录扫描入口发现;不携带开发依赖、缓存、测试或私有配置。插件文件随包不等于原生适配器跨平台:Cocos 进程桥接仍受现有 Windows 实现和 feature 门禁约束,macOS 原生桥接另行设计与验收,不复制 Windows DLL 冒充支持。系统 Node、用户 Cocos Creator、账号登录、网络和生成工程的 npm 工具链仍是现有外部前提,不在此次 Codex 侧车补齐中隐式变更。 - macOS 安装包验收必须包括:脱离仓库位置的 `.app` 资源与架构检查、受限 PATH/隔离 HOME 下内置 Codex 启动和 app-server 握手、必需文件缺失/篡改/平台错误的拒绝测试,以及 DMG 完整性检查。真实登录、Provider 对话、GUI 和 Cocos 操作必须独立列出证据,不能用压缩包生成或 `--version` 成功替代。未配置正式签名、公证的本地测试包不得作为公开发行包。 - macOS 安装包的系统下限取主程序和全部原生组件中的最高要求;锁定 Codex 0.155.1 原生依赖中 `codex-resources/zsh/bin/zsh` 的 `LC_BUILD_VERSION` 下限为 macOS 15.0(`bin/codex`、`codex-code-mode-host`、`codex-path/rg` 分别为 11.0 / 10.12),因此 `bundle.macOS.minimumSystemVersion` 明确为 `15.0`。更新原生依赖时重新检查 Mach-O 的系统下限,不能只按 AGC 主程序宣称兼容版本。0.155.1 的 macOS 原生包新增 `codex-resources/voice/`(语音宿主与 GStreamer 动态库);AGC 不启用语音能力,侧车清单只 stage 上述组件,不打包该目录,未来如启用语音需重新评估依赖与许可。 diff --git a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md index 801261b85..42f2212f6 100644 --- a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md +++ b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md @@ -728,19 +728,19 @@ Pingora current release 自审脚本 `scripts/ops/pingora-current-release-audit. ### AGC macOS 手动构建节点 -Mac universal 构建脚本位于 `jenkins/Jenkinsfile.ai-game-creator-shell-macos-build`,只对专用 `genarrative-agc-macos` 标签运行。节点按 EXCLUSIVE、单 executor 配置,Job 禁止并发且不设置 trigger;不接入现有每小时版本调度,不改 Windows 发布职责。它使它只在专用 Agent 目录下构建:默认按约定匹配 `$HOME/Library/Jenkins/agents//workspace/`(不写死节点名,节点改名后仍成立),也可用 `AGC_AGENT_ROOT` 显式覆盖;禁止指向开发 checkout 或共享其可写 target/node_modules。 +Mac 单架构(arm64)构建脚本位于 `jenkins/Jenkinsfile.ai-game-creator-shell-macos-build`,只对专用 `genarrative-agc-macos` 标签运行。节点按 EXCLUSIVE、单 executor 配置,Job 禁止并发且不设置 trigger;不接入现有每小时版本调度,不改 Windows 发布职责。它使它只在专用 Agent 目录下构建:默认按约定匹配 `$HOME/Library/Jenkins/agents//workspace/`(不写死节点名,节点改名后仍成立),也可用 `AGC_AGENT_ROOT` 显式覆盖;禁止指向开发 checkout 或共享其可写 target/node_modules。 Job 名为 `Genarrative-Agc-MacOS-Build`,SCM 直接读取仓库内上述 Jenkinsfile,参数为 `SOURCE_BRANCH`、`COMMIT_HASH`、`AGC_UPDATE_CHANNEL`、`AGC_RELEASE_VERSION`、`AGC_RELEASE_DRY_RUN`、`AGC_UPDATE_RELEASE_NOTES`、`OSSUTIL_BIN`、`CARGO_BUILD_JOBS`。渠道参数是基础名(不含系统,默认 `dev`),脚本不接受 `dev-mac` 这类系统后缀,写入分区固定推导为 `-mac`:这与 Windows Job 的 `-win` 对称,也延续已发布客户端的端点。 -该 Job 的职责是构建并发布 `-mac` 分区更新:执行 `npm ci` 后使用锁文件校验并补齐两种 macOS Codex 原生依赖,再调用 `scripts/build-macos-ci.mjs`(AGC 应用目录下)生成 universal app、arm64/x86_64 隔离 smoke、universal DMG 与分区清单 `latest.json`,用产物内烘焙的公钥复核更新包签名(`verify-updater-signature.mjs`),最后按 `AGC_RELEASE_DRY_RUN` 决定是否上传 OSS。签到会同时取 `master`,让渠道清单里上一次发布的 commit 可解析——缺了它更新摘要会退化成「最近客户端改动」(该步失败只降级摘要,不阻断发布)。产物名(`*.app`、updater 归档、DMG、卷名)一律从 Tauri `productName` 推导,校验脚本从包内 `Info.plist` 读取可执行名,改产品名不会让入口静默找错对象;隔离 smoke 用 `ditto --clone` 复制副本(实测整轮 6.8 秒,此前整包复制约 1 分钟),并在构建前删除本次将写出的 DMG/更新包/签名,保证归档产物一定来自本次构建。归档限 `artifacts/` 下的 DMG、SHA-256、`latest.json`、更新包签名、更新摘要、非敏感构建清单和源码 commit;不归档用户 HOME、Jenkins secret、原始工作目录或全量日志。 +该 Job 的职责是构建并发布 `-mac` 分区更新:执行 `npm ci` 后使用锁文件校验并补齐两种 macOS Codex 原生依赖,再调用 `scripts/build-macos-ci.mjs`(AGC 应用目录下)生成 arm64 单架构 app、arm64 隔离 smoke、arm64 DMG(`<产品名>_<版本>_aarch64.dmg`)与分区清单 `latest.json`,用产物内烘焙的公钥复核更新包签名(`verify-updater-signature.mjs`),最后按 `AGC_RELEASE_DRY_RUN` 决定是否上传 OSS。签到会同时取 `master`,让渠道清单里上一次发布的 commit 可解析——缺了它更新摘要会退化成「最近客户端改动」(该步失败只降级摘要,不阻断发布)。产物名(`*.app`、updater 归档、DMG、卷名)一律从 Tauri `productName` 推导,校验脚本从包内 `Info.plist` 读取可执行名,改产品名不会让入口静默找错对象;隔离 smoke 用 `ditto --clone` 复制副本(实测整轮 6.8 秒,此前整包复制约 1 分钟),并在构建前删除本次将写出的 DMG/更新包/签名,保证归档产物一定来自本次构建。归档限 `artifacts/` 下的 DMG、SHA-256、`latest.json`、更新包签名、更新摘要、非敏感构建清单和源码 commit;不归档用户 HOME、Jenkins secret、原始工作目录或全量日志。 发布凭据全部走 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/-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`,避免按渠道高水位递增时出现版本链回退。 产物边界: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 → `-mac` 分区清单 → 更新包验签 → 按 dry-run 决定上传,未做 Apple 签名与公证」;节点描述说明「`genarrative-agc-macos`、EXCLUSIVE 单 executor、仅手动触发、独立 workspace、sccache 在 HOME」。维护手法:该 Job 上 `POST job//config.xml` 会返回 500,节点侧同接口正常;因此改 Job 描述用脚本接口原地更新(保留构建历史),不要为了改描述删 Job 重建。 +Job 描述与节点描述是 Jenkins 侧元数据,**不随仓库同步**:Jenkinsfile 只回写参数定义,描述必须手工维护,否则会停留在建 Job 时的口径(2026-09-20 就出现过描述还写着「只构建归档、不签名不上传」,而实际已经是含签名、分区清单、验签与 OSS 上传的发布管线)。当前口径:Job 描述说明「构建 arm64 单架构 → arm64 smoke → DMG → `-mac` 分区清单(只登记 `darwin-aarch64`)→ 更新包验签 → 按 dry-run 决定上传,未做 Apple 签名与公证」;节点描述说明「`genarrative-agc-macos`、EXCLUSIVE 单 executor、仅手动触发、独立 workspace、sccache 在 HOME」。维护手法:该 Job 上 `POST job//config.xml` 会返回 500,节点侧同接口正常;因此改 Job 描述用脚本接口原地更新(保留构建历史),不要为了改描述删 Job 重建。 -该 Job 的 Rust 编译走 sccache 对象缓存,与 `Genarrative-Api-Build`、`Genarrative-Stdb-Module-Build` 一致:`RUSTC_WRAPPER=sccache`、`CARGO_INCREMENTAL=0`、`SCCACHE_CACHE_SIZE=20G`。`SCCACHE_DIR` 默认落在稳定缓存根 `~/caches/genarrative-jenkins/agc-macos/sccache`,可用 `GENARRATIVE_AGC_MACOS_CACHE_ROOT` 覆盖;缓存根固定在 HOME 下而不是 `WORKSPACE` 内,避免 workspace 重建后无改动也触发近似冷构建。节点是 8 逻辑核(4P+4E)的共用机器,`CARGO_BUILD_JOBS` 因此做成 Job 参数(默认 `8`,吃满节点)。该值不只是并行 crate 数——cargo 会把它作为 jobserver 令牌上限传给 rustc,主 crate 的 codegen 也受它限制,因此写小会直接拖长整条构建;节点只有 24 GB 内存且是日常办公机,若构建期间出现明显换页再临时调低。首次构建 sccache 必然全未命中(实测 0 命中 / 407 未命中),此后的构建才逐步吃到缓存。节点未安装 sccache 时管线打印提示并回退到真实 `rustc`,不阻断构建。universal 双架构各自独立编译,依赖 crate 的复用收益约为单架构的两倍;命中情况由构建末尾的 `sccache --show-stats` 输出,供判断是否需要预热或调整缓存上限。 +该 Job 的 Rust 编译走 sccache 对象缓存,与 `Genarrative-Api-Build`、`Genarrative-Stdb-Module-Build` 一致:`RUSTC_WRAPPER=sccache`、`CARGO_INCREMENTAL=0`、`SCCACHE_CACHE_SIZE=20G`。`SCCACHE_DIR` 默认落在稳定缓存根 `~/caches/genarrative-jenkins/agc-macos/sccache`,可用 `GENARRATIVE_AGC_MACOS_CACHE_ROOT` 覆盖;缓存根固定在 HOME 下而不是 `WORKSPACE` 内,避免 workspace 重建后无改动也触发近似冷构建。节点是 8 逻辑核(4P+4E)的共用机器,`CARGO_BUILD_JOBS` 因此做成 Job 参数(默认 `8`,吃满节点)。该值不只是并行 crate 数——cargo 会把它作为 jobserver 令牌上限传给 rustc,主 crate 的 codegen 也受它限制,因此写小会直接拖长整条构建;节点只有 24 GB 内存且是日常办公机,若构建期间出现明显换页再临时调低。首次构建 sccache 必然全未命中(实测 0 命中 / 407 未命中),此后的构建才逐步吃到缓存。节点未安装 sccache 时管线打印提示并回退到真实 `rustc`,不阻断构建。单架构发布后依赖 crate 的复用更彻底(此前每次要编 aarch64 与 x86_64 两遍);命中情况由构建末尾的 `sccache --show-stats` 输出,供判断是否需要预热或调整缓存上限。工作区 `target/` 目录跨构建保留(Checkout 只跑 `git clean -fd`,不动 ignored 目录),因此仓库内 path 依赖之外的 crate 只编一次。 首次端到端验证(2026-09-20,build #3):`SUCCESS`,43 分钟。产出 `陶泥儿_0.1.68_universal.dmg`、`陶泥儿.app.tar.gz` 与 `.sig`、`latest.json`、`build-manifest.json`;两个架构的隔离 smoke(arm64 / Rosetta x86_64)通过,更新包用产物内烘焙公钥复核通过(`alg=ED`,`keyId=cb883447e3e87c4e`),dry-run 只打印 4 个上传对象、未写 OSS。构建清单实测记录 `appleSigned=false`、`appleSignatureKind=adhoc`、`notarized=false`。首次实跑暴露并修掉两个入口缺陷:传 `--no-sign` 会连带跳过 updater 的 minisign 签名(产物无 `.sig`),以及复用 workspace 里残留的同名 DMG 让 `hdiutil create` 直接失败、残留旧 `.sig` 还可能让验签误通过——入口现在会在构建前删除本次将写出的确切产物并要求 `hdiutil -ov`。 diff --git a/jenkins/Jenkinsfile.ai-game-creator-shell-macos-build b/jenkins/Jenkinsfile.ai-game-creator-shell-macos-build index 91bcfdc97..6e57007b8 100644 --- a/jenkins/Jenkinsfile.ai-game-creator-shell-macos-build +++ b/jenkins/Jenkinsfile.ai-game-creator-shell-macos-build @@ -10,7 +10,7 @@ pipeline { timestamps() } parameters { - string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '必须含 universal 双架构依赖实现的受信任分支') + string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '必须含 macOS 单架构(arm64)发布实现的受信任分支') string(name: 'COMMIT_HASH', defaultValue: '', description: '可选,固定属于源码分支的提交;不递增应用版本') string(name: 'AGC_UPDATE_CHANNEL', defaultValue: 'dev', description: 'AGC 发布渠道(基础名,不含系统):dev、release 或自定义小写名称;写入的分区固定为 -mac') string(name: 'AGC_RELEASE_VERSION', defaultValue: '', description: '可选三段版本号;留空则按 -mac 分区清单高水位递增 patch。首次发布建议显式指定,避免版本链回退') @@ -26,7 +26,7 @@ pipeline { AGC_OSS_BUCKET = 'agc-dev' AGC_OSS_ENDPOINT = 'oss-rg-china-mainland.aliyuncs.com' // 与 Linux 生产管线(api-build / stdb-module-build)对齐:Rust 编译走 sccache 对象缓存。 - // 本 Job 是 universal 双架构(aarch64 + x86_64)各自独立编译,缓存收益约为单架构的两倍。 + // 本 Job 只编译 aarch64-apple-darwin 单架构(Intel 未支持):sccache 与工作区 target 目录跨构建复用。 // 缓存根固定在 HOME 下的稳定目录,避免 WORKSPACE 重建导致近似冷构建;可用下面的变量覆盖。 GENARRATIVE_AGC_MACOS_CACHE_ROOT = 'caches/genarrative-jenkins/agc-macos' RUSTC_WRAPPER = 'sccache' @@ -143,7 +143,7 @@ pipeline { sh ''' set -eu export PATH="$HOME/.local/bin:$HOME/.cargo/bin:${AGC_EXTRA_PATH}" - echo "[agc-macos] 渠道=${AGC_UPDATE_CHANNEL} 分区=${AGC_UPDATE_CHANNEL}-mac 目标=universal-apple-darwin dry-run=${AGC_RELEASE_DRY_RUN}" + echo "[agc-macos] 渠道=${AGC_UPDATE_CHANNEL} 分区=${AGC_UPDATE_CHANNEL}-mac 目标=aarch64-apple-darwin(单架构,Intel 未支持)dry-run=${AGC_RELEASE_DRY_RUN}" ossutil_bin="${OSSUTIL_BIN:-ossutil}" if command -v "${ossutil_bin}" >/dev/null 2>&1; then "${ossutil_bin}" --version | head -1 @@ -194,8 +194,8 @@ pipeline { } success { echo params.AGC_RELEASE_DRY_RUN - ? 'macOS universal 渠道演练完成:已构建、验签并生成清单,未写入 OSS;Apple 签名与公证暂缺。' - : "macOS universal 更新包、签名、首装包与 ${params.AGC_UPDATE_CHANNEL}-mac 分区清单已上传 OSS;Apple 签名与公证暂缺,首装需手动放行。" + ? 'macOS arm64 渠道演练完成:已构建、验签并生成清单,未写入 OSS;Apple 签名与公证暂缺。' + : "macOS arm64 更新包、签名、首装包与 ${params.AGC_UPDATE_CHANNEL}-mac 分区清单已上传 OSS(清单只登记 darwin-aarch64);Apple 签名与公证暂缺,首装需手动放行。" } } } -- 2.52.0 From 613d62fdb2613d47f7f636de630971b74905ea27 Mon Sep 17 00:00:00 2001 From: suzmii Date: Mon, 21 Sep 2026 12:35:36 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E9=87=8C=E7=A8=8B?= =?UTF-8?q?=E7=A2=91=E8=AE=A1=E5=88=92=EF=BC=9AmacOS=20=E6=B8=85=E5=8D=95?= =?UTF-8?q?=E5=8F=AA=E7=99=BB=E8=AE=B0=20darwin-aarch64=20-=20docs/project?= =?UTF-8?q?-memory/plans=20=E7=9A=84=E6=B8=A0=E9=81=93=E5=8C=96=E9=87=8C?= =?UTF-8?q?=E7=A8=8B=E7=A2=91=E4=BB=8D=E5=86=99=E7=9D=80=20dev-mac=20unive?= =?UTF-8?q?rsal=20=E5=90=8C=E6=97=B6=E5=86=99=E4=B8=A4=E4=B8=AA=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E9=94=AE=EF=BC=8C=E4=B8=8E=202026-09-21=20=E7=9A=84?= =?UTF-8?q?=E5=8D=95=E6=9E=B6=E6=9E=84=E5=86=B3=E7=AD=96=E5=86=B2=E7=AA=81?= =?UTF-8?q?=EF=BC=8C=E6=8C=89=E5=BD=93=E5=89=8D=E5=8F=A3=E5=BE=84=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=B9=B6=E6=8C=87=E5=90=91=E5=86=B3=E7=AD=96=E8=AE=B0?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/project-memory/plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md b/docs/project-memory/plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md index 5191b0129..3498047bd 100644 --- a/docs/project-memory/plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md +++ b/docs/project-memory/plans/【里程碑】AGC更新发布管线渠道化-2026-09-17.md @@ -13,7 +13,7 @@ ## 范围 -- 构建期按渠道生成清单:版本按渠道独立递增,清单包含该渠道平台的下载地址与签名;`dev-mac` 的 universal 包按同一地址与签名同时写入 `darwin-aarch64` 与 `darwin-x86_64`。 +- 构建期按渠道生成清单:版本按渠道独立递增,清单包含该渠道平台的下载地址与签名;macOS 自 2026-09-21 起只出 arm64 单架构,`dev-mac` 只登记 `darwin-aarch64`(原 universal 双键口径已作废:随包 Node 只有单架构官方发行版,需先实现双架构运行时才能恢复 Intel,见 `docs/project-memory/shared-memory/decision-log.md`)。 - 构建期生成更新产物签名,并在缺少签名私钥或私钥不可用时失败关闭。 - 渠道参数与目标平台绑定校验:Windows 目标只能发布 `dev-win`,macOS 目标只能发布 `dev-mac`;未显式指定时按目标平台取默认渠道。 - 上传按渠道落位:安装包与签名进版本目录,清单覆盖写渠道路径的 latest 指针。 -- 2.52.0