按审计结论收口 M3:CI 接线、并发口径、feature 单一推导与文档对齐
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m22s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m52s
Project CI / Native shell tests (pull_request) Failing after 1m12s
Project CI / Frontend tests (pull_request) Failing after 44s
Project CI / Backend tests (pull_request) Successful in 3m28s
Project CI / Repository checks (pull_request) Successful in 1m50s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m20s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m31s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m14s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m22s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m52s
Project CI / Native shell tests (pull_request) Failing after 1m12s
Project CI / Frontend tests (pull_request) Failing after 44s
Project CI / Backend tests (pull_request) Successful in 3m28s
Project CI / Repository checks (pull_request) Successful in 1m50s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m20s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m31s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m14s
- CI:把 bundled-resources:test(13 条,跨平台、注入假执行器)接进 ai-game-creator-shell:check:web,Linux CI 即可覆盖准备步骤逻辑 - 并发:按「无实际场景不做并发支持」定论——不加锁,但替换失败改为可读错误并保留已生成的 staging;技术方案 §4.3 与风险表同步改写 - feature:新增 cargo-features.resolveEditorFeatures 作为唯一推导入口(env > 命令行 --features > 平台默认),dev 与发布入口的 cargo 参数、随包资源准备步骤共用同一集合;--no-bundle 也执行 staging(app 构建本身需要随包资源),只跳过总号发布 - 文档:pitfalls 的校验收口限定到 source 派生内容;里程碑验收清单按实况修正(命令 --、prepared 只查存在性、Windows 侧已复跑) - 验证:准备步骤 13/13、发布入口 39/39、dev 入口 12/12、声明门禁一致
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
} from './agc-global-version.mjs';
|
||||
import {
|
||||
defaultEditorFeatures,
|
||||
resolveEditorFeatures,
|
||||
withDefaultCargoFeatures,
|
||||
} from './cargo-features.mjs';
|
||||
import {
|
||||
@@ -435,11 +436,11 @@ function writeChannelConfigFile(channel, target, includeNodeRuntime = false) {
|
||||
/// 随包资源必须在打包工具之前生成:构建脚本只做只读校验,不再生成。
|
||||
export function stageBundledResources(
|
||||
target,
|
||||
{ prepare = prepareBundledResources } = {},
|
||||
{ prepare = prepareBundledResources, features } = {},
|
||||
) {
|
||||
const summaries = prepare({
|
||||
target,
|
||||
features: new Set(defaultEditorFeatures(target)),
|
||||
features: new Set(features ?? defaultEditorFeatures(target)),
|
||||
profile: 'release',
|
||||
log: (line) => console.log(`[ai-game-creator-shell] ${line}`),
|
||||
});
|
||||
@@ -465,10 +466,12 @@ export function runTauriBuild(
|
||||
}
|
||||
const tauriArguments = buildTauriBuildArguments(args, context.target);
|
||||
const { channel, target } = context;
|
||||
const features = resolveEditorFeatures({ argv: args, target });
|
||||
if (!args.includes('--no-bundle')) {
|
||||
stageRuntime(target);
|
||||
stageBundled(target);
|
||||
}
|
||||
// app 构建本身就需要随包资源,因此 --no-bundle 也要 staging;只有总号发布是打包专属。
|
||||
stageBundled(target, { features });
|
||||
const configPath = writeChannelConfigFile(
|
||||
channel,
|
||||
target,
|
||||
|
||||
@@ -839,12 +839,20 @@ test('release stages Node before Tauri and injects its resource mapping only for
|
||||
},
|
||||
});
|
||||
assert.deepEqual(events, ['stage', 'bundled', 'build']);
|
||||
events.length = 0;
|
||||
runTauriBuild(['--no-bundle', '--target', windowsTarget], context, {
|
||||
stageRuntime() {
|
||||
assert.fail('no-bundle must not stage resources');
|
||||
assert.fail('no-bundle must not stage node runtime');
|
||||
},
|
||||
stageBundled() {
|
||||
assert.fail('no-bundle must not stage bundled resources');
|
||||
stageBundled(target, options) {
|
||||
// app 构建本身就需要随包资源,因此 --no-bundle 也要 staging,
|
||||
// 且必须拿到与 cargo 相同的 feature 集(避免「cargo 开、staging 没开」)。
|
||||
assert.equal(target, windowsTarget);
|
||||
assert.ok(
|
||||
options?.features?.length > 0,
|
||||
'bundled staging 必须收到 feature 集',
|
||||
);
|
||||
events.push('bundled');
|
||||
},
|
||||
spawn(_binary, args) {
|
||||
const config = JSON.parse(
|
||||
|
||||
@@ -22,3 +22,48 @@ export function defaultEditorFeatures(target) {
|
||||
? ['cocos-editor-execute', 'unity-editor-execute', 'godot-editor-execute']
|
||||
: [];
|
||||
}
|
||||
|
||||
/// 单一声明式解析:本次构建实际生效的编辑器 feature 集。
|
||||
/// 解析顺序:环境变量 `AGC_DEV_CARGO_FEATURES` > 命令行 `--features` > 目标平台默认值。
|
||||
/// dev 入口、发布入口的 cargo 参数与随包资源准备步骤都必须用这里的返回值,
|
||||
/// 否则会出现「cargo 开了 feature、staging 没开(或反之)」的窗口。
|
||||
export function resolveEditorFeatures({
|
||||
argv = [],
|
||||
target,
|
||||
env = process.env,
|
||||
} = {}) {
|
||||
// 顺序:环境变量(dev 的显式覆盖)> 命令行 --features > 目标平台默认值。
|
||||
const fromEnv = String(env.AGC_DEV_CARGO_FEATURES ?? '').trim();
|
||||
if (fromEnv) {
|
||||
return fromEnv
|
||||
.split(',')
|
||||
.map((name) => name.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
const fromArgv = featuresFromArgv(argv);
|
||||
if (fromArgv) {
|
||||
return fromArgv;
|
||||
}
|
||||
return defaultEditorFeatures(target);
|
||||
}
|
||||
|
||||
/// 从 argv 里解析 `--features a,b` / `--features=a,b`;没有则返回 null。
|
||||
export function featuresFromArgv(argv = []) {
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const value = String(argv[index]);
|
||||
if (value.startsWith('--features=')) {
|
||||
return value
|
||||
.slice('--features='.length)
|
||||
.split(',')
|
||||
.map((name) => name.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
if (value === '--features' && argv[index + 1] !== undefined) {
|
||||
return String(argv[index + 1])
|
||||
.split(',')
|
||||
.map((name) => name.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -498,7 +498,14 @@ function stageAtomically(unitPath, builder) {
|
||||
throw error;
|
||||
}
|
||||
rmSync(unitPath, { recursive: true, force: true });
|
||||
renameSync(stagingPath, unitPath);
|
||||
try {
|
||||
renameSync(stagingPath, unitPath);
|
||||
} catch (error) {
|
||||
// 并发调用未做加锁(见技术方案 §4.3):这里只保证失败可读、且不丢已经生成好的 staging。
|
||||
fail(
|
||||
`替换 ${unitPath} 失败(${error.code ?? error.message}):同一资源目录可能正被另一个准备步骤进程写入;staging 已保留在 ${stagingPath},确认没有并发进程后重试`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function prepareCodex({
|
||||
|
||||
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { buildLocalRustProcessEnv } from '../../../scripts/dev.mjs';
|
||||
import {
|
||||
defaultEditorFeatures,
|
||||
resolveEditorFeatures,
|
||||
withDefaultCargoFeatures,
|
||||
} from './cargo-features.mjs';
|
||||
import {
|
||||
@@ -57,18 +57,11 @@ function buildTauriArguments(argv, devUrl = readAgcDevEndpoint().url) {
|
||||
|
||||
// 开发和发行构建使用同一平台编辑器 feature 集合。
|
||||
// 可用 AGC_DEV_CARGO_FEATURES(逗号分隔)覆盖,传空串即关闭。
|
||||
function readDevCargoFeatures(env = process.env) {
|
||||
const override = env.AGC_DEV_CARGO_FEATURES;
|
||||
if (override !== undefined) {
|
||||
return override
|
||||
.split(',')
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
return defaultEditorFeatures(process.platform);
|
||||
function readDevCargoFeatures(argv = []) {
|
||||
return resolveEditorFeatures({ argv, target: process.platform });
|
||||
}
|
||||
|
||||
function withDevCargoFeatures(argv, features = readDevCargoFeatures()) {
|
||||
function withDevCargoFeatures(argv, features = readDevCargoFeatures(argv)) {
|
||||
return withDefaultCargoFeatures(argv, features);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @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 = "agc-package-layout.v1";
|
||||
|
||||
Reference in New Issue
Block a user