按系统性审计修复 M3:指纹反判断、守卫死代码、fail-open 与卫生项
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m23s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m55s
Project CI / Native shell tests (pull_request) Failing after 1m26s
Project CI / Frontend tests (pull_request) Successful in 1m36s
Project CI / Backend tests (pull_request) Successful in 3m37s
Project CI / AI game creator shell web tests (pull_request) Failing after 34s
Project CI / Repository checks (pull_request) Successful in 1m49s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m37s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m19s

- P1-1:pluginSourceFingerprint 的判断取反(prepared 内容与交付态真正进入指纹);pluginTreeMatches 的「当前目标/feature 下不该存在却存在 → 重建」从死代码修活
- P1-7:package_layout 的 `sha256_file(x).ok() != ...` 改为 map_err 传播,两侧读失败不再被判为「相等」
- P3-2:copyNativePayloads 的 'plugins' 字面量改由声明 sourceDirectory 派生
- P3-5:payload 交付内容一致时跳过写入(命中缓存保持零写入、时间戳稳定)
- P3-7:门禁提示里的脚本名改为 agc:bundled-resources:sync
- P3-8:删除已无写入方的 resources/cocos-editor-bridge 占位目录
- P2-4:root 包装脚本补结尾 `--`,文档里的 prepare 命令可直接复制执行
- 验证:工具用例全绿、声明门禁通过
This commit is contained in:
2026-09-28 10:20:09 +08:00
parent 547fb0dbce
commit 3b4f9b2e10
6 changed files with 41 additions and 19 deletions
@@ -453,7 +453,7 @@ function renderGenerated(declaration) {
return `// @generated by apps/ai-game-creator-shell/scripts/check-package-layout.mjs
// 来源:build_support/package-layout.json。不要手工编辑本文件。
// 修改随包资源布局请编辑声明文件,然后运行
// npm run agc:package-layout:sync(在仓库根目录)
// npm run agc:bundled-resources:sync(在仓库根目录)
// 门禁会校验两者一致(npm run agc:typecheck 链内含 check-package-layout.mjs)。
pub const DECLARATION_SCHEMA: &str = ${rustString(EXPECTED_SCHEMA)};
@@ -615,7 +615,7 @@ function main() {
`随包资源声明与 Rust 常量不一致:`,
` 声明:${path.relative(process.cwd(), DECLARATION_PATH)}`,
` 生成:${path.relative(process.cwd(), GENERATED_PATH)}`,
'请运行:npm run agc:package-layout:sync',
'请运行:npm run agc:bundled-resources:sync',
].join('\n'),
);
process.exit(1);
@@ -660,8 +660,8 @@ function pluginSourceFingerprint(declaration, plugins, target, features) {
for (const plugin of plugins) {
for (const subdirectory of declaration.plugins.subdirectories) {
if (
subdirectoryAppliesToPlugin(subdirectory, plugin.name) ||
subdirectoryEnabled(subdirectory, target, features)
!subdirectoryAppliesToPlugin(subdirectory, plugin.name) ||
!subdirectoryEnabled(subdirectory, target, features)
) {
continue;
}
@@ -747,10 +747,7 @@ function pluginTreeMatches(
return false;
}
for (const subdirectory of declaration.plugins.subdirectories) {
if (
!subdirectoryAppliesToPlugin(subdirectory, plugin.name) ||
!subdirectoryEnabled(subdirectory, target, features)
) {
if (!subdirectoryAppliesToPlugin(subdirectory, plugin.name)) {
continue;
}
const stagedDirectory = path.join(pluginDestination, subdirectory.path);
@@ -763,10 +760,7 @@ function pluginTreeMatches(
}
const sourceRoot = path.join(plugin.root, subdirectory.path);
if (subdirectory.origin !== 'source') {
if (
existsSync(sourceRoot) &&
!existsSync(path.join(pluginDestination, subdirectory.path))
) {
if (existsSync(sourceRoot) && !existsSync(stagedDirectory)) {
return false;
}
continue;
@@ -1053,6 +1047,21 @@ function copyPreparedPayloads({
}
}
/// 内容一致时不重写(保住时间戳与「命中缓存零写入」口径)。
function copyFilePreservingModeIfChanged(source, destination) {
if (existsSync(destination)) {
const sourceInfo = statSync(source);
const destinationInfo = statSync(destination);
if (
sourceInfo.size === destinationInfo.size &&
Buffer.compare(readFileSync(source), readFileSync(destination)) === 0
) {
return;
}
}
copyFilePreservingMode(source, destination);
}
/// Cocos bridge 的 dll 既要进插件工作区(唯一真源),也要进随包目录。
function copyNativePayloads({
declaration,
@@ -1100,7 +1109,7 @@ function copyNativePayloads({
}
const destinationName =
payload.destinationFileName ?? payload.sourceFileName;
copyFilePreservingMode(
copyFilePreservingModeIfChanged(
source,
path.join(
repoRoot,
@@ -1110,7 +1119,7 @@ function copyNativePayloads({
destinationName,
),
);
copyFilePreservingMode(
copyFilePreservingModeIfChanged(
source,
path.join(
staging,
@@ -280,7 +280,15 @@ pub fn validate_staged_plugins(
if !staged_manifest.is_file() {
return Err(format!("随包插件缺少清单:{}", staged_manifest.display()));
}
if sha256_file(&source_manifest).ok() != sha256_file(&staged_manifest).ok() {
let source_manifest_digest = sha256_file(&source_manifest)
.map_err(|error| format!("读取插件清单失败 {}:{error}", source_manifest.display()))?;
let staged_manifest_digest = sha256_file(&staged_manifest).map_err(|error| {
format!(
"读取随包插件清单失败 {}:{error}",
staged_manifest.display()
)
})?;
if source_manifest_digest != staged_manifest_digest {
return Err(format!(
"随包插件清单与源码不一致:{}",
staged_manifest.display()
@@ -319,7 +327,13 @@ pub fn validate_staged_plugins(
if !staged_file.is_file() {
return Err(format!("随包插件缺少文件:{}", staged_file.display()));
}
if sha256_file(&source_file).ok() != sha256_file(&staged_file).ok() {
let source_digest = sha256_file(&source_file).map_err(|error| {
format!("读取插件文件失败 {}:{error}", source_file.display())
})?;
let staged_digest = sha256_file(&staged_file).map_err(|error| {
format!("读取随包插件文件失败 {}:{error}", staged_file.display())
})?;
if source_digest != staged_digest {
return Err(format!(
"随包插件文件与源码不一致:{}",
staged_file.display()
+2 -2
View File
@@ -170,8 +170,8 @@
"agc:skill-pack:sync": "npm --prefix apps/ai-game-creator-shell run skill-pack:sync",
"agc:bundled-resources:check": "npm --prefix apps/ai-game-creator-shell run bundled-resources:check",
"agc:bundled-resources:sync": "npm --prefix apps/ai-game-creator-shell run bundled-resources:sync",
"agc:bundled-resources:prepare": "npm --prefix apps/ai-game-creator-shell run bundled-resources:prepare",
"agc:bundled-resources:test": "npm --prefix apps/ai-game-creator-shell run bundled-resources:test",
"agc:bundled-resources:prepare": "npm --prefix apps/ai-game-creator-shell run bundled-resources:prepare --",
"agc:bundled-resources:test": "npm --prefix apps/ai-game-creator-shell run bundled-resources:test --",
"agc:plugins:test": "node --test plugins/agc-cocos-editor/src/entry.test.mjs plugins/agc-unity-editor/src/entry.test.mjs plugins/agc-godot-editor/src/entry.test.mjs",
"agc:plugins:native-test": "cargo test --manifest-path plugins/agc-cocos-editor/native/cocos-editor-bridge/Cargo.toml && cargo test --locked --manifest-path plugins/agc-unity-editor/native/unity-editor-bridge/Cargo.toml && cargo test --locked --manifest-path plugins/agc-godot-editor/native/godot-editor-bridge/Cargo.toml",
"agc:plugins:check": "npm run agc:plugins:test && npm run agc:plugins:native-test",