@expo/eas-json
Advanced tools
Comparing version 0.3.0 to 0.4.0
@@ -1,2 +0,2 @@ | ||
import { Android, Workflow } from '@expo/eas-build-job'; | ||
import { Android, Workflow, iOS } from '@expo/eas-build-job'; | ||
export declare enum CredentialsSource { | ||
@@ -11,3 +11,4 @@ LOCAL = "local", | ||
} | ||
export interface AndroidManagedBuildProfile { | ||
export declare type VersionAutoIncrement = boolean | 'version' | 'buildNumber'; | ||
export interface AndroidManagedBuildProfile extends Android.BuilderEnvironment { | ||
workflow: Workflow.Managed; | ||
@@ -19,3 +20,3 @@ credentialsSource: CredentialsSource; | ||
} | ||
export interface AndroidGenericBuildProfile { | ||
export interface AndroidGenericBuildProfile extends Android.BuilderEnvironment { | ||
workflow: Workflow.Generic; | ||
@@ -29,15 +30,19 @@ credentialsSource: CredentialsSource; | ||
} | ||
export interface iOSManagedBuildProfile { | ||
export interface iOSManagedBuildProfile extends iOS.BuilderEnvironment { | ||
workflow: Workflow.Managed; | ||
credentialsSource: CredentialsSource; | ||
buildType?: iOS.ManagedBuildType; | ||
releaseChannel?: string; | ||
distribution?: DistributionType; | ||
autoIncrement: VersionAutoIncrement; | ||
} | ||
export interface iOSGenericBuildProfile { | ||
export interface iOSGenericBuildProfile extends iOS.BuilderEnvironment { | ||
workflow: Workflow.Generic; | ||
credentialsSource: CredentialsSource; | ||
scheme?: string; | ||
schemeBuildConfiguration?: iOS.GenericSchemeBuildConfiguration; | ||
releaseChannel?: string; | ||
artifactPath?: string; | ||
distribution?: DistributionType; | ||
autoIncrement: VersionAutoIncrement; | ||
} | ||
@@ -44,0 +49,0 @@ export declare type AndroidBuildProfile = AndroidManagedBuildProfile | AndroidGenericBuildProfile; |
@@ -14,3 +14,4 @@ import { Workflow } from '@expo/eas-build-job'; | ||
interface BuildProfilePreValidation { | ||
workflow: Workflow; | ||
workflow?: Workflow; | ||
extends?: string; | ||
} | ||
@@ -25,3 +26,6 @@ export declare class EasJsonReader { | ||
private validateBuildProfile; | ||
private isWorkflowKeySpecified; | ||
private resolveBuildProfile; | ||
} | ||
export declare function deepMerge(base: Record<string, any>, update: Record<string, any>): Record<string, any>; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.EasJsonReader = void 0; | ||
exports.deepMerge = exports.EasJsonReader = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -15,11 +15,11 @@ const eas_build_job_1 = require("@expo/eas-build-job"); | ||
async readAsync(buildProfileName) { | ||
var _a, _b, _c, _d; | ||
var _a, _b; | ||
const easJson = await this.readRawAsync(); | ||
let androidConfig; | ||
if (['android', 'all'].includes(this.platform)) { | ||
androidConfig = this.validateBuildProfile(eas_build_job_1.Platform.Android, buildProfileName, (_b = (_a = easJson.builds) === null || _a === void 0 ? void 0 : _a.android) === null || _b === void 0 ? void 0 : _b[buildProfileName]); | ||
androidConfig = this.validateBuildProfile(eas_build_job_1.Platform.Android, buildProfileName, ((_a = easJson.builds) === null || _a === void 0 ? void 0 : _a.android) || {}); | ||
} | ||
let iosConfig; | ||
if (['ios', 'all'].includes(this.platform)) { | ||
iosConfig = this.validateBuildProfile(eas_build_job_1.Platform.iOS, buildProfileName, (_d = (_c = easJson.builds) === null || _c === void 0 ? void 0 : _c.ios) === null || _d === void 0 ? void 0 : _d[buildProfileName]); | ||
iosConfig = this.validateBuildProfile(eas_build_job_1.Platform.iOS, buildProfileName, ((_b = easJson.builds) === null || _b === void 0 ? void 0 : _b.ios) || {}); | ||
} | ||
@@ -33,5 +33,8 @@ return { | ||
const easJson = await this.readRawAsync(); | ||
for (const [name, profile] of Object.entries((_b = (_a = easJson.builds) === null || _a === void 0 ? void 0 : _a.android) !== null && _b !== void 0 ? _b : {})) { | ||
const androidProfiles = (_b = (_a = easJson.builds) === null || _a === void 0 ? void 0 : _a.android) !== null && _b !== void 0 ? _b : {}; | ||
for (const name of Object.keys(androidProfiles)) { | ||
try { | ||
await this.validateBuildProfile(eas_build_job_1.Platform.Android, name, profile); | ||
if (this.isWorkflowKeySpecified(eas_build_job_1.Platform.Android, name, androidProfiles)) { | ||
await this.validateBuildProfile(eas_build_job_1.Platform.Android, name, androidProfiles); | ||
} | ||
} | ||
@@ -43,5 +46,8 @@ catch (err) { | ||
} | ||
for (const [name, profile] of Object.entries((_d = (_c = easJson.builds) === null || _c === void 0 ? void 0 : _c.ios) !== null && _d !== void 0 ? _d : {})) { | ||
const iosProfiles = (_d = (_c = easJson.builds) === null || _c === void 0 ? void 0 : _c.ios) !== null && _d !== void 0 ? _d : {}; | ||
for (const name of Object.keys(iosProfiles)) { | ||
try { | ||
await this.validateBuildProfile(eas_build_job_1.Platform.iOS, name, profile); | ||
if (this.isWorkflowKeySpecified(eas_build_job_1.Platform.iOS, name, iosProfiles)) { | ||
await this.validateBuildProfile(eas_build_job_1.Platform.iOS, name, iosProfiles); | ||
} | ||
} | ||
@@ -55,3 +61,3 @@ catch (err) { | ||
async readRawAsync() { | ||
const rawFile = await fs_extra_1.default.readFile(path_1.default.join(this.projectDir, 'eas.json'), 'utf-8'); | ||
const rawFile = await fs_extra_1.default.readFile(path_1.default.join(this.projectDir, 'eas.json'), 'utf8'); | ||
const json = JSON.parse(rawFile); | ||
@@ -66,7 +72,8 @@ const { value, error } = EasJsonSchema_1.EasJsonSchema.validate(json, { | ||
} | ||
validateBuildProfile(platform, buildProfileName, buildProfile) { | ||
if (!buildProfile) { | ||
throw new Error(`There is no profile named ${buildProfileName} for platform ${platform}`); | ||
validateBuildProfile(platform, buildProfileName, buildProfiles) { | ||
const buildProfile = this.resolveBuildProfile(platform, buildProfileName, buildProfiles); | ||
if (![eas_build_job_1.Workflow.Generic, eas_build_job_1.Workflow.Managed].includes(buildProfile.workflow)) { | ||
throw new Error('"workflow" key is required in a build profile and has to be one of ["generic", "managed"].'); | ||
} | ||
const schema = EasJsonSchema_1.schemaBuildProfileMap[platform][buildProfile === null || buildProfile === void 0 ? void 0 : buildProfile.workflow]; | ||
const schema = EasJsonSchema_1.schemaBuildProfileMap[platform][buildProfile.workflow]; | ||
if (!schema) { | ||
@@ -85,3 +92,51 @@ throw new Error('invalid workflow'); // this should be validated earlier | ||
} | ||
isWorkflowKeySpecified(platform, buildProfileName, buildProfiles) { | ||
const buildProfile = this.resolveBuildProfile(platform, buildProfileName, buildProfiles); | ||
return !!buildProfile.workflow; | ||
} | ||
resolveBuildProfile(platform, buildProfileName, buildProfiles, depth = 0) { | ||
if (depth >= 2) { | ||
throw new Error('Too long chain of build profile extensions, make sure "extends" keys do not make a cycle'); | ||
} | ||
const buildProfile = buildProfiles[buildProfileName]; | ||
if (!buildProfile) { | ||
throw new Error(`There is no profile named ${buildProfileName} for platform ${platform}`); | ||
} | ||
const baseProfileName = buildProfile.extends; | ||
delete buildProfile.extends; | ||
if (baseProfileName) { | ||
return deepMerge(this.resolveBuildProfile(platform, baseProfileName, buildProfiles, depth + 1), buildProfile); | ||
} | ||
else { | ||
return buildProfile; | ||
} | ||
} | ||
} | ||
exports.EasJsonReader = EasJsonReader; | ||
function isObject(value) { | ||
return typeof value === 'object' && value !== null; | ||
} | ||
function deepMerge(base, update) { | ||
const result = {}; | ||
Object.keys(base).forEach(key => { | ||
const oldValue = base[key]; | ||
const newValue = update[key]; | ||
if (isObject(newValue) && isObject(oldValue)) { | ||
result[key] = deepMerge(oldValue, newValue); | ||
} | ||
else if (newValue !== undefined) { | ||
result[key] = isObject(newValue) ? deepMerge({}, newValue) : newValue; | ||
} | ||
else { | ||
result[key] = isObject(oldValue) ? deepMerge({}, oldValue) : oldValue; | ||
} | ||
}); | ||
Object.keys(update).forEach(key => { | ||
const newValue = update[key]; | ||
if (result[key] === undefined) { | ||
result[key] = isObject(newValue) ? deepMerge({}, newValue) : newValue; | ||
} | ||
}); | ||
return result; | ||
} | ||
exports.deepMerge = deepMerge; |
@@ -5,3 +5,31 @@ "use strict"; | ||
const tslib_1 = require("tslib"); | ||
const eas_build_job_1 = require("@expo/eas-build-job"); | ||
const joi_1 = tslib_1.__importDefault(require("@hapi/joi")); | ||
const semverSchemaCheck = (value, helpers) => { | ||
if (/^[0-9]+\.[0-9]+\.[0-9]+$/.test(value)) { | ||
return value; | ||
} | ||
else { | ||
throw new Error(`${value} is not a valid version`); | ||
} | ||
}; | ||
const AndroidBuilderEnvironmentSchema = joi_1.default.object({ | ||
image: joi_1.default.string() | ||
.valid(...eas_build_job_1.Android.builderBaseImages) | ||
.default('default'), | ||
node: joi_1.default.string().custom(semverSchemaCheck), | ||
yarn: joi_1.default.string().custom(semverSchemaCheck), | ||
ndk: joi_1.default.string(), | ||
env: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.string()).default({}), | ||
}); | ||
const IosBuilderEnvironmentSchema = joi_1.default.object({ | ||
image: joi_1.default.string() | ||
.valid(...eas_build_job_1.iOS.builderBaseImages) | ||
.default('default'), | ||
node: joi_1.default.string().custom(semverSchemaCheck), | ||
yarn: joi_1.default.string().custom(semverSchemaCheck), | ||
fastlane: joi_1.default.string().custom(semverSchemaCheck), | ||
cocoapods: joi_1.default.string().custom(semverSchemaCheck), | ||
env: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.string()).default({}), | ||
}); | ||
const AndroidGenericSchema = joi_1.default.object({ | ||
@@ -19,3 +47,3 @@ workflow: joi_1.default.string().valid('generic').required(), | ||
distribution: joi_1.default.string().valid('store', 'internal').default('store'), | ||
}); | ||
}).concat(AndroidBuilderEnvironmentSchema); | ||
const AndroidManagedSchema = joi_1.default.object({ | ||
@@ -31,3 +59,3 @@ workflow: joi_1.default.string().valid('managed').required(), | ||
distribution: joi_1.default.string().valid('store', 'internal').default('store'), | ||
}); | ||
}).concat(AndroidBuilderEnvironmentSchema); | ||
const iOSGenericSchema = joi_1.default.object({ | ||
@@ -37,12 +65,20 @@ workflow: joi_1.default.string().valid('generic').required(), | ||
scheme: joi_1.default.string(), | ||
schemeBuildConfiguration: joi_1.default.string().valid('Debug', 'Release'), | ||
releaseChannel: joi_1.default.string(), | ||
artifactPath: joi_1.default.string(), | ||
distribution: joi_1.default.string().valid('store', 'internal').default('store'), | ||
}); | ||
autoIncrement: joi_1.default.alternatives() | ||
.try(joi_1.default.boolean(), joi_1.default.string().valid('version', 'buildNumber')) | ||
.default(false), | ||
}).concat(IosBuilderEnvironmentSchema); | ||
const iOSManagedSchema = joi_1.default.object({ | ||
workflow: joi_1.default.string().valid('managed').required(), | ||
credentialsSource: joi_1.default.string().valid('local', 'remote', 'auto').default('auto'), | ||
buildType: joi_1.default.string().valid('release', 'development-client'), | ||
releaseChannel: joi_1.default.string(), | ||
distribution: joi_1.default.string().valid('store', 'internal').default('store'), | ||
}); | ||
autoIncrement: joi_1.default.alternatives() | ||
.try(joi_1.default.boolean(), joi_1.default.string().valid('version', 'buildNumber')) | ||
.default(false), | ||
}).concat(IosBuilderEnvironmentSchema); | ||
const schemaBuildProfileMap = { | ||
@@ -62,7 +98,7 @@ android: { | ||
android: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.object({ | ||
workflow: joi_1.default.string().valid('generic', 'managed').required(), | ||
workflow: joi_1.default.string().valid('generic', 'managed'), | ||
}).unknown(true) // profile is validated further only if build is for that platform | ||
), | ||
ios: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.object({ | ||
workflow: joi_1.default.string().valid('generic', 'managed').required(), | ||
workflow: joi_1.default.string().valid('generic', 'managed'), | ||
}).unknown(true) // profile is validated further only if build is for that platform | ||
@@ -69,0 +105,0 @@ ), |
@@ -1,2 +0,2 @@ | ||
export { CredentialsSource, DistributionType, AndroidManagedBuildProfile, AndroidGenericBuildProfile, AndroidBuildProfile, iOSManagedBuildProfile, iOSGenericBuildProfile, iOSBuildProfile, EasConfig, } from './Config.types'; | ||
export { CredentialsSource, DistributionType, AndroidManagedBuildProfile, AndroidGenericBuildProfile, AndroidBuildProfile, iOSManagedBuildProfile, iOSGenericBuildProfile, iOSBuildProfile, EasConfig, VersionAutoIncrement, } from './Config.types'; | ||
export { EasJsonReader } from './EasJsonReader'; |
{ | ||
"name": "@expo/eas-json", | ||
"description": "A library for interacting with the eas.json", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"author": "Expo <support@expo.io>", | ||
"bugs": "https://github.com/expo/eas-cli/issues", | ||
"dependencies": { | ||
"@expo/eas-build-job": "0.1.3", | ||
"@expo/eas-build-job": "0.2.10", | ||
"@hapi/joi": "^17.1.1", | ||
@@ -41,3 +41,3 @@ "fs-extra": "^9.0.1", | ||
}, | ||
"gitHead": "b82f0b04fed235a1fa26687c894d8883692883ea" | ||
"gitHead": "ced2e192eb0704b8bf53d50d27d69f54a9b972bc" | ||
} |
@@ -30,2 +30,4 @@ import fs from 'fs-extra'; | ||
credentialsSource: 'auto', | ||
env: {}, | ||
image: 'default', | ||
}, | ||
@@ -53,2 +55,5 @@ }, | ||
workflow: 'generic', | ||
autoIncrement: false, | ||
env: {}, | ||
image: 'default', | ||
}, | ||
@@ -75,4 +80,17 @@ }, | ||
builds: { | ||
android: { workflow: 'generic', distribution: 'store', credentialsSource: 'auto' }, | ||
ios: { workflow: 'generic', distribution: 'store', credentialsSource: 'auto' }, | ||
android: { | ||
workflow: 'generic', | ||
distribution: 'store', | ||
credentialsSource: 'auto', | ||
env: {}, | ||
image: 'default', | ||
}, | ||
ios: { | ||
workflow: 'generic', | ||
distribution: 'store', | ||
credentialsSource: 'auto', | ||
autoIncrement: false, | ||
env: {}, | ||
image: 'default', | ||
}, | ||
}, | ||
@@ -102,2 +120,4 @@ }).toEqual(easJson); | ||
credentialsSource: 'auto', | ||
env: {}, | ||
image: 'default', | ||
}, | ||
@@ -108,3 +128,3 @@ }, | ||
test('valid eas.json for debug builds', async () => { | ||
test('valid eas.json for development client builds', async () => { | ||
await fs.writeJson('/project/eas.json', { | ||
@@ -114,10 +134,9 @@ builds: { | ||
release: { workflow: 'managed' }, | ||
debug: { workflow: 'managed', buildType: 'simulator' }, | ||
debug: { workflow: 'managed', buildType: 'development-client' }, | ||
}, | ||
android: { | ||
release: { workflow: 'generic' }, | ||
release: { workflow: 'managed' }, | ||
debug: { | ||
workflow: 'generic', | ||
gradleCommand: ':app:assembleDebug', | ||
withoutCredentials: true, | ||
workflow: 'managed', | ||
buildType: 'development-client', | ||
}, | ||
@@ -134,6 +153,7 @@ }, | ||
credentialsSource: 'auto', | ||
workflow: 'generic', | ||
gradleCommand: ':app:assembleDebug', | ||
withoutCredentials: true, | ||
workflow: 'managed', | ||
distribution: 'store', | ||
env: {}, | ||
image: 'default', | ||
buildType: 'development-client', | ||
}, | ||
@@ -144,2 +164,6 @@ ios: { | ||
distribution: 'store', | ||
autoIncrement: false, | ||
env: {}, | ||
image: 'default', | ||
buildType: 'development-client', | ||
}, | ||
@@ -171,2 +195,4 @@ }, | ||
gradleCommand: ':app:assembleRelease', | ||
env: {}, | ||
image: 'default', | ||
}, | ||
@@ -198,2 +224,4 @@ }, | ||
credentialsSource: 'auto', | ||
env: {}, | ||
image: 'default', | ||
}, | ||
@@ -228,3 +256,5 @@ }, | ||
android: { | ||
release: { workflow: 'generic' }, | ||
release: { | ||
workflow: 'generic', | ||
}, | ||
}, | ||
@@ -269,3 +299,3 @@ }, | ||
await expect(promise).rejects.toThrowError( | ||
'eas.json is not valid [ValidationError: "builds.android.release.workflow" is required]' | ||
'"workflow" key is required in a build profile and has to be one of ["generic", "managed"].' | ||
); | ||
@@ -283,1 +313,17 @@ }); | ||
}); | ||
test('invalid semver value', async () => { | ||
await fs.writeJson('/project/eas.json', { | ||
builds: { | ||
android: { | ||
release: { workflow: 'generic', node: '12.0.0-alpha' }, | ||
}, | ||
}, | ||
}); | ||
const reader = new EasJsonReader('/project', 'android'); | ||
const promise = reader.readAsync('release'); | ||
await expect(promise).rejects.toThrowError( | ||
'Object "android.release" in eas.json is not valid [ValidationError: "node" failed custom validation because 12.0.0-alpha is not a valid version]' | ||
); | ||
}); |
@@ -1,2 +0,2 @@ | ||
import { Android, Workflow } from '@expo/eas-build-job'; | ||
import { Android, Workflow, iOS } from '@expo/eas-build-job'; | ||
@@ -14,3 +14,5 @@ export enum CredentialsSource { | ||
export interface AndroidManagedBuildProfile { | ||
export type VersionAutoIncrement = boolean | 'version' | 'buildNumber'; | ||
export interface AndroidManagedBuildProfile extends Android.BuilderEnvironment { | ||
workflow: Workflow.Managed; | ||
@@ -23,3 +25,3 @@ credentialsSource: CredentialsSource; | ||
export interface AndroidGenericBuildProfile { | ||
export interface AndroidGenericBuildProfile extends Android.BuilderEnvironment { | ||
workflow: Workflow.Generic; | ||
@@ -34,16 +36,20 @@ credentialsSource: CredentialsSource; | ||
export interface iOSManagedBuildProfile { | ||
export interface iOSManagedBuildProfile extends iOS.BuilderEnvironment { | ||
workflow: Workflow.Managed; | ||
credentialsSource: CredentialsSource; | ||
buildType?: iOS.ManagedBuildType; | ||
releaseChannel?: string; | ||
distribution?: DistributionType; | ||
autoIncrement: VersionAutoIncrement; | ||
} | ||
export interface iOSGenericBuildProfile { | ||
export interface iOSGenericBuildProfile extends iOS.BuilderEnvironment { | ||
workflow: Workflow.Generic; | ||
credentialsSource: CredentialsSource; | ||
scheme?: string; | ||
schemeBuildConfiguration?: iOS.GenericSchemeBuildConfiguration; | ||
releaseChannel?: string; | ||
artifactPath?: string; | ||
distribution?: DistributionType; | ||
autoIncrement: VersionAutoIncrement; | ||
} | ||
@@ -50,0 +56,0 @@ |
@@ -16,3 +16,4 @@ import { Platform, Workflow } from '@expo/eas-build-job'; | ||
interface BuildProfilePreValidation { | ||
workflow: Workflow; | ||
workflow?: Workflow; | ||
extends?: string; | ||
} | ||
@@ -31,3 +32,3 @@ | ||
buildProfileName, | ||
easJson.builds?.android?.[buildProfileName] | ||
easJson.builds?.android || {} | ||
); | ||
@@ -40,3 +41,3 @@ } | ||
buildProfileName, | ||
easJson.builds?.ios?.[buildProfileName] | ||
easJson.builds?.ios || {} | ||
); | ||
@@ -55,5 +56,8 @@ } | ||
for (const [name, profile] of Object.entries(easJson.builds?.android ?? {})) { | ||
const androidProfiles = easJson.builds?.android ?? {}; | ||
for (const name of Object.keys(androidProfiles)) { | ||
try { | ||
await this.validateBuildProfile(Platform.Android, name, profile); | ||
if (this.isWorkflowKeySpecified(Platform.Android, name, androidProfiles)) { | ||
await this.validateBuildProfile(Platform.Android, name, androidProfiles); | ||
} | ||
} catch (err) { | ||
@@ -64,5 +68,8 @@ err.msg = `Failed to validate Android build profile "${name}"\n${err.msg}`; | ||
} | ||
for (const [name, profile] of Object.entries(easJson.builds?.ios ?? {})) { | ||
const iosProfiles = easJson.builds?.ios ?? {}; | ||
for (const name of Object.keys(iosProfiles)) { | ||
try { | ||
await this.validateBuildProfile(Platform.iOS, name, profile); | ||
if (this.isWorkflowKeySpecified(Platform.iOS, name, iosProfiles)) { | ||
await this.validateBuildProfile(Platform.iOS, name, iosProfiles); | ||
} | ||
} catch (err) { | ||
@@ -76,3 +83,3 @@ err.msg = `Failed to validate iOS build profile "${name}"\n${err.msg}`; | ||
public async readRawAsync(): Promise<EasJson> { | ||
const rawFile = await fs.readFile(path.join(this.projectDir, 'eas.json'), 'utf-8'); | ||
const rawFile = await fs.readFile(path.join(this.projectDir, 'eas.json'), 'utf8'); | ||
const json = JSON.parse(rawFile); | ||
@@ -93,8 +100,11 @@ | ||
buildProfileName: string, | ||
buildProfile?: BuildProfilePreValidation | ||
buildProfiles: Record<string, BuildProfilePreValidation> | ||
): T { | ||
if (!buildProfile) { | ||
throw new Error(`There is no profile named ${buildProfileName} for platform ${platform}`); | ||
const buildProfile = this.resolveBuildProfile(platform, buildProfileName, buildProfiles); | ||
if (![Workflow.Generic, Workflow.Managed].includes(buildProfile.workflow)) { | ||
throw new Error( | ||
'"workflow" key is required in a build profile and has to be one of ["generic", "managed"].' | ||
); | ||
} | ||
const schema = schemaBuildProfileMap[platform][buildProfile?.workflow]; | ||
const schema = schemaBuildProfileMap[platform][buildProfile.workflow]; | ||
if (!schema) { | ||
@@ -116,2 +126,67 @@ throw new Error('invalid workflow'); // this should be validated earlier | ||
} | ||
private isWorkflowKeySpecified<T extends BuildProfile>( | ||
platform: Platform, | ||
buildProfileName: string, | ||
buildProfiles: Record<string, BuildProfilePreValidation> | ||
): boolean { | ||
const buildProfile = this.resolveBuildProfile(platform, buildProfileName, buildProfiles); | ||
return !!buildProfile.workflow; | ||
} | ||
private resolveBuildProfile( | ||
platform: Platform, | ||
buildProfileName: string, | ||
buildProfiles: Record<string, BuildProfilePreValidation>, | ||
depth: number = 0 | ||
): Record<string, any> { | ||
if (depth >= 2) { | ||
throw new Error( | ||
'Too long chain of build profile extensions, make sure "extends" keys do not make a cycle' | ||
); | ||
} | ||
const buildProfile = buildProfiles[buildProfileName]; | ||
if (!buildProfile) { | ||
throw new Error(`There is no profile named ${buildProfileName} for platform ${platform}`); | ||
} | ||
const baseProfileName = buildProfile.extends; | ||
delete buildProfile.extends; | ||
if (baseProfileName) { | ||
return deepMerge( | ||
this.resolveBuildProfile(platform, baseProfileName, buildProfiles, depth + 1), | ||
buildProfile | ||
); | ||
} else { | ||
return buildProfile; | ||
} | ||
} | ||
} | ||
function isObject(value: any): boolean { | ||
return typeof value === 'object' && value !== null; | ||
} | ||
export function deepMerge( | ||
base: Record<string, any>, | ||
update: Record<string, any> | ||
): Record<string, any> { | ||
const result: Record<string, any> = {}; | ||
Object.keys(base).forEach(key => { | ||
const oldValue = base[key]; | ||
const newValue = update[key]; | ||
if (isObject(newValue) && isObject(oldValue)) { | ||
result[key] = deepMerge(oldValue, newValue); | ||
} else if (newValue !== undefined) { | ||
result[key] = isObject(newValue) ? deepMerge({}, newValue) : newValue; | ||
} else { | ||
result[key] = isObject(oldValue) ? deepMerge({}, oldValue) : oldValue; | ||
} | ||
}); | ||
Object.keys(update).forEach(key => { | ||
const newValue = update[key]; | ||
if (result[key] === undefined) { | ||
result[key] = isObject(newValue) ? deepMerge({}, newValue) : newValue; | ||
} | ||
}); | ||
return result; | ||
} |
@@ -1,3 +0,33 @@ | ||
import Joi from '@hapi/joi'; | ||
import { Android, iOS } from '@expo/eas-build-job'; | ||
import Joi, { CustomHelpers } from '@hapi/joi'; | ||
const semverSchemaCheck = (value: any, helpers: CustomHelpers) => { | ||
if (/^[0-9]+\.[0-9]+\.[0-9]+$/.test(value)) { | ||
return value; | ||
} else { | ||
throw new Error(`${value} is not a valid version`); | ||
} | ||
}; | ||
const AndroidBuilderEnvironmentSchema = Joi.object({ | ||
image: Joi.string() | ||
.valid(...Android.builderBaseImages) | ||
.default('default'), | ||
node: Joi.string().custom(semverSchemaCheck), | ||
yarn: Joi.string().custom(semverSchemaCheck), | ||
ndk: Joi.string(), | ||
env: Joi.object().pattern(Joi.string(), Joi.string()).default({}), | ||
}); | ||
const IosBuilderEnvironmentSchema = Joi.object({ | ||
image: Joi.string() | ||
.valid(...iOS.builderBaseImages) | ||
.default('default'), | ||
node: Joi.string().custom(semverSchemaCheck), | ||
yarn: Joi.string().custom(semverSchemaCheck), | ||
fastlane: Joi.string().custom(semverSchemaCheck), | ||
cocoapods: Joi.string().custom(semverSchemaCheck), | ||
env: Joi.object().pattern(Joi.string(), Joi.string()).default({}), | ||
}); | ||
const AndroidGenericSchema = Joi.object({ | ||
@@ -15,3 +45,3 @@ workflow: Joi.string().valid('generic').required(), | ||
distribution: Joi.string().valid('store', 'internal').default('store'), | ||
}); | ||
}).concat(AndroidBuilderEnvironmentSchema); | ||
@@ -28,3 +58,3 @@ const AndroidManagedSchema = Joi.object({ | ||
distribution: Joi.string().valid('store', 'internal').default('store'), | ||
}); | ||
}).concat(AndroidBuilderEnvironmentSchema); | ||
@@ -35,6 +65,10 @@ const iOSGenericSchema = Joi.object({ | ||
scheme: Joi.string(), | ||
schemeBuildConfiguration: Joi.string().valid('Debug', 'Release'), | ||
releaseChannel: Joi.string(), | ||
artifactPath: Joi.string(), | ||
distribution: Joi.string().valid('store', 'internal').default('store'), | ||
}); | ||
autoIncrement: Joi.alternatives() | ||
.try(Joi.boolean(), Joi.string().valid('version', 'buildNumber')) | ||
.default(false), | ||
}).concat(IosBuilderEnvironmentSchema); | ||
@@ -44,5 +78,9 @@ const iOSManagedSchema = Joi.object({ | ||
credentialsSource: Joi.string().valid('local', 'remote', 'auto').default('auto'), | ||
buildType: Joi.string().valid('release', 'development-client'), | ||
releaseChannel: Joi.string(), | ||
distribution: Joi.string().valid('store', 'internal').default('store'), | ||
}); | ||
autoIncrement: Joi.alternatives() | ||
.try(Joi.boolean(), Joi.string().valid('version', 'buildNumber')) | ||
.default(false), | ||
}).concat(IosBuilderEnvironmentSchema); | ||
@@ -65,3 +103,3 @@ const schemaBuildProfileMap: Record<string, Record<string, Joi.Schema>> = { | ||
Joi.object({ | ||
workflow: Joi.string().valid('generic', 'managed').required(), | ||
workflow: Joi.string().valid('generic', 'managed'), | ||
}).unknown(true) // profile is validated further only if build is for that platform | ||
@@ -72,3 +110,3 @@ ), | ||
Joi.object({ | ||
workflow: Joi.string().valid('generic', 'managed').required(), | ||
workflow: Joi.string().valid('generic', 'managed'), | ||
}).unknown(true) // profile is validated further only if build is for that platform | ||
@@ -75,0 +113,0 @@ ), |
@@ -11,3 +11,4 @@ export { | ||
EasConfig, | ||
VersionAutoIncrement, | ||
} from './Config.types'; | ||
export { EasJsonReader } from './EasJsonReader'; |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
39449
19
1039
0
+ Added@expo/eas-build-job@0.2.10(transitive)
- Removed@expo/eas-build-job@0.1.3(transitive)
Updated@expo/eas-build-job@0.2.10