补齐移动壳原生包构建配置
移动壳新增 EAS Android 和 iOS 构建 profile 移动壳增加原生包构建配置 smoke 门禁 原生壳总验收接入移动 EAS 配置检查 宿主壳文档同步移动原生包构建口径
This commit is contained in:
@@ -89,6 +89,8 @@ const sharedContractPath = new URL(
|
||||
const sharedContractSource = fs.readFileSync(sharedContractPath, 'utf8');
|
||||
const packagePath = new URL('../package.json', import.meta.url);
|
||||
const packageConfig = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
||||
const easConfigPath = new URL('../eas.json', import.meta.url);
|
||||
const easConfig = JSON.parse(fs.readFileSync(easConfigPath, 'utf8'));
|
||||
const rootPackagePath = new URL('../../../package.json', import.meta.url);
|
||||
const rootPackageConfig = JSON.parse(fs.readFileSync(rootPackagePath, 'utf8'));
|
||||
const rootPackageLockPath = new URL('../../../package-lock.json', import.meta.url);
|
||||
@@ -438,7 +440,7 @@ function collectProductionSourceFiles(entry) {
|
||||
if (!productionFileExtensions.has(extension)) {
|
||||
return [];
|
||||
}
|
||||
if (path.includes('.test.') || path.endsWith('/scripts/check-config.mjs')) {
|
||||
if (path.includes('.test.') || path.includes('/scripts/check-')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -524,6 +526,9 @@ for (const [scriptName, expected] of Object.entries({
|
||||
android: 'expo run:android',
|
||||
ios: 'expo run:ios',
|
||||
test: 'vitest run -c vitest.config.ts',
|
||||
'build:android': 'eas build --local --profile production --platform android',
|
||||
'build:ios': 'eas build --local --profile production-simulator --platform ios',
|
||||
'build-config:smoke': 'node scripts/check-eas-build-config.mjs',
|
||||
'config:smoke': 'node scripts/check-expo-config.mjs',
|
||||
'export:smoke': 'node scripts/check-expo-export.mjs',
|
||||
typecheck: 'tsc -p tsconfig.json --noEmit && node scripts/check-config.mjs',
|
||||
@@ -535,6 +540,9 @@ for (const [scriptName, expected] of Object.entries({
|
||||
'mobile-shell:dev': 'npm --prefix apps/mobile-shell run dev',
|
||||
'mobile-shell:typecheck': 'npm --prefix apps/mobile-shell run typecheck',
|
||||
'mobile-shell:test': 'npm --prefix apps/mobile-shell run test',
|
||||
'mobile-shell:build-config': 'npm --prefix apps/mobile-shell run build-config:smoke',
|
||||
'mobile-shell:build:android': 'npm --prefix apps/mobile-shell run build:android',
|
||||
'mobile-shell:build:ios': 'npm --prefix apps/mobile-shell run build:ios',
|
||||
'mobile-shell:config': 'npm --prefix apps/mobile-shell run config:smoke',
|
||||
'mobile-shell:export': 'npm --prefix apps/mobile-shell run export:smoke',
|
||||
})) {
|
||||
@@ -618,6 +626,15 @@ for (const [dependency, expected] of Object.entries({
|
||||
);
|
||||
}
|
||||
|
||||
assertPackageDependencyVersion(
|
||||
rootPackageConfig,
|
||||
'root package',
|
||||
'devDependencies',
|
||||
'eas-cli',
|
||||
'^20.3.0',
|
||||
);
|
||||
assertPackageLockVersion('eas-cli', '20.3.0');
|
||||
|
||||
for (const [dependency, expected] of Object.entries({
|
||||
typescript: '5.8.3',
|
||||
vitest: '0.34.6',
|
||||
@@ -983,6 +1000,40 @@ if ('releaseChannel' in appConfig || 'channel' in appConfig) {
|
||||
throw new Error('mobile shell must not configure an app release channel without a real release process');
|
||||
}
|
||||
|
||||
if (easConfig.cli?.version !== '>= 20.3.0' || easConfig.cli?.appVersionSource !== 'local') {
|
||||
throw new Error('mobile shell EAS builds must pin CLI floor and use local app version fields');
|
||||
}
|
||||
|
||||
if (
|
||||
easConfig.build?.production?.distribution !== 'internal' ||
|
||||
easConfig.build?.production?.channel !== 'production' ||
|
||||
easConfig.build?.production?.android?.buildType !== 'apk' ||
|
||||
easConfig.build?.production?.env?.EXPO_NO_DOTENV !== '1'
|
||||
) {
|
||||
throw new Error('mobile shell EAS Android production profile must build an internal APK without local dotenv');
|
||||
}
|
||||
|
||||
if (
|
||||
easConfig.build?.['production-simulator']?.distribution !== 'internal' ||
|
||||
easConfig.build?.['production-simulator']?.channel !== 'production' ||
|
||||
easConfig.build?.['production-simulator']?.ios?.simulator !== true ||
|
||||
easConfig.build?.['production-simulator']?.env?.EXPO_NO_DOTENV !== '1'
|
||||
) {
|
||||
throw new Error('mobile shell EAS iOS production smoke profile must build an internal simulator package without local dotenv');
|
||||
}
|
||||
|
||||
for (const [profileName, profile] of Object.entries(easConfig.build ?? {})) {
|
||||
for (const blockedKey of ['credentialsSource', 'autoIncrement', 'submit', 'releaseChannel']) {
|
||||
if (blockedKey in profile) {
|
||||
throw new Error(`mobile shell EAS ${profileName} profile must not configure ${blockedKey}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ('submit' in easConfig) {
|
||||
throw new Error('mobile shell EAS config must not include store submit profiles yet');
|
||||
}
|
||||
|
||||
assertSameList(
|
||||
appConfig.ios?.associatedDomains ?? [],
|
||||
[sharedPublicWebAssociatedDomain],
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
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'));
|
||||
|
||||
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 (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'
|
||||
) {
|
||||
throw new Error('mobile shell Android package build script drifted');
|
||||
}
|
||||
|
||||
if (
|
||||
packageConfig.scripts['build:ios'] !==
|
||||
'eas build --local --profile production-simulator --platform ios'
|
||||
) {
|
||||
throw new Error('mobile shell iOS package build script drifted');
|
||||
}
|
||||
|
||||
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', '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');
|
||||
Reference in New Issue
Block a user