From c6381f68e1f31a96fa684f0223cfbafd9f4f4e67 Mon Sep 17 00:00:00 2001 From: Linghong Date: Wed, 30 Sep 2026 18:11:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAGC=E5=A3=B3CI=E7=BC=BA?= =?UTF-8?q?=E5=B0=91Claude=20SDK=E4=BE=9D=E8=B5=96=E5=AE=89=E8=A3=85=20(#5?= =?UTF-8?q?58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为两条Rust测试lane及smoke任务补齐编译前npm安装 为Rust缓存预热补齐Claude SDK依赖安装 更新CI安装顺序验证及开发运维文档和共享记忆 Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/558 Co-authored-by: Linghong Co-committed-by: Linghong --- .gitea/workflows/project-ci.yml | 15 ++++++++--- .../tests/prompt_source_boundaries.rs | 2 +- .../shared-memory/development-workflow.md | 2 +- ...¹案】AI游戏创作智能体App实施计划-2026-06-24.md | 2 ++ ...发运维】本地开发验证与生产运维-2026-05-15.md | 8 +++--- scripts/build-gitea-rust-cache.sh | 2 ++ scripts/project-ci-workflow.test.ts | 25 +++++++++++++------ 7 files changed, 40 insertions(+), 16 deletions(-) diff --git a/.gitea/workflows/project-ci.yml b/.gitea/workflows/project-ci.yml index 8338196cf..a5694037f 100644 --- a/.gitea/workflows/project-ci.yml +++ b/.gitea/workflows/project-ci.yml @@ -47,8 +47,8 @@ env: # 各自顺序运行两片,让一次 job 只做一次依赖预热,同时避免同一容器内多进程并行争抢 # 共享 HOME、target 与固定临时路径。每个分片调用仍会自校验「片并集等于全集且互斥」。 jobs: - # AGC 壳自身的 Rust bin 单测由两条 lane 承载,每条 lane 顺序运行两片;这里不装 npm - # 依赖:壳 Rust 门禁只用 cargo 与 node 内建模块,也只需要 AGC 壳自己那份锁定依赖。 + # AGC 壳自身的 Rust bin 单测由两条 lane 承载,每条 lane 顺序运行两片; + # build.rs 会准备 Claude Agent SDK sidecar,因此编译前必须安装根 workspace npm 依赖。 ai-game-creator-shell-rust-lane-1: name: AI game creator shell Rust lane 1/2 runs-on: genarrative-ci @@ -62,6 +62,9 @@ jobs: - name: Validate preinstalled CI job image and sandbox run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh + - name: Install npm dependencies + run: bash scripts/ci-npm-ci-with-retry.sh + - name: Prepare AI game creator shell Rust dependencies shell: bash run: | @@ -118,6 +121,9 @@ jobs: - name: Validate preinstalled CI job image and sandbox run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh + - name: Install npm dependencies + run: bash scripts/ci-npm-ci-with-retry.sh + - name: Prepare AI game creator shell Rust dependencies shell: bash run: | @@ -154,7 +160,7 @@ jobs: GENARRATIVE_GITEA_TOKEN: ${{ github.token }} run: python3 scripts/export-gitea-rust-cache.py - # agent-run smoke 会 spawn `cargo run`(走壳自己的 manifest),同样不装 npm 依赖, + # agent-run smoke 会 spawn `cargo run`(走壳自己的 manifest),同样需要 npm 随包依赖, # 单独一个 job,免得把已经压到 4 分钟级的片 job 拖长。 ai-game-creator-shell-rust-smoke: name: AI game creator shell Rust smoke @@ -169,6 +175,9 @@ jobs: - name: Validate preinstalled CI job image and sandbox run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh + - name: Install npm dependencies + run: bash scripts/ci-npm-ci-with-retry.sh + - name: Prepare AI game creator shell Rust dependencies shell: bash run: | diff --git a/apps/ai-game-creator-shell/src-tauri/tests/prompt_source_boundaries.rs b/apps/ai-game-creator-shell/src-tauri/tests/prompt_source_boundaries.rs index 453b77e00..5ed98b980 100644 --- a/apps/ai-game-creator-shell/src-tauri/tests/prompt_source_boundaries.rs +++ b/apps/ai-game-creator-shell/src-tauri/tests/prompt_source_boundaries.rs @@ -258,7 +258,7 @@ fn prompt_entry_points_load_their_prose_from_external_files() { ), ( "src/agent/direct_codex_user_item/wire.rs", - &["render_ui_design_code_context"], + &["ui_design_code_context_for"], ), ( "src/commands.rs", diff --git a/docs/project-memory/shared-memory/development-workflow.md b/docs/project-memory/shared-memory/development-workflow.md index 6b9ba3efb..29b6c172d 100644 --- a/docs/project-memory/shared-memory/development-workflow.md +++ b/docs/project-memory/shared-memory/development-workflow.md @@ -114,4 +114,4 @@ Gitea 缓存部署必须区分网络:runner 的 RPC 走 `gitea-runner-fetch-ga AGC Rust 两条 lane、crates、smoke、Backend 和桌面壳测试使用镜像内可信 sccache 对象快照;Native shell release step 显式清空双 wrapper,前端/repository checks 不启用。仅首次人工 bootstrap 时,维护者通过 `scripts/build-gitea-rust-cache.sh` 从远端 master 在限额、无宿主挂载的临时容器中按实际 cwd/profile/目标预热全部测试组,仅编译、不执行测试/应用;后端 workspace 与 spacetime-module 保持独立,AGC 的三个 cwd 入口之间清理预热 target,防止 fresh 判断漏产缓存键。最终镜像只追加 sccache、对象和来源元数据,不包含源码或 target。容量上限 4 GiB,不替代宿主旧镜像/归档清理。PR 只写当前容器层、不回传,不开放 Docker API/发布权限;继续禁用 incremental。`ci-rust-cache.sh` 在快照缺失、工具链不符或 wrapper 探测失败时直接编译,并隔离远程缓存配置和 daemon。分片日志记录编译耗时,收尾输出命中统计;两个 lane 的测试和前置检查不同,耗时差不是严格 A/B。线上存在活跃 CI 时不得重启 runner 或切换标签;全组启用前须刷新完整快照并逐组验证,详见开发运维文档。 -`.gitea/workflows/project-ci.yml` 的客户端门禁拆成 lane 与功能 job,每个 job 只预热自己会构建的那几份依赖:`AI game creator shell Rust lane 1/2`、`lane 2/2` 各自预取一次 AGC 壳 manifest,并顺序运行两片 Rust bin 单测;`AI game creator shell Rust smoke` 同样只预取 AGC 壳 manifest(`agent-run` smoke 会用 `src-tauri/Cargo.toml` spawn `cargo run`),`AI game creator shell Rust crates` 预取 `server-rs/Cargo.toml` 与独立 crate,`Native shell tests` 预取桌面壳与 AGC 壳 manifest,`AI game creator shell web tests` 不触碰 Cargo,不预热。两条 Rust lane、smoke job 与 crates job 只用 cargo 与 node 内建模块,因此不执行 `npm ci`。两个被 `server-rs/Cargo.toml` 排除、且没有提交 `Cargo.lock` 的独立 crate(`agent-runtime-core`、`agent-runtime-orchestration`)只能在 `AI game creator shell Rust crates` 里用不带锁标志的 fetch。AGC 壳的 bin target 单测(约 2466 条)由 `apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs` 编译后按 `--list` 名单分 4 片:每次分片调用用 `--shard-index=` 只跑自己那片,片内保持 `--test-threads=1` 并使用独立 `TMPDIR`;两条 lane 之间并发,lane 内顺序运行两片,避免重复依赖预热和同一容器内多进程争抢。不要改回「一个 job 内多进程并行这几片」——同一容器里它们会争抢共享 `HOME`、target 目录与固定临时路径,实测比整套串行还慢。每个分片调用都会自校验「片并集等于全集且互斥」,因此改分片规则不会静默漏跑。Backend host workspace tests 使用 `cargo test --locked --workspace --exclude spacetime-module --no-fail-fast`,避免 `spacetime-module` 的 `spacetime-types` feature 统一污染普通领域 crate 的 host 测试;随后单独执行 `cargo test --locked -p spacetime-module --no-fail-fast`,由 `spacetime-module/src/active.rs` 在 host 测试构建期间提供仅测试期的 SpacetimeDB ABI 链接支持,使该 crate 的纯单元测试也纳入 Backend 门禁。`spacetime-module` 的 reducer / procedure 运行时行为仍必须通过真实 SpacetimeDB runtime/integration harness 验证,host 链接支持不得被当作运行时替身。Backend 另外执行 `cargo check --locked -p spacetime-module` 验证模块源码。AGC 壳检查还会运行 `platform-llm` 与 `shared-contracts` 的 server-rs workspace 测试,这些命令以及 AGC 壳测试必须带 `--locked`,避免在测试阶段重新解析 registry index;锁文件发生变化时应先更新受信任 CI 镜像缓存,再重跑门禁。 +`.gitea/workflows/project-ci.yml` 的客户端门禁拆成 lane 与功能 job,每个 job 只预热自己会构建的那几份依赖:`AI game creator shell Rust lane 1/2`、`lane 2/2` 各自预取一次 AGC 壳 manifest,并顺序运行两片 Rust bin 单测;`AI game creator shell Rust smoke` 同样只预取 AGC 壳 manifest(`agent-run` smoke 会用 `src-tauri/Cargo.toml` spawn `cargo run`),`AI game creator shell Rust crates` 预取 `server-rs/Cargo.toml` 与独立 crate,`Native shell tests` 预取桌面壳与 AGC 壳 manifest,`AI game creator shell web tests` 不触碰 Cargo,不预热。两条 Rust lane 与 smoke job 必须在编译前通过 `scripts/ci-npm-ci-with-retry.sh` 执行根 `npm ci`:AGC 壳的 `build.rs` 会从 `node_modules` 准备 Claude Agent SDK 与目标平台原生运行时,镜像中的 npm 下载缓存不能替代安装。人工 `scripts/build-gitea-rust-cache.sh` bootstrap 同样在首次编译 AGC 壳前安装 npm 依赖;只有不构建壳的 crates job 继续省略 npm 安装。两个被 `server-rs/Cargo.toml` 排除、且没有提交 `Cargo.lock` 的独立 crate(`agent-runtime-core`、`agent-runtime-orchestration`)只能在 `AI game creator shell Rust crates` 里用不带锁标志的 fetch。AGC 壳的 bin target 单测(约 2466 条)由 `apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs` 编译后按 `--list` 名单分 4 片:每次分片调用用 `--shard-index=` 只跑自己那片,片内保持 `--test-threads=1` 并使用独立 `TMPDIR`;两条 lane 之间并发,lane 内顺序运行两片,避免重复依赖预热和同一容器内多进程争抢。不要改回「一个 job 内多进程并行这几片」——同一容器里它们会争抢共享 `HOME`、target 目录与固定临时路径,实测比整套串行还慢。每个分片调用都会自校验「片并集等于全集且互斥」,因此改分片规则不会静默漏跑。Backend host workspace tests 使用 `cargo test --locked --workspace --exclude spacetime-module --no-fail-fast`,避免 `spacetime-module` 的 `spacetime-types` feature 统一污染普通领域 crate 的 host 测试;随后单独执行 `cargo test --locked -p spacetime-module --no-fail-fast`,由 `spacetime-module/src/active.rs` 在 host 测试构建期间提供仅测试期的 SpacetimeDB ABI 链接支持,使该 crate 的纯单元测试也纳入 Backend 门禁。`spacetime-module` 的 reducer / procedure 运行时行为仍必须通过真实 SpacetimeDB runtime/integration harness 验证,host 链接支持不得被当作运行时替身。Backend 另外执行 `cargo check --locked -p spacetime-module` 验证模块源码。AGC 壳检查还会运行 `platform-llm` 与 `shared-contracts` 的 server-rs workspace 测试,这些命令以及 AGC 壳测试必须带 `--locked`,避免在测试阶段重新解析 registry index;锁文件发生变化时应先更新受信任 CI 镜像缓存,再重跑门禁。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 5315d44f8..4f86ce479 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -740,6 +740,8 @@ Prompt 静态门禁必须断言上述 Bundle section 当前定义的权威语义 Agent 可见的系统指令、工具与参数说明、恢复指引和上下文模板统一由外置提示词文件维护。AGC 沿用 `prompts/runtime/manifest.json`:已有 composition/section 保持原有组合关系,独立调用的文本按职责登记在 `textCatalogs`,目录为 `prompts/runtime/texts/`,每份 JSON 是稳定文本键到正文的映射。构建期校验目录、文件、重复键和空正文,并生成可供 `format!` 使用的编译期文本宏;变量填充沿用 Rust 格式语法。Runtime 状态、用户内容、schema 类型与枚举、权限和校验继续由代码生成。服务端 Agent 的独立 crate 使用各自 `prompts/` 中的编译期文本文件。迁移以当前组装结果和工具 schema 等价为验收依据,源码门禁检查各提示词入口的内联正文与外置引用。 +`tests/prompt_source_boundaries.rs` 的入口清单随生产函数迁移同步更新。UI 设计文档代码上下文在入队冻结时由 `src/agent/direct_codex_user_item/wire.rs::ui_design_code_context_for` 生成,门禁检查该函数继续使用外置的 `projectContext.uiDesign.codeContext` 与 `projectContext.uiDesign.generationErrorContext`;旧 `render_ui_design_code_context` 已随冻结与纯投影拆分删除,不保留兼容入口。 + 2026-07-12 起,通用开发能力的 Runtime V1.1 增量以 [`【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`](<./【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md>) 为编码级事实源。它补充仓库启动上下文、同一发布二进制独立 Runner、受限本地预览浏览器验证、动态隔离子 Agent 和真实 Provider 全链路验收;本文件中“进程内 tokio task”“首轮不预加载项目内容”和“不创建动态执行实例”的旧口径由 V1.1 明确替代,未涉及能力继续沿用本文件。 同一文档的“V1.2 对标 Codex CLI 增量”继续作为受控命令与推理档位的事实源。对一次性 `command.exec` 而言,只接受 Runtime 白名单内的固定 `program` 和逐项 `args` argv,默认 `confirm`,可执行文件解析为项目外绝对路径且子进程只使用安全 PATH;不解析 shell 字符串,不提供管道、重定向、PTY 或后台进程。这里对 PTY 和后台进程的排除仅适用于 `command.exec`,不能用来否定 V1.10 的独立持久进程工具,也不能把 `command.exec` 自身改成长驻入口。`command.exec` 的 action、stdout / stderr、退出码、超时与源码指纹结果统一进入现有 `action / observation`、project revision、verification gate 和 `needs-reconciliation` 链路;只有明确验证型命令且退出码、源码指纹、命令日志、manifest 与 Agent DB 审计全通过才签发 passed gate,Git / rg / cargo metadata / 普通 npm run 只作诊断。首版只请求终止受控进程组,安全等级与 `project.verify` 相同,不宣称已具备完整 OS sandbox 或 detached-process 隔离。 diff --git a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md index 50ceb846d..7946645c1 100644 --- a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md +++ b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md @@ -329,8 +329,8 @@ Linux process-session 的 owner SIGKILL 用例必须在启动 owner 后立即建 - `Backend tests`:先对 `server-rs/Cargo.lock` 执行带 5 次整命令级有界重试的 `cargo fetch --locked`,再执行 `npm run check:server-rs-ddd`、`cargo test --locked --workspace --exclude spacetime-module --no-fail-fast`、`cargo test --locked -p spacetime-module --no-fail-fast`、`api-server --all-targets` 编译和 `cargo check --locked -p spacetime-module`;普通 workspace host 测试排除 `spacetime-module` 以避免其 `spacetime-types` feature 统一污染领域 crate,模块自身的纯单元测试通过独立 package test 纳入门禁。`spacetime-module` 的 reducer / procedure 运行时行为仍必须通过真实 SpacetimeDB runtime/integration harness 验证,不能把 host 链接支持当作运行时替身。依赖准备必须位于会触发 Cargo build 的 DDD / 产物边界门禁之前,避免锁新增依赖未命中镜像缓存时绕过既有下载重试。runner 安装 `ffmpeg`,避免视频抽帧测试因工具缺失提前返回。依赖真实服务或密钥的测试必须显式 `ignored`,不能让普通 PR job访问现场环境。 - `Native shell tests`:按唯一根 workspace lockfile 安装全部 App 依赖后,用 `npm run check:native-shells:contract`、`npm run check:native-shells:shells` 和 `npm run check:native-shells:release` 分别执行静态契约、H5 / 微信 / Expo / Tauri 桌面壳运行时门禁,以及依赖发布产物的构建 smoke,最后确认桌面壳与 AI 游戏创作壳的 `Cargo.lock` 都没有被构建过程改写。 - `AI game creator shell web tests`:执行 `npm run check:native-shells:agc-web`(即 `npm run ai-game-creator-shell:check:web`:AGC 壳 typecheck 与壳内测试)。该分组不触碰 Cargo,因此不预热 Rust 依赖。 -- `AI game creator shell Rust lane 1/2`、`lane 2/2`:两条 lane 各自只预热一次 AGC 壳自己的锁定依赖(`apps/ai-game-creator-shell/src-tauri/Cargo.lock` 的 path 依赖已含 `platform-llm`、`platform-agent`、`agent-runtime-core` 与 `shared-contracts`),然后顺序执行两次 `npm run check:native-shells:agc-rust-shard-`(每次分片运行器使用对应的 `--shard-index=`):AGC 壳 bin target 的 2466 条 Rust 单测按 `--list` 名单排序后切 4 片,片内保持 `--test-threads=1`、各片独立 `TMPDIR`,两条 lane 之间靠 job 级并发摊开;每次分片调用都会自校验「片并集等于全集且互斥」。**不要**改回「一个 job 里多进程并行这几片」:同一容器内它们共享 `HOME`、target 与固定临时路径,实测(run 2102)比整套串行还慢。这些 lane 只用 cargo 与 node 内建模块,因此不装 npm 依赖。 -- `AI game creator shell Rust smoke`:同样只预热 AGC 壳那份锁定依赖,执行 `npm run check:native-shells:agc-rust-smoke`(即 `npm run ai-game-creator-shell:agent-run:smoke`)。smoke 会用 `src-tauri/Cargo.toml` spawn `cargo run`,单独一个 job 以免把已经压到分钟级的片 job 拖长;只用 cargo 与 node 内建模块(脚本只 import `node:*`),因此不装 npm 依赖。 +- `AI game creator shell Rust lane 1/2`、`lane 2/2`:两条 lane 各自只预热一次 AGC 壳自己的锁定依赖(`apps/ai-game-creator-shell/src-tauri/Cargo.lock` 的 path 依赖已含 `platform-llm`、`platform-agent`、`agent-runtime-core` 与 `shared-contracts`),然后顺序执行两次 `npm run check:native-shells:agc-rust-shard-`(每次分片运行器使用对应的 `--shard-index=`):AGC 壳 bin target 的 2466 条 Rust 单测按 `--list` 名单排序后切 4 片,片内保持 `--test-threads=1`、各片独立 `TMPDIR`,两条 lane 之间靠 job 级并发摊开;每次分片调用都会自校验「片并集等于全集且互斥」。**不要**改回「一个 job 里多进程并行这几片」:同一容器内它们共享 `HOME`、target 与固定临时路径,实测(run 2102)比整套串行还慢。这些 lane 在编译前通过 `scripts/ci-npm-ci-with-retry.sh` 执行根 `npm ci`,为 `build.rs` 准备 Claude Agent SDK 与 Linux 原生运行时。 +- `AI game creator shell Rust smoke`:同样只预热 AGC 壳那份锁定依赖,执行 `npm run check:native-shells:agc-rust-smoke`(即 `npm run ai-game-creator-shell:agent-run:smoke`)。smoke 会用 `src-tauri/Cargo.toml` spawn `cargo run`,单独一个 job 以免把已经压到分钟级的片 job 拖长;smoke 脚本自身只 import `node:*`,但壳的 `build.rs` 需要 Claude Agent SDK,因此同样在编译前执行根 `npm ci`。 - `AI game creator shell Rust crates`:预热 `server-rs/Cargo.toml` 与两个无锁独立 crate(`agent-runtime-core`、`agent-runtime-orchestration`)后执行 `npm run check:native-shells:agc-rust-crates`(即 `npm run ai-game-creator-shell:check:rust:crates`),覆盖 `agent-runtime-core`、`agent-runtime-orchestration`、`platform-llm` 与 `shared-contracts`。这四条命令用的是 server-rs workspace 与独立 crate 的 manifest,属另一套依赖图,因此单独一个 job,也只跑 cargo、不装 npm 依赖。 九个 job 合起来覆盖根 `npm run check`,并补齐根检查没有包含的 BgFilter worker smoke harness、无密钥生产巡检 / 发布 / 部署行为 fixture、server-rs DDD、正式 workspace Rust 测试与现役后端编译门禁。客户端门禁的拆分口径是 `scripts/check-native-shells.mjs` 的 `--groups=`:十个分组(`contract`、`shells`、`agc-web`、`agc-rust-crates`、`agc-rust-shard-1` ~ `agc-rust-shard-4`、`agc-rust-smoke`、`release`)各自对应一个 `check:native-shells:` 根脚本,并在 workflow 的某个 lane/job 里被恰好调用一次;每个 Rust lane 顺序调用两组,不带 `--groups=` 时脚本仍然串行跑全部分组,本地语义不变。`scripts/project-ci-workflow.test.ts` 会同时校验分组清单、根脚本内容、lane/job 覆盖与分片运行器,新增分组必须三处同步。普通 PR CI 不注入业务密钥,不启动真实 API、SpacetimeDB、OSS、支付、图片生成或生产 live smoke;需要现场环境、可变外部状态、Docker 编排或发布凭据的 `check:*` 继续按对应专题和 Jenkins 发布流程执行,不能遍历所有同名前缀脚本冒充 PR 门禁。 @@ -354,7 +354,7 @@ bash scripts/gitea-ci-job-image.sh load-runner 执行账号只要有权访问宿主 Docker API 并管理 runner 容器即可,不强制使用 root;无该权限时由 runner 运维人员执行。更新顺序必须是 `build/verify -> export 仓库外镜像归档与 SHA-256 sidecar -> load-runner -> 确认无活跃 job -> 备份当前 config -> 增加或替换 label -> docker restart --timeout 660 gitea-runner`。`--timeout 660` 只是停止宽限,不是 drain API;rootless DinD supervisor 可能同时停止内层 dockerd,因此重启前必须确认 Gitea 没有 `in_progress` run 且内层 `docker ps` 为空。config 和镜像归档只保存到仓库外受控位置,不在文档、仓库或日志中记录注册信息。重启后先重跑真实 PR 的九个 job,复核隔离边界并确认全部通过,再清理旧镜像。回滚时先把 workflow 的 `runs-on` 改回 `ubuntu-latest`,再恢复 config 备份并重启 runner。 -九个 job 先运行镜像内 `genarrative-gitea-checkout`,再以 `GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1` 执行 `scripts/check-gitea-ci-job-image.sh`,校验 Node 与 npm 固定版本、仓库 Rust toolchain、受信任 PATH、四份缓存锁命中状态、原生命令、pkg-config 依赖、完整 bwrap sandbox 和 Chrome headless。运行时发现锁不匹配时必须输出对应 `*_cache_lock=partial` 和 Actions warning,提示可信分支落地后刷新镜像,不能把陈旧缓存误报为闭合。`RUSTUP_AUTO_INSTALL=0`,因此仓库 `rust-toolchain.toml` 变更必须先更新镜像,不能让 job 现场下载。需要 `node_modules` 的 job 仍各自独立运行一次根 `npm ci`,以唯一 workspace lock 验证 PR 的全部 App 依赖;两条 AGC 壳 Rust lane、`AI game creator shell Rust smoke` 与 `AI game creator shell Rust crates` 是纯 cargo 门禁(只用 cargo 与 node 内建模块),显式不装 npm 依赖,这也由 `scripts/project-ci-workflow.test.ts` 钉住。`npm ci` 统一通过 `scripts/ci-npm-ci-with-retry.sh` 做最多 3 次整命令级有界重试,同时保留 `NPM_CONFIG_PREFER_OFFLINE=true` 和 npm 自身 10 次 fetch retry。命中镜像 cache 时只做干净解包,lock 变化时允许补齐差量。不在镜像内烘入 `node_modules`,也不挂载跨 PR 可写缓存。任何 job 的 sandbox canary 失败都必须停止,不允许跳过。Cargo 通过受控 proxy 下载 lock 差量时继续关闭 HTTP multiplexing,并设置 `CARGO_NET_RETRY=10`。 +九个 job 先运行镜像内 `genarrative-gitea-checkout`,再以 `GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1` 执行 `scripts/check-gitea-ci-job-image.sh`,校验 Node 与 npm 固定版本、仓库 Rust toolchain、受信任 PATH、四份缓存锁命中状态、原生命令、pkg-config 依赖、完整 bwrap sandbox 和 Chrome headless。运行时发现锁不匹配时必须输出对应 `*_cache_lock=partial` 和 Actions warning,提示可信分支落地后刷新镜像,不能把陈旧缓存误报为闭合。`RUSTUP_AUTO_INSTALL=0`,因此仓库 `rust-toolchain.toml` 变更必须先更新镜像,不能让 job 现场下载。需要 `node_modules` 的 job 仍各自独立运行一次根 `npm ci`,以唯一 workspace lock 验证 PR 的全部 App 依赖;两条 AGC 壳 Rust lane 与 `AI game creator shell Rust smoke` 也必须在编译前安装 npm 依赖,因为 `build.rs` 会准备随包 Claude Agent SDK 与目标平台原生运行时;只有不构建壳的 `AI game creator shell Rust crates` 省略 npm 安装。安装覆盖与编译前顺序由 `scripts/project-ci-workflow.test.ts` 验证。`npm ci` 统一通过 `scripts/ci-npm-ci-with-retry.sh` 做最多 3 次整命令级有界重试,同时保留 `NPM_CONFIG_PREFER_OFFLINE=true` 和 npm 自身 10 次 fetch retry。命中镜像 cache 时只做干净解包,lock 变化时允许补齐差量。不在镜像内烘入 `node_modules`,也不挂载跨 PR 可写缓存。任何 job 的 sandbox canary 失败都必须停止,不允许跳过。Cargo 通过受控 proxy 下载 lock 差量时继续关闭 HTTP multiplexing,并设置 `CARGO_NET_RETRY=10`。 站点 stack 仍由宿主受控目录管理,`.env`、runner 注册文件和数据库凭据不进入仓库。Compose 必须在 helper/container 内把该目录挂到与宿主相同的绝对路径再执行;挂载到不同路径会让相对 bind source 被 Docker daemon 解析到错误的宿主目录并启动空数据。升级或 runner 迁移前先停止 Gitea 写入,并把 Gitea 冷快照、数据库导出、compose/env 与 runner config/.runner 保存到仓库外受控备份位置。备份文件、绝对宿主配置和注册 token 不得提交 Git,也不在共享文档中记录具体路径或注册内容。 @@ -368,7 +368,7 @@ master 日常交付必须禁止直接 push,只允许经 PR 在最近一次 Pro AGC Rust 两条 lane、crates、agent-run smoke、Backend 和 Native shell 的桌面壳测试均启用 sccache。Native shell 的 release build smoke 显式清空两个 wrapper,保持发布构建原有 profile/features 与资源 staging;前端和 repository checks 不启用对象缓存。继续设置 `CARGO_INCREMENTAL=0`,不共享 target、不恢复 Actions 可写缓存。可信快照通过已有固定 Image ID 分发:镜像只增加固定版本的 sccache、编译对象和来源元数据,不包含源码、target 或凭据;容器写时复制层承接本 job 的新增对象,job 删除后丢弃,PR 没有 Docker API 或快照发布权限。该权限边界由 runner 基础设施保证,不能仅用 workflow 的分支条件替代。 -人工 bootstrap 在没有可消费快照时使用 `bash scripts/build-gitea-rust-cache.sh <已验证基础镜像> <候选镜像tag> [完整master-SHA]`;自动维护不调用它。bootstrap 固定 master 归档,在无宿主挂载、无凭据、有资源限额的容器中只编译预热,不执行测试/应用,Cargo features 和工作目录保持实际 CI 口径。日常更新由 master CI 导出新 key,命中继承对象只传使用时间;PR 不导出、不扫描。宿主验证来源、大小和哈希,不从上传产物执行程序,只从可信来源镜像复制固定 sccache。工具链或基础镜像输入变化时重建无对象缓存基础镜像,缓存工具链必须匹配。公共快照不接受 PR,不复用 Jenkins 发布缓存。 +人工 bootstrap 在没有可消费快照时使用 `bash scripts/build-gitea-rust-cache.sh <已验证基础镜像> <候选镜像tag> [完整master-SHA]`;自动维护不调用它。bootstrap 固定 master 归档,在无宿主挂载、无凭据、有资源限额的容器中先通过 `scripts/ci-npm-ci-with-retry.sh` 安装根 npm 依赖,再编译预热 AGC 壳,以满足 Claude Agent SDK 随包资源的构建要求;不执行测试/应用,Cargo features 和工作目录保持实际 CI 口径。日常更新由 master CI 导出新 key,命中继承对象只传使用时间;PR 不导出、不扫描。宿主验证来源、大小和哈希,不从上传产物执行程序,只从可信来源镜像复制固定 sccache。工具链或基础镜像输入变化时重建无对象缓存基础镜像,缓存工具链必须匹配。公共快照不接受 PR,不复用 Jenkins 发布缓存。 各 Rust job 在编译前执行 `scripts/ci-rust-cache.sh prepare`:检查快照与 rustc 身份,隔离 sccache 配置和 daemon,限时探测真实 wrapper。旧镜像没有快照或探测失败时保留空 wrapper,输出 fallback 原因;缓存故障不得把真实编译/测试失败改成成功,也不允许重跑整个测试组掩盖失败。结束时 `report` 输出命中统计;分片日志单独记录 Cargo 编译耗时。对象缓存上限为 4 GiB,快照构建完成后输出实际体积;最终测试 bin 的链接仍须执行。全组开启时必须生成覆盖全部目标的新快照,不能把旧 AGC 单目标快照当作后端/桌面壳的预热验收。 diff --git a/scripts/build-gitea-rust-cache.sh b/scripts/build-gitea-rust-cache.sh index 1bd6a22aa..ea96b2bcb 100644 --- a/scripts/build-gitea-rust-cache.sh +++ b/scripts/build-gitea-rust-cache.sh @@ -107,6 +107,8 @@ done echo '[rust-cache] warming desktop tests and AGC prompt contracts' cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml --no-run +# AGC build.rs 从 node_modules 准备 Claude Agent SDK 与目标平台原生运行时。 +bash scripts/ci-npm-ci-with-retry.sh cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml \ --test runtime_prompt_bundle_build --test prompt_source_boundaries --no-run diff --git a/scripts/project-ci-workflow.test.ts b/scripts/project-ci-workflow.test.ts index 6f793634a..283f6a53a 100644 --- a/scripts/project-ci-workflow.test.ts +++ b/scripts/project-ci-workflow.test.ts @@ -67,14 +67,9 @@ const jobNames = [ 'ai-game-creator-shell-rust-crates', ] as const; -// 这几个 job 是纯 cargo 门禁:AGC 壳的 Rust 分片与 agent-run smoke 只用 cargo 与 -// node 内建模块(smoke 脚本只 import `node:*`),crate 级测试只跑 cargo 命令, -// 都不需要 node_modules。省掉这些 `npm ci`(各 1~3 分钟)是把客户端 Rust 关键路径 -// 压到 7 分钟以内的前提,因此这里显式允许它们不装 npm 依赖。 +// crate 级测试只跑共享 / 平台 / 编辑器插件的 Cargo manifest,不构建 AGC 壳。 +// 壳的 Rust 分片与 agent-run smoke 会执行 build.rs,需要 npm 安装的 Claude Agent SDK。 const jobsWithoutNpmInstall: readonly string[] = [ - 'ai-game-creator-shell-rust-lane-1', - 'ai-game-creator-shell-rust-lane-2', - 'ai-game-creator-shell-rust-smoke', 'ai-game-creator-shell-rust-crates', ]; @@ -199,6 +194,13 @@ describe('project CI workflow', () => { section.indexOf('Checkout full history from Gitea'), ); expect(prepare).toBeLessThan(section.indexOf(`- name: ${firstRustStep}`)); + if (!jobsWithoutNpmInstall.includes(job)) { + const npmInstall = section.indexOf( + 'bash scripts/ci-npm-ci-with-retry.sh', + ); + expect(npmInstall).toBeGreaterThanOrEqual(0); + expect(npmInstall).toBeLessThan(prepare); + } expect( stepSection(job, 'Report isolated Rust compilation cache'), ).toContain('if: always()'); @@ -246,6 +248,15 @@ describe('project CI workflow', () => { expect(warmCommands).toContain( 'cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml --no-run', ); + const npmInstall = warmCommands.indexOf( + 'bash scripts/ci-npm-ci-with-retry.sh', + ); + expect(npmInstall).toBeGreaterThanOrEqual(0); + expect(npmInstall).toBeLessThan( + warmCommands.indexOf( + 'cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml', + ), + ); }); it('runs for master pushes, pull requests, and manual dispatch only', () => {