diff --git a/packages/build-tools/src/ios/credentials/__tests__/manager.test.ios.ts b/packages/build-tools/src/ios/credentials/__tests__/manager.test.ios.ts index 0a711ec2..64408a19 100644 --- a/packages/build-tools/src/ios/credentials/__tests__/manager.test.ios.ts +++ b/packages/build-tools/src/ios/credentials/__tests__/manager.test.ios.ts @@ -19,6 +19,7 @@ const iosCredentials: Ios.BuildCredentials = { dataBase64: '', password: '', }, + provisioningProfileType: Ios.ProvisioningProfileType.MOBILEPROVISION, }, }; @@ -60,6 +61,7 @@ describe(IosCredentialsManager, () => { [targetName]: { distributionCertificate, provisioningProfileBase64: provisioningProfile.dataBase64, + provisioningProfileType: Ios.ProvisioningProfileType.MOBILEPROVISION, }, }, }); diff --git a/packages/build-tools/src/ios/credentials/__tests__/provisioningProfile.test.ios.ts b/packages/build-tools/src/ios/credentials/__tests__/provisioningProfile.test.ios.ts index cbe1a072..1601ba22 100644 --- a/packages/build-tools/src/ios/credentials/__tests__/provisioningProfile.test.ios.ts +++ b/packages/build-tools/src/ios/credentials/__tests__/provisioningProfile.test.ios.ts @@ -34,13 +34,13 @@ describe('ProvisioningProfile class', () => { }); it("shouldn't throw any error if the provisioning profile and distribution certificate match", async () => { - const pp = new ProvisioningProfile( + const pp = new ProvisioningProfile({ ctx, - Buffer.from(provisioningProfile.dataBase64, 'base64'), - keychain.data.path, - 'testapp', - 'Abc 123' - ); + profile: Buffer.from(provisioningProfile.dataBase64, 'base64'), + keychainPath: keychain.data.path, + target: 'testapp', + certificateCommonName: 'Abc 123', + }); try { await pp.init(); expect(() => { @@ -52,13 +52,13 @@ describe('ProvisioningProfile class', () => { }); it("should throw an error if the provisioning profile and distribution certificate don't match", async () => { - const pp = new ProvisioningProfile( + const pp = new ProvisioningProfile({ ctx, - Buffer.from(provisioningProfile.dataBase64, 'base64'), - keychain.data.path, - 'testapp', - 'Abc 123' - ); + profile: Buffer.from(provisioningProfile.dataBase64, 'base64'), + keychainPath: keychain.data.path, + target: 'testapp', + certificateCommonName: 'Abc 123', + }); try { await pp.init(); diff --git a/packages/build-tools/src/ios/credentials/manager.ts b/packages/build-tools/src/ios/credentials/manager.ts index 896d3c21..6ba58f1a 100644 --- a/packages/build-tools/src/ios/credentials/manager.ts +++ b/packages/build-tools/src/ios/credentials/manager.ts @@ -128,13 +128,14 @@ export default class IosCredentialsManager { ); this.ctx.logger.info('Initializing provisioning profile'); - const provisioningProfile = new ProvisioningProfile( - this.ctx, - Buffer.from(targetCredentials.provisioningProfileBase64, 'base64'), - this.keychain.data.path, + const provisioningProfile = new ProvisioningProfile({ + ctx: this.ctx, + profile: Buffer.from(targetCredentials.provisioningProfileBase64, 'base64'), + keychainPath: this.keychain.data.path, target, - certificateCommonName - ); + certificateCommonName, + profileType: targetCredentials.provisioningProfileType, + }); await provisioningProfile.init(); this.ctx.logger.info( diff --git a/packages/build-tools/src/ios/credentials/provisioningProfile.ts b/packages/build-tools/src/ios/credentials/provisioningProfile.ts index 986cd312..800bfa6e 100644 --- a/packages/build-tools/src/ios/credentials/provisioningProfile.ts +++ b/packages/build-tools/src/ios/credentials/provisioningProfile.ts @@ -28,6 +28,16 @@ export enum DistributionType { ENTERPRISE = 'enterprise', } +/** Type enum may not correspond with extension. */ +function getExtensionForType(type: Ios.ProvisioningProfileType): string { + switch (type) { + case Ios.ProvisioningProfileType.MOBILEPROVISION: + return type; + case Ios.ProvisioningProfileType.PROVISIONPROFILE: + return type; + } +} + const PROVISIONING_PROFILES_DIRECTORY = path.join( os.homedir(), 'Library/MobileDevice/Provisioning Profiles' @@ -42,17 +52,40 @@ export default class ProvisioningProfile { } } + private readonly ctx: BuildContext; + private readonly profile: Buffer; + private readonly keychainPath: string; + private readonly target: string; + private readonly certificateCommonName: string; private readonly profilePath: string; + private readonly profileType: Ios.ProvisioningProfileType; private profileData?: ProvisioningProfileData; - constructor( - private readonly ctx: BuildContext, - private readonly profile: Buffer, - private readonly keychainPath: string, - private readonly target: string, - private readonly certificateCommonName: string - ) { - this.profilePath = path.join(PROVISIONING_PROFILES_DIRECTORY, `${uuid()}.mobileprovision`); + constructor({ + ctx, + profile, + keychainPath, + target, + certificateCommonName, + profileType, + }: { + ctx: BuildContext; + profile: Buffer; + keychainPath: string; + target: string; + certificateCommonName: string; + profileType: Ios.ProvisioningProfileType; + }) { + this.ctx = ctx; + this.profile = profile; + this.keychainPath = keychainPath; + this.target = target; + this.certificateCommonName = certificateCommonName; + this.profileType = profileType; + this.profilePath = path.join( + PROVISIONING_PROFILES_DIRECTORY, + `${uuid()}.${getExtensionForType(profileType)}` + ); } public async init(): Promise { @@ -111,7 +144,9 @@ Profile's certificate fingerprint = ${devCertFingerprint}, distribution certific } const applicationIdentifier = (plistData.Entitlements as plist.PlistObject)[ - 'application-identifier' + this.profileType === Ios.ProvisioningProfileType.PROVISIONPROFILE + ? 'com.apple.application-identifier' + : 'application-identifier' ] as string; const bundleIdentifier = applicationIdentifier.replace(/^.+?\./, ''); diff --git a/packages/build-tools/src/steps/functions/configureIosCredentials.ts b/packages/build-tools/src/steps/functions/configureIosCredentials.ts index e82cdb30..f4203c94 100644 --- a/packages/build-tools/src/steps/functions/configureIosCredentials.ts +++ b/packages/build-tools/src/steps/functions/configureIosCredentials.ts @@ -4,7 +4,6 @@ import { BuildFunction, BuildStepInput, BuildStepInputValueTypeName } from '@exp import { Ios } from '@expo/eas-build-job'; import IosCredentialsManager from '../utils/ios/credentials/manager'; -import { IosBuildCredentialsSchema } from '../utils/ios/credentials/credentials'; import { configureCredentialsAsync } from '../utils/ios/configure'; import { resolveBuildConfiguration } from '../utils/ios/resolve'; @@ -28,7 +27,7 @@ export function configureIosCredentialsFunction(): BuildFunction { ], fn: async (stepCtx, { inputs }) => { const rawCredentialsInput = inputs.credentials.value as Record; - const { value, error } = IosBuildCredentialsSchema.validate(rawCredentialsInput, { + const { value, error } = Ios.BuildCredentialsSchema.validate(rawCredentialsInput, { stripUnknown: true, convert: true, abortEarly: false, diff --git a/packages/build-tools/src/steps/functions/configureIosVersion.ts b/packages/build-tools/src/steps/functions/configureIosVersion.ts index f9c0df80..f295e051 100644 --- a/packages/build-tools/src/steps/functions/configureIosVersion.ts +++ b/packages/build-tools/src/steps/functions/configureIosVersion.ts @@ -4,7 +4,6 @@ import { BuildFunction, BuildStepInput, BuildStepInputValueTypeName } from '@exp import { Ios } from '@expo/eas-build-job'; import semver from 'semver'; -import { IosBuildCredentialsSchema } from '../utils/ios/credentials/credentials'; import IosCredentialsManager from '../utils/ios/credentials/manager'; import { updateVersionsAsync } from '../utils/ios/configure'; import { resolveBuildConfiguration } from '../utils/ios/resolve'; @@ -39,7 +38,7 @@ export function configureIosVersionFunction(): BuildFunction { ], fn: async (stepCtx, { inputs }) => { const rawCredentialsInput = inputs.credentials.value as Record; - const { value, error } = IosBuildCredentialsSchema.validate(rawCredentialsInput, { + const { value, error } = Ios.BuildCredentialsSchema.validate(rawCredentialsInput, { stripUnknown: true, convert: true, abortEarly: false, diff --git a/packages/build-tools/src/steps/functions/generateGymfileFromTemplate.ts b/packages/build-tools/src/steps/functions/generateGymfileFromTemplate.ts index b21e5fee..b927670f 100644 --- a/packages/build-tools/src/steps/functions/generateGymfileFromTemplate.ts +++ b/packages/build-tools/src/steps/functions/generateGymfileFromTemplate.ts @@ -11,7 +11,6 @@ import { bunyan } from '@expo/logger'; import templateFile from '@expo/template-file'; import { v4 as uuid } from 'uuid'; -import { IosBuildCredentialsSchema } from '../utils/ios/credentials/credentials'; import IosCredentialsManager, { Credentials } from '../utils/ios/credentials/manager'; import { resolveBuildConfiguration, resolveScheme } from '../utils/ios/resolve'; import { isTVOS } from '../utils/ios/tvos'; @@ -102,7 +101,7 @@ export function generateGymfileFromTemplateFunction(): BuildFunction { let credentials: Credentials | undefined = undefined; const rawCredentialsInput = inputs.credentials.value as Record | undefined; if (rawCredentialsInput) { - const { value, error } = IosBuildCredentialsSchema.validate(rawCredentialsInput, { + const { value, error } = Ios.BuildCredentialsSchema.validate(rawCredentialsInput, { stripUnknown: true, convert: true, abortEarly: false, diff --git a/packages/build-tools/src/steps/functions/resolveAppleTeamIdFromCredentials.ts b/packages/build-tools/src/steps/functions/resolveAppleTeamIdFromCredentials.ts index ab3da789..f2275878 100644 --- a/packages/build-tools/src/steps/functions/resolveAppleTeamIdFromCredentials.ts +++ b/packages/build-tools/src/steps/functions/resolveAppleTeamIdFromCredentials.ts @@ -1,3 +1,4 @@ +import { Ios } from '@expo/eas-build-job'; import { BuildFunction, BuildStepInput, @@ -6,7 +7,6 @@ import { } from '@expo/steps'; import IosCredentialsManager from '../utils/ios/credentials/manager'; -import { IosBuildCredentialsSchema } from '../utils/ios/credentials/credentials'; export function resolveAppleTeamIdFromCredentialsFunction(): BuildFunction { return new BuildFunction({ @@ -29,7 +29,7 @@ export function resolveAppleTeamIdFromCredentialsFunction(): BuildFunction { ], fn: async (stepCtx, { inputs, outputs }) => { const rawCredentialsInput = inputs.credentials.value as Record; - const { value, error } = IosBuildCredentialsSchema.validate(rawCredentialsInput, { + const { value, error } = Ios.BuildCredentialsSchema.validate(rawCredentialsInput, { stripUnknown: true, convert: true, abortEarly: false, diff --git a/packages/build-tools/src/steps/utils/ios/credentials/__tests__/manager.test.ios.ts b/packages/build-tools/src/steps/utils/ios/credentials/__tests__/manager.test.ios.ts index cfc8fb78..ed8a83b3 100644 --- a/packages/build-tools/src/steps/utils/ios/credentials/__tests__/manager.test.ios.ts +++ b/packages/build-tools/src/steps/utils/ios/credentials/__tests__/manager.test.ios.ts @@ -18,6 +18,7 @@ const iosCredentials: Ios.BuildCredentials = { dataBase64: '', password: '', }, + provisioningProfileType: Ios.ProvisioningProfileType.MOBILEPROVISION, }, }; @@ -59,6 +60,7 @@ describe(IosCredentialsManager, () => { [targetName]: { distributionCertificate, provisioningProfileBase64: provisioningProfile.dataBase64, + provisioningProfileType: Ios.ProvisioningProfileType.MOBILEPROVISION, }, }, }); diff --git a/packages/build-tools/src/steps/utils/ios/credentials/__tests__/provisioningProfile.test.ios.ts b/packages/build-tools/src/steps/utils/ios/credentials/__tests__/provisioningProfile.test.ios.ts index 9418df04..f2d0e5c1 100644 --- a/packages/build-tools/src/steps/utils/ios/credentials/__tests__/provisioningProfile.test.ios.ts +++ b/packages/build-tools/src/steps/utils/ios/credentials/__tests__/provisioningProfile.test.ios.ts @@ -1,3 +1,4 @@ +import { Ios } from '@expo/eas-build-job'; import { createLogger } from '@expo/logger'; import Keychain from '../keychain'; @@ -23,12 +24,13 @@ describe('ProvisioningProfile class', () => { }); it("shouldn't throw any error if the provisioning profile and distribution certificate match", async () => { - const pp = new ProvisioningProfile( - Buffer.from(provisioningProfile.dataBase64, 'base64'), - keychain.data.path, - 'testapp', - 'Abc 123' - ); + const pp = new ProvisioningProfile({ + profile: Buffer.from(provisioningProfile.dataBase64, 'base64'), + keychainPath: keychain.data.path, + target: 'testapp', + certificateCommonName: 'Abc 123', + profileType: Ios.ProvisioningProfileType.MOBILEPROVISION, + }); try { await pp.init(mockLogger); expect(() => { @@ -40,12 +42,13 @@ describe('ProvisioningProfile class', () => { }); it("should throw an error if the provisioning profile and distribution certificate don't match", async () => { - const pp = new ProvisioningProfile( - Buffer.from(provisioningProfile.dataBase64, 'base64'), - keychain.data.path, - 'testapp', - 'Abc 123' - ); + const pp = new ProvisioningProfile({ + profile: Buffer.from(provisioningProfile.dataBase64, 'base64'), + keychainPath: keychain.data.path, + target: 'testapp', + certificateCommonName: 'Abc 123', + profileType: Ios.ProvisioningProfileType.MOBILEPROVISION, + }); try { await pp.init(mockLogger); diff --git a/packages/build-tools/src/steps/utils/ios/credentials/credentials.ts b/packages/build-tools/src/steps/utils/ios/credentials/credentials.ts deleted file mode 100644 index 8bf431a8..00000000 --- a/packages/build-tools/src/steps/utils/ios/credentials/credentials.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Ios } from '@expo/eas-build-job'; -import Joi from 'joi'; - -export const TargetCredentialsSchema = Joi.object().keys({ - provisioningProfileBase64: Joi.string().required(), - distributionCertificate: Joi.object({ - dataBase64: Joi.string().required(), - password: Joi.string().allow('').required(), - }).required(), -}); - -export const IosBuildCredentialsSchema = Joi.object().pattern( - Joi.string().required(), - TargetCredentialsSchema -); diff --git a/packages/build-tools/src/steps/utils/ios/credentials/manager.ts b/packages/build-tools/src/steps/utils/ios/credentials/manager.ts index 2a29c2d8..39ce0938 100644 --- a/packages/build-tools/src/steps/utils/ios/credentials/manager.ts +++ b/packages/build-tools/src/steps/utils/ios/credentials/manager.ts @@ -117,12 +117,13 @@ export default class IosCredentialsManager { ); logger.info('Initializing provisioning profile'); - const provisioningProfile = new ProvisioningProfile( - Buffer.from(targetCredentials.provisioningProfileBase64, 'base64'), - this.keychain.data.path, + const provisioningProfile = new ProvisioningProfile({ + profile: Buffer.from(targetCredentials.provisioningProfileBase64, 'base64'), + certificateCommonName, + keychainPath: this.keychain.data.path, target, - certificateCommonName - ); + profileType: targetCredentials.provisioningProfileType, + }); await provisioningProfile.init(logger); logger.info('Validating whether distribution certificate has been imported successfully'); diff --git a/packages/build-tools/src/steps/utils/ios/credentials/provisioningProfile.ts b/packages/build-tools/src/steps/utils/ios/credentials/provisioningProfile.ts index ae49be6c..9ec0fd4c 100644 --- a/packages/build-tools/src/steps/utils/ios/credentials/provisioningProfile.ts +++ b/packages/build-tools/src/steps/utils/ios/credentials/provisioningProfile.ts @@ -2,7 +2,7 @@ import crypto from 'crypto'; import os from 'os'; import path from 'path'; -import { errors } from '@expo/eas-build-job'; +import { Ios, errors } from '@expo/eas-build-job'; import spawn from '@expo/turtle-spawn'; import fs from 'fs-extra'; import plist from 'plist'; @@ -27,6 +27,16 @@ export enum DistributionType { ENTERPRISE = 'enterprise', } +/** Type enum may not correspond with extension. */ +function getExtensionForType(type: Ios.ProvisioningProfileType): string { + switch (type) { + case Ios.ProvisioningProfileType.MOBILEPROVISION: + return type; + case Ios.ProvisioningProfileType.PROVISIONPROFILE: + return type; + } +} + const PROVISIONING_PROFILES_DIRECTORY = path.join( os.homedir(), 'Library/MobileDevice/Provisioning Profiles' @@ -41,16 +51,36 @@ export default class ProvisioningProfile { } } + private readonly profile: Buffer; + private readonly keychainPath: string; + private readonly target: string; + private readonly certificateCommonName: string; private readonly profilePath: string; + private readonly profileType: Ios.ProvisioningProfileType; private profileData?: ProvisioningProfileData; - constructor( - private readonly profile: Buffer, - private readonly keychainPath: string, - private readonly target: string, - private readonly certificateCommonName: string - ) { - this.profilePath = path.join(PROVISIONING_PROFILES_DIRECTORY, `${uuid()}.mobileprovision`); + constructor({ + profile, + keychainPath, + target, + certificateCommonName, + profileType, + }: { + profile: Buffer; + keychainPath: string; + target: string; + certificateCommonName: string; + profileType: Ios.ProvisioningProfileType; + }) { + this.profile = profile; + this.keychainPath = keychainPath; + this.target = target; + this.certificateCommonName = certificateCommonName; + this.profileType = profileType; + this.profilePath = path.join( + PROVISIONING_PROFILES_DIRECTORY, + `${uuid()}.${getExtensionForType(profileType)}` + ); } public async init(logger: bunyan): Promise { @@ -107,7 +137,9 @@ Profile's certificate fingerprint = ${devCertFingerprint}, distribution certific } const applicationIdentifier = (plistData.Entitlements as plist.PlistObject)[ - 'application-identifier' + this.profileType === Ios.ProvisioningProfileType.PROVISIONPROFILE + ? 'com.apple.application-identifier' + : 'application-identifier' ] as string; const bundleIdentifier = applicationIdentifier.replace(/^.+?\./, ''); diff --git a/packages/eas-build-job/src/ios.ts b/packages/eas-build-job/src/ios.ts index a2165c2c..c2e52926 100644 --- a/packages/eas-build-job/src/ios.ts +++ b/packages/eas-build-job/src/ios.ts @@ -17,20 +17,27 @@ import { export type DistributionType = 'store' | 'internal' | 'simulator'; -const TargetCredentialsSchema = Joi.object().keys({ +export enum ProvisioningProfileType { + MOBILEPROVISION = 'mobileprovision', + PROVISIONPROFILE = 'provisionprofile', +} + +export const TargetCredentialsSchema = Joi.object().keys({ provisioningProfileBase64: Joi.string().required(), distributionCertificate: Joi.object({ dataBase64: Joi.string().required(), password: Joi.string().allow('').required(), }).required(), + provisioningProfileType: Joi.allow(Object.values(ProvisioningProfileType)).required(), }); export interface TargetCredentials { provisioningProfileBase64: string; distributionCertificate: DistributionCertificate; + provisioningProfileType: ProvisioningProfileType; } -const BuildCredentialsSchema = Joi.object().pattern( +export const BuildCredentialsSchema = Joi.object().pattern( Joi.string().required(), TargetCredentialsSchema );