Files
Genarrative/apps/ai-game-creator-shell/scripts/macos-release-identity.test.mjs
T
kdletters 110f088d41
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 1m12s
Project CI / AI game creator shell Rust smoke (push) Failing after 1m12s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 1m12s
Project CI / AI game creator shell Rust crates (push) Successful in 1m37s
Project CI / Frontend tests (push) Successful in 3m37s
Project CI / Repository checks (push) Successful in 4m4s
Project CI / AI game creator shell web tests (push) Successful in 2m11s
Project CI / Backend tests (push) Successful in 6m31s
Project CI / Native shell tests (push) Successful in 7m13s
修复 Jenkins 发布流水线与 macOS 包门禁
- 转义 Stdb Publish GString 内的 shell 命令替换,避免 Jenkinsfile 加载时 Groovy 编译失败
- 为 Stdb Publish 暂存清理命令补充生产运维回归门禁
- 抽离 macOS 包内容策略并放行随包 Claude Agent SDK 的受控 node_modules
- 违规资源现在会输出具体相对路径,并补充包内容白名单单测与排障记录
2026-09-30 16:01:21 +08:00

163 lines
5.4 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { listForbiddenBundledResourceFiles } from './macos-bundle-policy.mjs';
import {
assertMacosAppMatchesChannelIdentity,
assertManifestArtifactMatchesExpected,
listStaleMacosArtifacts,
readMacosAppInfoIdentity,
} from './macos-release-identity.mjs';
test('allows only the production node_modules subtrees in the macOS bundle', () => {
assert.deepEqual(
listForbiddenBundledResourceFiles([
'game-runtime/node/node_modules',
'game-runtime/node/node_modules/npm/bin/npm-cli.js',
'claude-agent/node_modules',
'claude-agent/node_modules/@anthropic-ai',
'claude-agent/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs',
'claude-agent/node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64/claude',
]),
[],
);
assert.deepEqual(
listForbiddenBundledResourceFiles([
'claude-agent/node_modules/unexpected/index.mjs',
'claude-agent/node_modules/@anthropic-ai/unexpected/index.mjs',
'plugins/agc-cocos-editor/node_modules/leftover/index.mjs',
'game-runtime/node/.env.local',
'claude-agent/auth.json',
'claude-agent/target/debug/claude',
'claude-agent/tool.exe',
'claude-agent/tool.dll',
]),
[
'claude-agent/node_modules/unexpected/index.mjs',
'claude-agent/node_modules/@anthropic-ai/unexpected/index.mjs',
'plugins/agc-cocos-editor/node_modules/leftover/index.mjs',
'game-runtime/node/.env.local',
'claude-agent/auth.json',
'claude-agent/target/debug/claude',
'claude-agent/tool.exe',
'claude-agent/tool.dll',
],
);
});
const DEV_IDENTITY = {
productName: '陶泥儿开发版',
identifier: 'world.genarrative.ai-game-creator',
};
function plist({ version, identifier, name }) {
return `<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
<key>CFBundleShortVersionString</key><string>${version}</string>
<key>CFBundleIdentifier</key><string>${identifier}</string>
<key>CFBundleName</key><string>${name}</string>
<key>CFBundleDisplayName</key><string>${name}</string>
</dict></plist>`;
}
test('reads version, identifier and product name from Info.plist text', () => {
const identity = readMacosAppInfoIdentity(
plist({
version: '0.1.154',
...DEV_IDENTITY,
name: DEV_IDENTITY.productName,
}),
);
assert.deepEqual(identity, {
version: '0.1.154',
identifier: DEV_IDENTITY.identifier,
name: DEV_IDENTITY.productName,
displayName: DEV_IDENTITY.productName,
});
});
test('accepts the app bundle that matches this channel and version', () => {
assert.doesNotThrow(() =>
assertMacosAppMatchesChannelIdentity({
identity: readMacosAppInfoIdentity(
plist({
version: '0.1.154',
identifier: DEV_IDENTITY.identifier,
name: DEV_IDENTITY.productName,
}),
),
expectedVersion: '0.1.154',
expectedProductName: DEV_IDENTITY.productName,
expectedIdentifier: DEV_IDENTITY.identifier,
}),
);
});
// 回归:线上 dev-mac/0.1.142 清单实际指向 0.1.139 的 release 身份包。
test('rejects the release-identity bundle that shipped in the dev-mac channel', () => {
const shipped = readMacosAppInfoIdentity(
plist({
version: '0.1.139',
identifier: 'world.genarrative.ai-game-creator.release',
name: '陶泥儿 Release',
}),
);
assert.throws(
() =>
assertMacosAppMatchesChannelIdentity({
identity: shipped,
expectedVersion: '0.1.142',
expectedProductName: DEV_IDENTITY.productName,
expectedIdentifier: DEV_IDENTITY.identifier,
}),
/版本不一致:产物 0\.1\.139,本轮清单 0\.1\.142[\s\S]*bundle identifier 不一致[\s\S]*产品名不一致/u,
);
});
test('rejects a bundle whose version is right but channel identity is wrong', () => {
assert.throws(
() =>
assertMacosAppMatchesChannelIdentity({
identity: readMacosAppInfoIdentity(
plist({
version: '0.1.154',
identifier: 'world.genarrative.ai-game-creator.release',
name: '陶泥儿 Release',
}),
),
expectedVersion: '0.1.154',
expectedProductName: DEV_IDENTITY.productName,
expectedIdentifier: DEV_IDENTITY.identifier,
}),
/bundle identifier 不一致/u,
);
});
test('lists every stale mac artifact so the bundle directory cannot leak into the manifest', () => {
const files = [
'/bundle/macos/陶泥儿 Release.app.tar.gz',
'/bundle/macos/陶泥儿 Release.app.tar.gz.sig',
'/bundle/macos/陶泥儿开发版_0.1.139_aarch64.dmg',
'/bundle/macos/陶泥儿开发版_0.1.139_aarch64.dmg.sha256',
'/bundle/macos/陶泥儿开发版.app/Contents/Info.plist',
];
assert.deepEqual(listStaleMacosArtifacts(files), files.slice(0, 4));
});
test('rejects a manifest that selected a different artifact than this build wrote', () => {
assert.throws(
() =>
assertManifestArtifactMatchesExpected({
artifactPath: '/bundle/macos/陶泥儿 Release.app.tar.gz',
expectedPath: '/bundle/macos/陶泥儿开发版.app.tar.gz',
}),
/清单选中的更新包不是本轮产物/u,
);
assert.doesNotThrow(() =>
assertManifestArtifactMatchesExpected({
artifactPath: '/bundle/macos/陶泥儿开发版.app.tar.gz',
expectedPath: '/bundle/macos/陶泥儿开发版.app.tar.gz',
}),
);
});