Files
Genarrative/apps/mobile-shell/scripts/check-eas-build-config.mjs
T
kdletters 01394ed238 门禁加固与 Windows 可跑性
- 新增 scripts/check-generated-bindings.mjs 并接入 lint 与 ai-game-creator-shell:check:rust:crates:重跑 export_bindings 前后逐字节比对共享契约绑定。
- check:native-shells 的 AGC 边界改为负向门禁:渲染源码不得出现 fetch(/XHR/EventSource/sendBeacon/WebSocket/plugin-http,客户端 capability 不得授予 http:default。
- check-pingora-gateway-smoke 在 Windows 上找 pingora-gateway.exe,并修掉「API 并发保护」先关客户端连接再读响应的竞态。
- mobile-shell 三个 smoke 改成固定命令串启动 npm(绕开 npm.cmd EINVAL 与 DEP0190),desktop-shell 的 stage-release-binary 改用 fileURLToPath 修掉 F:\F:\… 路径拼接。
- vitest.config.ts 白名单补回现役根用例;三份 nginx 模板的 SPA allowlist 补回 /components 与 /design-system。
2026-09-28 14:15:50 +08:00

193 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
const shellRoot = new URL('../', import.meta.url);
const easConfigPath = new URL('eas.json', shellRoot);
const packagePath = new URL('package.json', shellRoot);
const easConfig = JSON.parse(fs.readFileSync(easConfigPath, 'utf8'));
const packageConfig = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const androidBuildOutputPath =
'../../build/native/mobile/genarrative-mobile-android.apk';
const iosSimulatorBuildOutputPath =
'../../build/native/mobile/genarrative-mobile-ios-simulator.tar.gz';
function assertObject(value, label) {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`${label} must be an object`);
}
}
function assertNoKeys(value, blockedKeys, label) {
for (const key of blockedKeys) {
if (key in value) {
throw new Error(`${label} must not configure ${key}`);
}
}
}
assertObject(easConfig.cli, 'EAS cli config');
assertObject(easConfig.build, 'EAS build config');
if (easConfig.cli.version !== '>= 20.3.0') {
throw new Error(
'mobile shell EAS CLI version gate must stay pinned to the checked devDependency floor',
);
}
if (packageConfig.devDependencies?.['eas-cli'] !== '^20.3.0') {
throw new Error(
'mobile shell package must declare the checked EAS CLI devDependency',
);
}
// 同 check-expo-config.mjs:Windows 上启动 npm.cmd 必须有 shell,这里改用固定命令串,
// 既避开 EINVAL,也避开 Node 对「shell + args 数组」的 DEP0190 警告。
const easVersionResult = spawnSync(`${npmCommand} exec eas -- --version`, {
cwd: shellRoot,
encoding: 'utf8',
shell: true,
});
if (easVersionResult.error) {
throw new Error(
`failed to start local EAS CLI version check: ${easVersionResult.error.message}`,
);
}
if (easVersionResult.signal) {
throw new Error(
`local EAS CLI version check was terminated by signal ${easVersionResult.signal}`,
);
}
if ((easVersionResult.status ?? 0) !== 0) {
process.stdout.write(easVersionResult.stdout ?? '');
process.stderr.write(easVersionResult.stderr ?? '');
process.exit(easVersionResult.status ?? 1);
}
if (!(easVersionResult.stdout ?? '').trim().startsWith('eas-cli/20.3.0 ')) {
throw new Error('mobile shell local EAS CLI version drifted');
}
if (easConfig.cli.appVersionSource !== 'local') {
throw new Error(
'mobile shell EAS builds must use local app.json version fields',
);
}
for (const scriptName of ['build:android', 'build:ios', 'build-config:smoke']) {
if (typeof packageConfig.scripts?.[scriptName] !== 'string') {
throw new Error(`mobile shell package missing ${scriptName} script`);
}
}
if (
packageConfig.scripts['build:android'] !==
`eas build --local --profile production --platform android --output ${androidBuildOutputPath}`
) {
throw new Error('mobile shell Android package build script drifted');
}
if (
packageConfig.scripts['build:ios'] !==
`eas build --local --profile production-simulator --platform ios --output ${iosSimulatorBuildOutputPath}`
) {
throw new Error('mobile shell iOS package build script drifted');
}
for (const [label, outputPath, extension] of [
['Android', androidBuildOutputPath, '.apk'],
['iOS simulator', iosSimulatorBuildOutputPath, '.tar.gz'],
]) {
if (!outputPath.startsWith('../../build/native/mobile/')) {
throw new Error(
`mobile shell ${label} local build output must stay under root build/native/mobile`,
);
}
if (!outputPath.endsWith(extension)) {
throw new Error(
`mobile shell ${label} local build output must end with ${extension}`,
);
}
}
if (
packageConfig.scripts['build-config:smoke'] !==
'node scripts/check-eas-build-config.mjs'
) {
throw new Error('mobile shell EAS config smoke script drifted');
}
const production = easConfig.build.production;
const productionSimulator = easConfig.build['production-simulator'];
assertObject(production, 'EAS Android production profile');
assertObject(productionSimulator, 'EAS iOS simulator production profile');
if (
production.distribution !== 'internal' ||
production.channel !== 'production'
) {
throw new Error(
'mobile shell Android production profile must build an internal production package',
);
}
if (production.android?.buildType !== 'apk') {
throw new Error(
'mobile shell Android production profile must produce an installable APK',
);
}
if (production.env?.EXPO_NO_DOTENV !== '1') {
throw new Error(
'mobile shell Android production profile must ignore local dotenv files',
);
}
if (
productionSimulator.distribution !== 'internal' ||
productionSimulator.channel !== 'production'
) {
throw new Error(
'mobile shell iOS simulator production profile must stay internal production',
);
}
if (productionSimulator.ios?.simulator !== true) {
throw new Error(
'mobile shell iOS production smoke profile must target simulator builds until signing is configured',
);
}
if (productionSimulator.env?.EXPO_NO_DOTENV !== '1') {
throw new Error(
'mobile shell iOS simulator production profile must ignore local dotenv files',
);
}
for (const [profileName, profile] of Object.entries(easConfig.build)) {
assertObject(profile, `EAS ${profileName} profile`);
assertNoKeys(
profile,
[
'credentialsSource',
'autoIncrement',
'submit',
'runtimeVersion',
'releaseChannel',
],
`EAS ${profileName} profile`,
);
}
if ('submit' in easConfig) {
throw new Error(
'mobile shell EAS config must not include store submit profiles yet',
);
}
console.log('[mobile-shell:eas-build-config] OK');