From 1f1fd7e75a407412cd42ee43415093ae892e0078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20=E5=B0=8F=E6=9E=97=20Hales?= Date: Wed, 12 May 2021 11:44:15 -0700 Subject: [PATCH 01/28] [eas-build-job] add channel to Job --- packages/eas-build-job/src/android.ts | 2 ++ packages/eas-build-job/src/ios.ts | 2 ++ packages/eas-build-job/src/metadata.ts | 10 ++++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/eas-build-job/src/android.ts b/packages/eas-build-job/src/android.ts index b6bf7e6f..3c17603a 100644 --- a/packages/eas-build-job/src/android.ts +++ b/packages/eas-build-job/src/android.ts @@ -57,6 +57,7 @@ interface BaseJob { platform: Platform.ANDROID; projectRootDirectory: string; releaseChannel?: string; + channel?: string; secrets: { buildCredentials?: { keystore: Keystore; @@ -72,6 +73,7 @@ const BaseJobSchema = Joi.object({ platform: Joi.string().valid(Platform.ANDROID).required(), projectRootDirectory: Joi.string().required(), releaseChannel: Joi.string(), + channel: Joi.string(), secrets: Joi.object({ buildCredentials: Joi.object({ keystore: KeystoreSchema.required() }), environmentSecrets: EnvSchema, diff --git a/packages/eas-build-job/src/ios.ts b/packages/eas-build-job/src/ios.ts index 516414fd..c1d3dc28 100644 --- a/packages/eas-build-job/src/ios.ts +++ b/packages/eas-build-job/src/ios.ts @@ -76,6 +76,7 @@ interface BaseJob { platform: Platform.IOS; projectRootDirectory: string; releaseChannel?: string; + channel?: string; distribution?: DistributionType; secrets: { buildCredentials?: BuildCredentials; @@ -90,6 +91,7 @@ const BaseJobSchema = Joi.object().keys({ platform: Joi.string().valid(Platform.IOS).required(), projectRootDirectory: Joi.string().required(), releaseChannel: Joi.string(), + channel: Joi.string(), distribution: Joi.string().valid('store', 'internal', 'simulator'), secrets: Joi.object({ buildCredentials: BuildCredentialsSchema, diff --git a/packages/eas-build-job/src/metadata.ts b/packages/eas-build-job/src/metadata.ts index 27e5664f..fe363660 100644 --- a/packages/eas-build-job/src/metadata.ts +++ b/packages/eas-build-job/src/metadata.ts @@ -50,11 +50,17 @@ export type Metadata = { sdkVersion?: string; /** - * Release channel (for expo-updates) - * It's undefined if the expo-updates package is not installed for the project. + * Release channel (for classic expo-updates) + * It's undefined if the classic expo-updates package is not installed for the project. */ releaseChannel?: string; + /** + * Channel (for expo-updates) + * It's undefined if the expo-updates package is not installed for the project. + */ + channel?: string; + /** * Distribution type * Indicates whether this is a build for store, internal distribution, or simulator (iOS). From f619f4a16019be15a33b7ff0b5408f604d44ffcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20=E5=B0=8F=E6=9E=97=20Hales?= Date: Wed, 12 May 2021 13:31:14 -0700 Subject: [PATCH 02/28] testse --- packages/eas-build-job/src/__tests__/android.test.ts | 2 ++ packages/eas-build-job/src/__tests__/ios.test.ts | 2 ++ packages/eas-build-job/src/metadata.ts | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/eas-build-job/src/__tests__/android.test.ts b/packages/eas-build-job/src/__tests__/android.test.ts index a3c2af71..2e3f714d 100644 --- a/packages/eas-build-job/src/__tests__/android.test.ts +++ b/packages/eas-build-job/src/__tests__/android.test.ts @@ -34,6 +34,7 @@ describe('Android.GenericJobSchema', () => { artifactPath: 'android/app/build/outputs/bundle/release/app-release.aab', projectRootDirectory: '.', releaseChannel: 'default', + channel: 'main', builderEnvironment: { image: 'default', node: '1.2.3', @@ -86,6 +87,7 @@ describe('Android.ManagedJobSchema', () => { }, projectRootDirectory: '.', releaseChannel: 'default', + channel: 'main', builderEnvironment: { image: 'default', node: '1.2.3', diff --git a/packages/eas-build-job/src/__tests__/ios.test.ts b/packages/eas-build-job/src/__tests__/ios.test.ts index 6e9262c4..43727659 100644 --- a/packages/eas-build-job/src/__tests__/ios.test.ts +++ b/packages/eas-build-job/src/__tests__/ios.test.ts @@ -37,6 +37,7 @@ describe('Ios.GenericJobSchema', () => { buildConfiguration: 'Release', artifactPath: 'ios/build/*.ipa', releaseChannel: 'default', + channel: 'main', builderEnvironment: { image: 'default', node: '1.2.3', @@ -93,6 +94,7 @@ describe('Ios.ManagedJobSchema', () => { buildType: Ios.ManagedBuildType.RELEASE, username: 'turtle-tutorial', releaseChannel: 'default', + channel: 'main', builderEnvironment: { image: 'default', node: '1.2.3', diff --git a/packages/eas-build-job/src/metadata.ts b/packages/eas-build-job/src/metadata.ts index fe363660..0f56b0b4 100644 --- a/packages/eas-build-job/src/metadata.ts +++ b/packages/eas-build-job/src/metadata.ts @@ -56,8 +56,8 @@ export type Metadata = { releaseChannel?: string; /** - * Channel (for expo-updates) - * It's undefined if the expo-updates package is not installed for the project. + * Channel (for Expo Updates when it is configured for for use with EAS) + * It's undefined if the expo-updates package is not configured for use with EAS. */ channel?: string; @@ -105,6 +105,7 @@ export const MetadataSchema = Joi.object({ credentialsSource: Joi.string().valid('local', 'remote'), sdkVersion: Joi.string(), releaseChannel: Joi.string(), + channel: Joi.string(), appName: Joi.string(), appIdentifier: Joi.string(), buildProfile: Joi.string(), From ca1ca13301b44733bbe89d537238982056fb10de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20=E5=B0=8F=E6=9E=97=20Hales?= Date: Wed, 12 May 2021 14:25:26 -0700 Subject: [PATCH 03/28] add oxor --- .../src/__tests__/android.test.ts | 38 +++++++++++++++- .../eas-build-job/src/__tests__/ios.test.ts | 44 ++++++++++++++++++- packages/eas-build-job/src/android.ts | 2 +- packages/eas-build-job/src/ios.ts | 30 +++++++------ 4 files changed, 95 insertions(+), 19 deletions(-) diff --git a/packages/eas-build-job/src/__tests__/android.test.ts b/packages/eas-build-job/src/__tests__/android.test.ts index 2e3f714d..52714a6a 100644 --- a/packages/eas-build-job/src/__tests__/android.test.ts +++ b/packages/eas-build-job/src/__tests__/android.test.ts @@ -34,7 +34,6 @@ describe('Android.GenericJobSchema', () => { artifactPath: 'android/app/build/outputs/bundle/release/app-release.aab', projectRootDirectory: '.', releaseChannel: 'default', - channel: 'main', builderEnvironment: { image: 'default', node: '1.2.3', @@ -87,7 +86,6 @@ describe('Android.ManagedJobSchema', () => { }, projectRootDirectory: '.', releaseChannel: 'default', - channel: 'main', builderEnvironment: { image: 'default', node: '1.2.3', @@ -125,4 +123,40 @@ describe('Android.ManagedJobSchema', () => { ); expect(value).not.toMatchObject(managedJob); }); + test('validates channel', () => { + const managedJob = { + secrets, + type: Workflow.MANAGED, + platform: Platform.ANDROID, + channel: 'main', + projectArchive: { + type: ArchiveSourceType.URL, + url: 'http://localhost:3000', + }, + projectRootDirectory: '.', + }; + + const { value, error } = Android.ManagedJobSchema.validate(managedJob, joiOptions); + expect(value).toMatchObject(managedJob); + expect(error).toBeFalsy(); + }); + test('fails when both releaseChannel and channel are defined.', () => { + const managedJob = { + secrets, + type: Workflow.MANAGED, + platform: Platform.ANDROID, + releaseChannel: 'default', + channel: 'main', + projectArchive: { + type: ArchiveSourceType.URL, + url: 'http://localhost:3000', + }, + projectRootDirectory: '.', + }; + + const { error } = Android.ManagedJobSchema.validate(managedJob, joiOptions); + expect(error?.message).toBe( + '"value" contains a conflict between optional exclusive peers [releaseChannel, channel]' + ); + }); }); diff --git a/packages/eas-build-job/src/__tests__/ios.test.ts b/packages/eas-build-job/src/__tests__/ios.test.ts index 43727659..35a6056f 100644 --- a/packages/eas-build-job/src/__tests__/ios.test.ts +++ b/packages/eas-build-job/src/__tests__/ios.test.ts @@ -37,7 +37,6 @@ describe('Ios.GenericJobSchema', () => { buildConfiguration: 'Release', artifactPath: 'ios/build/*.ipa', releaseChannel: 'default', - channel: 'main', builderEnvironment: { image: 'default', node: '1.2.3', @@ -94,7 +93,6 @@ describe('Ios.ManagedJobSchema', () => { buildType: Ios.ManagedBuildType.RELEASE, username: 'turtle-tutorial', releaseChannel: 'default', - channel: 'main', builderEnvironment: { image: 'default', node: '1.2.3', @@ -134,4 +132,46 @@ describe('Ios.ManagedJobSchema', () => { ); expect(value).not.toMatchObject(managedJob); }); + test('validates channel', () => { + const managedJob = { + secrets: { + buildCredentials, + }, + type: Workflow.MANAGED, + platform: Platform.IOS, + channel: 'main', + projectArchive: { + type: ArchiveSourceType.URL, + url: 'http://localhost:3000', + }, + projectRootDirectory: '.', + distribution: 'store', + }; + + const { value, error } = Ios.ManagedJobSchema.validate(managedJob, joiOptions); + expect(value).toMatchObject(managedJob); + expect(error).toBeFalsy(); + }); + test('fails when both releaseChannel and channel are defined.', () => { + const managedJob = { + secrets: { + buildCredentials, + }, + type: Workflow.MANAGED, + platform: Platform.IOS, + releaseChannel: 'default', + channel: 'main', + projectArchive: { + type: ArchiveSourceType.URL, + url: 'http://localhost:3000', + }, + projectRootDirectory: '.', + distribution: 'store', + }; + + const { error } = Ios.ManagedJobSchema.validate(managedJob, joiOptions); + expect(error?.message).toBe( + '"value" contains a conflict between optional exclusive peers [releaseChannel, channel]' + ); + }); }); diff --git a/packages/eas-build-job/src/android.ts b/packages/eas-build-job/src/android.ts index 3c17603a..a8b927c4 100644 --- a/packages/eas-build-job/src/android.ts +++ b/packages/eas-build-job/src/android.ts @@ -80,7 +80,7 @@ const BaseJobSchema = Joi.object({ }).required(), builderEnvironment: BuilderEnvironmentSchema, cache: CacheSchema.default(), -}); +}).oxor('releaseChannel', 'channel'); export interface GenericJob extends BaseJob { type: Workflow.GENERIC; diff --git a/packages/eas-build-job/src/ios.ts b/packages/eas-build-job/src/ios.ts index c1d3dc28..981d45a7 100644 --- a/packages/eas-build-job/src/ios.ts +++ b/packages/eas-build-job/src/ios.ts @@ -86,20 +86,22 @@ interface BaseJob { cache: Cache; } -const BaseJobSchema = Joi.object().keys({ - projectArchive: ArchiveSourceSchema.required(), - platform: Joi.string().valid(Platform.IOS).required(), - projectRootDirectory: Joi.string().required(), - releaseChannel: Joi.string(), - channel: Joi.string(), - distribution: Joi.string().valid('store', 'internal', 'simulator'), - secrets: Joi.object({ - buildCredentials: BuildCredentialsSchema, - environmentSecrets: EnvSchema, - }).required(), - builderEnvironment: BuilderEnvironmentSchema, - cache: CacheSchema.default(), -}); +const BaseJobSchema = Joi.object() + .keys({ + projectArchive: ArchiveSourceSchema.required(), + platform: Joi.string().valid(Platform.IOS).required(), + projectRootDirectory: Joi.string().required(), + releaseChannel: Joi.string(), + channel: Joi.string(), + distribution: Joi.string().valid('store', 'internal', 'simulator'), + secrets: Joi.object({ + buildCredentials: BuildCredentialsSchema, + environmentSecrets: EnvSchema, + }).required(), + builderEnvironment: BuilderEnvironmentSchema, + cache: CacheSchema.default(), + }) + .oxor('releaseChannel', 'channel'); export interface GenericJob extends BaseJob { type: Workflow.GENERIC; From 912ec74132391481358f8fb5b62aa935c7f3b00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20=E5=B0=8F=E6=9E=97=20Hales?= Date: Thu, 13 May 2021 05:57:44 -0700 Subject: [PATCH 04/28] refactor to umbrella 'updates' object --- packages/eas-build-job/src/__tests__/android.test.ts | 10 +++++++--- packages/eas-build-job/src/__tests__/ios.test.ts | 10 +++++++--- packages/eas-build-job/src/android.ts | 10 +++++++--- packages/eas-build-job/src/ios.ts | 10 +++++++--- packages/eas-build-job/src/metadata.ts | 11 ++++++++--- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/packages/eas-build-job/src/__tests__/android.test.ts b/packages/eas-build-job/src/__tests__/android.test.ts index 52714a6a..91000042 100644 --- a/packages/eas-build-job/src/__tests__/android.test.ts +++ b/packages/eas-build-job/src/__tests__/android.test.ts @@ -128,7 +128,9 @@ describe('Android.ManagedJobSchema', () => { secrets, type: Workflow.MANAGED, platform: Platform.ANDROID, - channel: 'main', + updates: { + channel: 'main', + }, projectArchive: { type: ArchiveSourceType.URL, url: 'http://localhost:3000', @@ -146,7 +148,9 @@ describe('Android.ManagedJobSchema', () => { type: Workflow.MANAGED, platform: Platform.ANDROID, releaseChannel: 'default', - channel: 'main', + updates: { + channel: 'main', + }, projectArchive: { type: ArchiveSourceType.URL, url: 'http://localhost:3000', @@ -156,7 +160,7 @@ describe('Android.ManagedJobSchema', () => { const { error } = Android.ManagedJobSchema.validate(managedJob, joiOptions); expect(error?.message).toBe( - '"value" contains a conflict between optional exclusive peers [releaseChannel, channel]' + '\"value\" contains a conflict between optional exclusive peers [releaseChannel, updates.channel]' ); }); }); diff --git a/packages/eas-build-job/src/__tests__/ios.test.ts b/packages/eas-build-job/src/__tests__/ios.test.ts index 35a6056f..682e9ce4 100644 --- a/packages/eas-build-job/src/__tests__/ios.test.ts +++ b/packages/eas-build-job/src/__tests__/ios.test.ts @@ -139,7 +139,9 @@ describe('Ios.ManagedJobSchema', () => { }, type: Workflow.MANAGED, platform: Platform.IOS, - channel: 'main', + updates: { + channel: 'main', + }, projectArchive: { type: ArchiveSourceType.URL, url: 'http://localhost:3000', @@ -160,7 +162,9 @@ describe('Ios.ManagedJobSchema', () => { type: Workflow.MANAGED, platform: Platform.IOS, releaseChannel: 'default', - channel: 'main', + updates: { + channel: 'main', + }, projectArchive: { type: ArchiveSourceType.URL, url: 'http://localhost:3000', @@ -171,7 +175,7 @@ describe('Ios.ManagedJobSchema', () => { const { error } = Ios.ManagedJobSchema.validate(managedJob, joiOptions); expect(error?.message).toBe( - '"value" contains a conflict between optional exclusive peers [releaseChannel, channel]' + '"value" contains a conflict between optional exclusive peers [releaseChannel, updates.channel]' ); }); }); diff --git a/packages/eas-build-job/src/android.ts b/packages/eas-build-job/src/android.ts index a8b927c4..fe4eec40 100644 --- a/packages/eas-build-job/src/android.ts +++ b/packages/eas-build-job/src/android.ts @@ -57,7 +57,9 @@ interface BaseJob { platform: Platform.ANDROID; projectRootDirectory: string; releaseChannel?: string; - channel?: string; + updates?: { + channel?: string; + }; secrets: { buildCredentials?: { keystore: Keystore; @@ -73,14 +75,16 @@ const BaseJobSchema = Joi.object({ platform: Joi.string().valid(Platform.ANDROID).required(), projectRootDirectory: Joi.string().required(), releaseChannel: Joi.string(), - channel: Joi.string(), + updates: Joi.object({ + channel: Joi.string(), + }), secrets: Joi.object({ buildCredentials: Joi.object({ keystore: KeystoreSchema.required() }), environmentSecrets: EnvSchema, }).required(), builderEnvironment: BuilderEnvironmentSchema, cache: CacheSchema.default(), -}).oxor('releaseChannel', 'channel'); +}).oxor('releaseChannel', 'updates.channel'); export interface GenericJob extends BaseJob { type: Workflow.GENERIC; diff --git a/packages/eas-build-job/src/ios.ts b/packages/eas-build-job/src/ios.ts index 981d45a7..0031bb90 100644 --- a/packages/eas-build-job/src/ios.ts +++ b/packages/eas-build-job/src/ios.ts @@ -76,7 +76,9 @@ interface BaseJob { platform: Platform.IOS; projectRootDirectory: string; releaseChannel?: string; - channel?: string; + updates?: { + channel?: string; + }; distribution?: DistributionType; secrets: { buildCredentials?: BuildCredentials; @@ -92,7 +94,9 @@ const BaseJobSchema = Joi.object() platform: Joi.string().valid(Platform.IOS).required(), projectRootDirectory: Joi.string().required(), releaseChannel: Joi.string(), - channel: Joi.string(), + updates: Joi.object({ + channel: Joi.string(), + }), distribution: Joi.string().valid('store', 'internal', 'simulator'), secrets: Joi.object({ buildCredentials: BuildCredentialsSchema, @@ -101,7 +105,7 @@ const BaseJobSchema = Joi.object() builderEnvironment: BuilderEnvironmentSchema, cache: CacheSchema.default(), }) - .oxor('releaseChannel', 'channel'); + .oxor('releaseChannel', 'updates.channel'); export interface GenericJob extends BaseJob { type: Workflow.GENERIC; diff --git a/packages/eas-build-job/src/metadata.ts b/packages/eas-build-job/src/metadata.ts index 0f56b0b4..87fb5f6e 100644 --- a/packages/eas-build-job/src/metadata.ts +++ b/packages/eas-build-job/src/metadata.ts @@ -56,10 +56,15 @@ export type Metadata = { releaseChannel?: string; /** - * Channel (for Expo Updates when it is configured for for use with EAS) - * It's undefined if the expo-updates package is not configured for use with EAS. + * Configuration for Expo Updates */ - channel?: string; + updates?: { + /** + * Channel (for Expo Updates when it is configured for for use with EAS) + * It's undefined if the expo-updates package is not configured for use with EAS. + */ + channel?: string; + }; /** * Distribution type From b22b560dbf0ff0c2046629b8ad872f3f2b8205d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20=E5=B0=8F=E6=9E=97=20Hales?= Date: Thu, 13 May 2021 06:00:37 -0700 Subject: [PATCH 05/28] fix lint --- packages/eas-build-job/src/__tests__/android.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eas-build-job/src/__tests__/android.test.ts b/packages/eas-build-job/src/__tests__/android.test.ts index 91000042..99a38973 100644 --- a/packages/eas-build-job/src/__tests__/android.test.ts +++ b/packages/eas-build-job/src/__tests__/android.test.ts @@ -160,7 +160,7 @@ describe('Android.ManagedJobSchema', () => { const { error } = Android.ManagedJobSchema.validate(managedJob, joiOptions); expect(error?.message).toBe( - '\"value\" contains a conflict between optional exclusive peers [releaseChannel, updates.channel]' + '"value" contains a conflict between optional exclusive peers [releaseChannel, updates.channel]' ); }); }); From 17951571f1044bdf6e7243073192e08d8c25649b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20=E5=B0=8F=E6=9E=97=20Hales?= Date: Thu, 13 May 2021 06:02:09 -0700 Subject: [PATCH 06/28] metadata schema --- packages/eas-build-job/src/metadata.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/eas-build-job/src/metadata.ts b/packages/eas-build-job/src/metadata.ts index 87fb5f6e..67dac425 100644 --- a/packages/eas-build-job/src/metadata.ts +++ b/packages/eas-build-job/src/metadata.ts @@ -110,10 +110,12 @@ export const MetadataSchema = Joi.object({ credentialsSource: Joi.string().valid('local', 'remote'), sdkVersion: Joi.string(), releaseChannel: Joi.string(), - channel: Joi.string(), + updates: Joi.object({ + channel: Joi.string(), + }), appName: Joi.string(), appIdentifier: Joi.string(), buildProfile: Joi.string(), gitCommitHash: Joi.string().length(40).hex(), username: Joi.string(), -}); +}).oxor('releaseChannel', 'updates.channel'); From d95347a1ad650f0dd76e75b74bb3444dbd3f833e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20=E5=B0=8F=E6=9E=97=20Hales?= Date: Thu, 13 May 2021 06:08:22 -0700 Subject: [PATCH 07/28] metadata test --- .../src/__tests__/android.test.ts | 2 +- .../eas-build-job/src/__tests__/ios.test.ts | 2 +- .../src/__tests__/metadata.test.ts | 26 +++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/eas-build-job/src/__tests__/android.test.ts b/packages/eas-build-job/src/__tests__/android.test.ts index 99a38973..8eba5c7d 100644 --- a/packages/eas-build-job/src/__tests__/android.test.ts +++ b/packages/eas-build-job/src/__tests__/android.test.ts @@ -142,7 +142,7 @@ describe('Android.ManagedJobSchema', () => { expect(value).toMatchObject(managedJob); expect(error).toBeFalsy(); }); - test('fails when both releaseChannel and channel are defined.', () => { + test('fails when both releaseChannel and updates.channel are defined.', () => { const managedJob = { secrets, type: Workflow.MANAGED, diff --git a/packages/eas-build-job/src/__tests__/ios.test.ts b/packages/eas-build-job/src/__tests__/ios.test.ts index 682e9ce4..d2f74f28 100644 --- a/packages/eas-build-job/src/__tests__/ios.test.ts +++ b/packages/eas-build-job/src/__tests__/ios.test.ts @@ -154,7 +154,7 @@ describe('Ios.ManagedJobSchema', () => { expect(value).toMatchObject(managedJob); expect(error).toBeFalsy(); }); - test('fails when both releaseChannel and channel are defined.', () => { + test('fails when both releaseChannel and updates.channel are defined.', () => { const managedJob = { secrets: { buildCredentials, diff --git a/packages/eas-build-job/src/__tests__/metadata.test.ts b/packages/eas-build-job/src/__tests__/metadata.test.ts index 7e77e2fa..b5a03842 100644 --- a/packages/eas-build-job/src/__tests__/metadata.test.ts +++ b/packages/eas-build-job/src/__tests__/metadata.test.ts @@ -46,4 +46,30 @@ describe('MetadataSchema', () => { '"credentialsSource" must be one of [local, remote]. "gitCommitHash" length must be 40 characters long. "gitCommitHash" must only contain hexadecimal characters' ); }); + test('fails when both releaseChannel and updates.channel are defined.', () => { + const metadata = { + appName: 'testapp', + appVersion: '1.0.0', + cliVersion: '1.2.3', + buildProfile: 'release', + credentialsSource: 'remote', + distribution: 'store', + gitCommitHash: '752e99d2b8fde1bf07ebb8af1b4a3c26a6703943', + trackingContext: {}, + workflow: 'generic', + username: 'definitelynotdominik', + releaseChannel: 'default', + updates: { + channel: 'main', + }, + }; + const { error } = MetadataSchema.validate(metadata, { + stripUnknown: true, + convert: true, + abortEarly: false, + }); + expect(error?.message).toBe( + '"value" contains a conflict between optional exclusive peers [releaseChannel, updates.channel]' + ); + }); }); From d41e809fe16b198555047faf5ca9a62547c2bfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20=E5=B0=8F=E6=9E=97=20Hales?= Date: Thu, 13 May 2021 07:44:39 -0700 Subject: [PATCH 08/28] remove nesting and validation from metadata --- .../src/__tests__/metadata.test.ts | 26 ------------------- packages/eas-build-job/src/metadata.ts | 17 ++++-------- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/packages/eas-build-job/src/__tests__/metadata.test.ts b/packages/eas-build-job/src/__tests__/metadata.test.ts index b5a03842..7e77e2fa 100644 --- a/packages/eas-build-job/src/__tests__/metadata.test.ts +++ b/packages/eas-build-job/src/__tests__/metadata.test.ts @@ -46,30 +46,4 @@ describe('MetadataSchema', () => { '"credentialsSource" must be one of [local, remote]. "gitCommitHash" length must be 40 characters long. "gitCommitHash" must only contain hexadecimal characters' ); }); - test('fails when both releaseChannel and updates.channel are defined.', () => { - const metadata = { - appName: 'testapp', - appVersion: '1.0.0', - cliVersion: '1.2.3', - buildProfile: 'release', - credentialsSource: 'remote', - distribution: 'store', - gitCommitHash: '752e99d2b8fde1bf07ebb8af1b4a3c26a6703943', - trackingContext: {}, - workflow: 'generic', - username: 'definitelynotdominik', - releaseChannel: 'default', - updates: { - channel: 'main', - }, - }; - const { error } = MetadataSchema.validate(metadata, { - stripUnknown: true, - convert: true, - abortEarly: false, - }); - expect(error?.message).toBe( - '"value" contains a conflict between optional exclusive peers [releaseChannel, updates.channel]' - ); - }); }); diff --git a/packages/eas-build-job/src/metadata.ts b/packages/eas-build-job/src/metadata.ts index 67dac425..0f56b0b4 100644 --- a/packages/eas-build-job/src/metadata.ts +++ b/packages/eas-build-job/src/metadata.ts @@ -56,15 +56,10 @@ export type Metadata = { releaseChannel?: string; /** - * Configuration for Expo Updates + * Channel (for Expo Updates when it is configured for for use with EAS) + * It's undefined if the expo-updates package is not configured for use with EAS. */ - updates?: { - /** - * Channel (for Expo Updates when it is configured for for use with EAS) - * It's undefined if the expo-updates package is not configured for use with EAS. - */ - channel?: string; - }; + channel?: string; /** * Distribution type @@ -110,12 +105,10 @@ export const MetadataSchema = Joi.object({ credentialsSource: Joi.string().valid('local', 'remote'), sdkVersion: Joi.string(), releaseChannel: Joi.string(), - updates: Joi.object({ - channel: Joi.string(), - }), + channel: Joi.string(), appName: Joi.string(), appIdentifier: Joi.string(), buildProfile: Joi.string(), gitCommitHash: Joi.string().length(40).hex(), username: Joi.string(), -}).oxor('releaseChannel', 'updates.channel'); +}); From d4f041d254e38e44418b71c2d9dfc05d1d982d26 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 20 May 2021 09:34:05 -0700 Subject: [PATCH 09/28] add native mutations --- packages/build-tools/src/__mocks__/fs.ts | 3 + .../{releaseChannel.ts => expoUpdates.ts} | 38 +- .../src/builders/androidGeneric.ts | 8 +- .../src/builders/androidManaged.ts | 7 +- .../build-tools/src/builders/iosGeneric.ts | 7 +- .../build-tools/src/builders/iosManaged.ts | 10 +- .../build-tools/src/generic/expoUpdates.ts | 36 -- .../ios/{releaseChannel.ts => expoUpdates.ts} | 55 ++- .../build-tools/src/managed/expoUpdates.ts | 23 -- .../src/utils/__tests__/expoUpdates.test.ts | 332 ++++++++++++++++++ packages/build-tools/src/utils/expoUpdates.ts | 258 ++++++++++++++ packages/local-build-plugin/src/eject.ts | 8 + packages/local-build-plugin/src/ios.ts | 6 + 13 files changed, 686 insertions(+), 105 deletions(-) rename packages/build-tools/src/android/{releaseChannel.ts => expoUpdates.ts} (65%) delete mode 100644 packages/build-tools/src/generic/expoUpdates.ts rename packages/build-tools/src/ios/{releaseChannel.ts => expoUpdates.ts} (56%) delete mode 100644 packages/build-tools/src/managed/expoUpdates.ts create mode 100644 packages/build-tools/src/utils/__tests__/expoUpdates.test.ts create mode 100644 packages/build-tools/src/utils/expoUpdates.ts diff --git a/packages/build-tools/src/__mocks__/fs.ts b/packages/build-tools/src/__mocks__/fs.ts index 7b6f1561..8314c390 100644 --- a/packages/build-tools/src/__mocks__/fs.ts +++ b/packages/build-tools/src/__mocks__/fs.ts @@ -3,5 +3,8 @@ import { fs } from 'memfs'; // `temp-dir` dependency of `tempy` is using `fs.realpathSync('/tmp')` // on import to verify existence of tmp directory fs.mkdirSync('/tmp'); +if (process.env.TMPDIR) { + fs.mkdirSync(process.env.TMPDIR, { recursive: true }); +} module.exports = fs; diff --git a/packages/build-tools/src/android/releaseChannel.ts b/packages/build-tools/src/android/expoUpdates.ts similarity index 65% rename from packages/build-tools/src/android/releaseChannel.ts rename to packages/build-tools/src/android/expoUpdates.ts index d171b4c9..ac795a31 100644 --- a/packages/build-tools/src/android/releaseChannel.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -3,12 +3,20 @@ import path from 'path'; import * as xml from 'xml2js'; import fs from 'fs-extra'; -const RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL'; +export enum AndroidMetadataName { + UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY', + RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL', +} -async function updateReleaseChannel( - reactNativeProjectDirectory: string, - releaseChannel: string -): Promise { +export async function setAndroidMetadataEntryAsync({ + reactNativeProjectDirectory, + androidMetadataValue, + androidMetadataName, +}: { + reactNativeProjectDirectory: string; + androidMetadataValue: string; + androidMetadataName: AndroidMetadataName; +}): Promise { const manifestPath = path.join( reactNativeProjectDirectory, 'android', @@ -35,18 +43,18 @@ async function updateReleaseChannel( const newItem = { $: { - 'android:name': RELEASE_CHANNEL, - 'android:value': releaseChannel, + 'android:name': androidMetadataName, + 'android:value': androidMetadataValue, }, }; if (mainApplication['meta-data']) { const existingMetaDataItem = mainApplication['meta-data'].find( - (e: any) => e.$['android:name'] === RELEASE_CHANNEL + (e: any) => e.$['android:name'] === androidMetadataName ); if (existingMetaDataItem) { - existingMetaDataItem.$['android:value'] = releaseChannel; + existingMetaDataItem.$['android:value'] = androidMetadataValue; } else { mainApplication['meta-data'].push(newItem); } @@ -58,7 +66,13 @@ async function updateReleaseChannel( await fs.writeFile(manifestPath, manifestXml); } -async function getReleaseChannel(reactNativeProjectDirectory: string): Promise { +export async function getAndroidMetadataEntryAsync({ + reactNativeProjectDirectory, + androidMetadataName, +}: { + reactNativeProjectDirectory: string; + androidMetadataName: AndroidMetadataName; +}): Promise { const manifestPath = path.join( reactNativeProjectDirectory, 'android', @@ -83,9 +97,7 @@ async function getReleaseChannel(reactNativeProjectDirectory: string): Promise e.$['android:name'] === RELEASE_CHANNEL + (e: any) => e.$['android:name'] === androidMetadataName ); return existingMetaDataItem?.$?.['android:value']; } - -export { getReleaseChannel, updateReleaseChannel }; diff --git a/packages/build-tools/src/builders/androidGeneric.ts b/packages/build-tools/src/builders/androidGeneric.ts index f196b501..a670c227 100644 --- a/packages/build-tools/src/builders/androidGeneric.ts +++ b/packages/build-tools/src/builders/androidGeneric.ts @@ -1,13 +1,12 @@ -import { Android, BuildPhase } from '@expo/eas-build-job'; +import { Android, BuildPhase, Platform } from '@expo/eas-build-job'; import { BuildContext } from '../context'; import { setup } from '../utils/project'; import { findBuildArtifacts } from '../utils/buildArtifacts'; import { Hook, runHookIfPresent } from '../utils/hooks'; -import { getReleaseChannel, updateReleaseChannel } from '../android/releaseChannel'; import { restoreCredentials } from '../android/credentials'; import { runGradleCommand, ensureLFLineEndingsInGradlewScript } from '../android/gradle'; -import { configureExpoUpdatesIfInstalled } from '../generic/expoUpdates'; +import { configureExpoUpdatesIfInstalledAsync } from '../utils/expoUpdates'; export default async function androidGenericBuilder( ctx: BuildContext @@ -33,7 +32,8 @@ export default async function androidGenericBuilder( } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - await configureExpoUpdatesIfInstalled(ctx, { getReleaseChannel, updateReleaseChannel }); + console.log('In androidGeneric, starting configureExpoUpdatesIfInstalled'); + await configureExpoUpdatesIfInstalledAsync(ctx, Platform.ANDROID); }); await ctx.runBuildPhase(BuildPhase.RUN_GRADLEW, async () => { diff --git a/packages/build-tools/src/builders/androidManaged.ts b/packages/build-tools/src/builders/androidManaged.ts index 5cd146fd..bec3bdf5 100644 --- a/packages/build-tools/src/builders/androidManaged.ts +++ b/packages/build-tools/src/builders/androidManaged.ts @@ -1,12 +1,11 @@ import { AndroidConfig } from '@expo/config-plugins'; -import { Android, BuildPhase } from '@expo/eas-build-job'; +import { Android, BuildPhase, Platform } from '@expo/eas-build-job'; import { ManagedBuildContext } from '../managed/context'; -import { configureExpoUpdatesIfInstalled } from '../managed/expoUpdates'; +import { configureExpoUpdatesIfInstalledAsync } from '../utils/expoUpdates'; import { setup } from '../utils/project'; import { findSingleBuildArtifact } from '../utils/buildArtifacts'; import { Hook, runHookIfPresent } from '../utils/hooks'; -import { updateReleaseChannel } from '../android/releaseChannel'; import { restoreCredentials } from '../android/credentials'; import { runGradleCommand } from '../android/gradle'; @@ -33,7 +32,7 @@ export default async function androidManagedBuilder( }); } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - await configureExpoUpdatesIfInstalled(ctx, updateReleaseChannel); + await configureExpoUpdatesIfInstalledAsync(ctx, Platform.ANDROID); }); await ctx.runBuildPhase(BuildPhase.RUN_GRADLEW, async () => { diff --git a/packages/build-tools/src/builders/iosGeneric.ts b/packages/build-tools/src/builders/iosGeneric.ts index 6097fbca..48b50419 100644 --- a/packages/build-tools/src/builders/iosGeneric.ts +++ b/packages/build-tools/src/builders/iosGeneric.ts @@ -1,15 +1,14 @@ -import { BuildPhase, Ios } from '@expo/eas-build-job'; +import { BuildPhase, Ios, Platform } from '@expo/eas-build-job'; import { BuildContext } from '../context'; import { setup } from '../utils/project'; import { findBuildArtifacts } from '../utils/buildArtifacts'; import { Hook, runHookIfPresent } from '../utils/hooks'; -import { updateReleaseChannel, getReleaseChannel } from '../ios/releaseChannel'; import CredentialsManager from '../ios/credentials/manager'; import { configureXcodeProject } from '../ios/configure'; import { runFastlaneGym } from '../ios/fastlane'; import { installPods } from '../ios/pod'; -import { configureExpoUpdatesIfInstalled } from '../generic/expoUpdates'; +import { configureExpoUpdatesIfInstalledAsync } from '../utils/expoUpdates'; export default async function iosGenericBuilder( ctx: BuildContext @@ -40,7 +39,7 @@ export default async function iosGenericBuilder( } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - await configureExpoUpdatesIfInstalled(ctx, { getReleaseChannel, updateReleaseChannel }); + await configureExpoUpdatesIfInstalledAsync(ctx, Platform.IOS); }); await ctx.runBuildPhase(BuildPhase.RUN_FASTLANE, async () => { diff --git a/packages/build-tools/src/builders/iosManaged.ts b/packages/build-tools/src/builders/iosManaged.ts index 470cf2a9..0e5a979a 100644 --- a/packages/build-tools/src/builders/iosManaged.ts +++ b/packages/build-tools/src/builders/iosManaged.ts @@ -1,14 +1,13 @@ import assert from 'assert'; import { IOSConfig } from '@expo/config-plugins'; -import { BuildPhase, Ios } from '@expo/eas-build-job'; +import { BuildPhase, Ios, Platform } from '@expo/eas-build-job'; import { ManagedBuildContext } from '../managed/context'; -import { configureExpoUpdatesIfInstalled } from '../managed/expoUpdates'; +import { configureExpoUpdatesIfInstalledAsync } from '../utils/expoUpdates'; import { setup } from '../utils/project'; import { findSingleBuildArtifact } from '../utils/buildArtifacts'; import { Hook, runHookIfPresent } from '../utils/hooks'; -import { updateReleaseChannel } from '../ios/releaseChannel'; import { configureXcodeProject } from '../ios/configure'; import CredentialsManager from '../ios/credentials/manager'; import { runFastlaneGym } from '../ios/fastlane'; @@ -17,6 +16,9 @@ import { installPods } from '../ios/pod'; export default async function iosManagedBuilder( ctx: ManagedBuildContext ): Promise { + console.log('STARTING IOS MANAGED'); + console.log('STARTING IOS MANAGED'); + console.log('STARTING IOS MANAGED'); await setup(ctx); await ctx.runBuildPhase(BuildPhase.PREBUILD, async () => { @@ -47,7 +49,7 @@ export default async function iosManagedBuilder( } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - await configureExpoUpdatesIfInstalled(ctx, updateReleaseChannel); + await configureExpoUpdatesIfInstalledAsync(ctx, Platform.IOS); }); await ctx.runBuildPhase(BuildPhase.RUN_FASTLANE, async () => { diff --git a/packages/build-tools/src/generic/expoUpdates.ts b/packages/build-tools/src/generic/expoUpdates.ts deleted file mode 100644 index 419e6d39..00000000 --- a/packages/build-tools/src/generic/expoUpdates.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Ios, Android, Platform } from '@expo/eas-build-job'; - -import { BuildContext } from '../context'; -import isExpoUpdatesInstalledAsync from '../utils/isExpoUpdatesInstalled'; - -type GenericJob = Ios.GenericJob | Android.GenericJob; - -export async function configureExpoUpdatesIfInstalled( - ctx: BuildContext, - { - getReleaseChannel, - updateReleaseChannel, - }: { - getReleaseChannel: (dir: string) => Promise; - updateReleaseChannel: (dir: string, releaseChannel: string) => Promise; - } -): Promise { - if (await isExpoUpdatesInstalledAsync(ctx.reactNativeProjectDirectory)) { - if (ctx.job.releaseChannel) { - const configFile = - ctx.job.platform === Platform.ANDROID ? 'AndroidManifest.xml' : 'Expo.plist'; - ctx.logger.info( - `Setting the release channel in '${configFile}' to '${ctx.job.releaseChannel}'` - ); - await updateReleaseChannel(ctx.reactNativeProjectDirectory, ctx.job.releaseChannel); - } else { - const channel = await getReleaseChannel(ctx.reactNativeProjectDirectory); - if (!channel || channel === 'default') { - ctx.logger.info(`Using default release channel for 'expo-updates' (default)`); - } else { - ctx.logger.info(`Using the release channel pre-configured in native project (${channel})`); - ctx.logger.warn('Please add the "releaseChannel" field to your build profile (eas.json)'); - } - } - } -} diff --git a/packages/build-tools/src/ios/releaseChannel.ts b/packages/build-tools/src/ios/expoUpdates.ts similarity index 56% rename from packages/build-tools/src/ios/releaseChannel.ts rename to packages/build-tools/src/ios/expoUpdates.ts index 2f5cda2d..f49601bd 100644 --- a/packages/build-tools/src/ios/releaseChannel.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -4,10 +4,19 @@ import plist from '@expo/plist'; import fg from 'fast-glob'; import fs from 'fs-extra'; -async function updateReleaseChannel( - reactNativeProjectDirectory: string, - releaseChannel: string -): Promise { +export enum IosMetadataName { + UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'expo.modules.updates.EXUpdatesRequestHeaders', + RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL', +} +async function setIosMetadataEntryAsync({ + reactNativeProjectDirectory, + iosMetadataValue, + iosMetadataName, +}: { + reactNativeProjectDirectory: string; + iosMetadataValue: string | Record; + iosMetadataName: IosMetadataName; +}): Promise { const pbxprojPaths = await fg('ios/*/project.pbxproj', { cwd: reactNativeProjectDirectory }); const pbxprojPath = pbxprojPaths.length > 0 ? pbxprojPaths[0] : undefined; @@ -25,15 +34,13 @@ async function updateReleaseChannel( 'Expo.plist' ); - let items: Record = {}; + let items: Record> = {}; if (await fs.pathExists(expoPlistPath)) { const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); items = plist.parse(expoPlistContent); } - - items.EXUpdatesReleaseChannel = releaseChannel; - + items[iosMetadataName] = iosMetadataValue; const expoPlist = plist.build(items); if (!(await fs.pathExists(path.dirname(expoPlistPath)))) { @@ -43,29 +50,43 @@ async function updateReleaseChannel( await fs.writeFile(expoPlistPath, expoPlist); } -async function getReleaseChannel(reactNativeProjectDirectory: string): Promise { +export async function getExpoPlistDirectoryAsync( + reactNativeProjectDirectory: string +): Promise { const pbxprojPaths = await fg('ios/*/project.pbxproj', { cwd: reactNativeProjectDirectory }); - const pbxprojPath = pbxprojPaths.length > 0 ? pbxprojPaths[0] : undefined; - if (!pbxprojPath) { throw new Error(`Couldn't find an iOS project at '${reactNativeProjectDirectory}'`); } - const xcodeprojPath = path.resolve(pbxprojPath, '..'); - const expoPlistPath = path.resolve( + return path.resolve( reactNativeProjectDirectory, 'ios', path.basename(xcodeprojPath).replace(/\.xcodeproj$/, ''), - 'Supporting', + 'Supporting' + ); +} +// TODO, add some sort of dependent typing, +async function getIosMetadataEntryAsync({ + reactNativeProjectDirectory, + iosMetadataName, +}: { + reactNativeProjectDirectory: string; + iosMetadataName: IosMetadataName; +}): Promise | undefined> { + const expoPlistPath = path.resolve( + await getExpoPlistDirectoryAsync(reactNativeProjectDirectory), 'Expo.plist' ); - if (!(await fs.pathExists(expoPlistPath))) { return; } const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); - return plist.parse(expoPlistContent)?.EXUpdatesReleaseChannel; + const parsedPlist = plist.parse(expoPlistContent); + if (!parsedPlist) { + return; + } + return parsedPlist[iosMetadataName]; } -export { updateReleaseChannel, getReleaseChannel }; +export { setIosMetadataEntryAsync, getIosMetadataEntryAsync }; diff --git a/packages/build-tools/src/managed/expoUpdates.ts b/packages/build-tools/src/managed/expoUpdates.ts deleted file mode 100644 index c6f36349..00000000 --- a/packages/build-tools/src/managed/expoUpdates.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Platform } from '@expo/eas-build-job'; - -import isExpoUpdatesInstalledAsync from '../utils/isExpoUpdatesInstalled'; - -import { ManagedBuildContext, ManagedJob } from './context'; - -export async function configureExpoUpdatesIfInstalled( - ctx: ManagedBuildContext, - updateReleaseChannel: (dir: string, releaseChannel: string) => Promise -): Promise { - if (await isExpoUpdatesInstalledAsync(ctx.reactNativeProjectDirectory)) { - if (ctx.job.releaseChannel) { - const configFile = - ctx.job.platform === Platform.ANDROID ? 'AndroidManifest.xml' : 'Expo.plist'; - ctx.logger.info( - `Setting the release channel in '${configFile}' to '${ctx.job.releaseChannel}'` - ); - await updateReleaseChannel(ctx.reactNativeProjectDirectory, ctx.job.releaseChannel); - } else { - ctx.logger.info(`Using default release channel for 'expo-updates' (default)`); - } - } -} diff --git a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts new file mode 100644 index 00000000..8cfa7284 --- /dev/null +++ b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts @@ -0,0 +1,332 @@ +import path from 'path'; + +import { vol } from 'memfs'; +import fs from 'fs-extra'; +import { Platform } from '@expo/eas-build-job'; +import { AndroidConfig } from '@expo/config-plugins'; +import plist from '@expo/plist'; + +import { ManagedBuildContext, ManagedJob } from '../../managed/context'; +import * as expoUpdates from '../expoUpdates'; +import isExpoUpdatesInstalledAsync from '../isExpoUpdatesInstalled'; +import { AndroidMetadataName } from '../../android/expoUpdates'; +import { getExpoPlistDirectoryAsync, IosMetadataName } from '../../ios/expoUpdates'; + +jest.mock('../isExpoUpdatesInstalled', () => jest.fn()); +jest.mock('fs'); +const noMetadataAndroidManifest = ` + + + + + + + + + + + + + + + +`; +const noItemsExpoPlist = ` + + + + + `; + +const channel = 'main'; + +describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { + it('aborts if expo-updates is not installed', async () => { + (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(false); + jest.spyOn(expoUpdates, 'configureEASExpoUpdatesAsync'); + jest.spyOn(expoUpdates, 'configureClassicExpoUpdatesAsync'); + + await expoUpdates.configureExpoUpdatesIfInstalledAsync({} as any, Platform.IOS); + + expect(expoUpdates.configureEASExpoUpdatesAsync).not.toBeCalled(); + expect(expoUpdates.configureClassicExpoUpdatesAsync).not.toBeCalled(); + expect(isExpoUpdatesInstalledAsync).toHaveBeenCalledTimes(1); + }); + + it('configures for EAS if the updates.channel field is set', async () => { + (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); + jest.spyOn(expoUpdates, 'configureEASExpoUpdatesAsync').mockImplementation(); + jest.spyOn(expoUpdates, 'configureClassicExpoUpdatesAsync'); + + const managedCtx: ManagedBuildContext = { + job: { updates: { channel: 'main' } }, + } as any; + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + + expect(expoUpdates.configureEASExpoUpdatesAsync).toBeCalledTimes(1); + expect(expoUpdates.configureClassicExpoUpdatesAsync).not.toBeCalled(); + expect(isExpoUpdatesInstalledAsync).toHaveBeenCalledTimes(1); + }); + + it('configures for classic updates if the updates.channel field is not set', async () => { + (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); + jest.spyOn(expoUpdates, 'configureEASExpoUpdatesAsync').mockImplementation(); + jest.spyOn(expoUpdates, 'configureClassicExpoUpdatesAsync'); + + const managedCtx: ManagedBuildContext = { + job: {}, + logger: { info: () => {} }, + } as any; + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + + expect(expoUpdates.configureEASExpoUpdatesAsync).not.toBeCalled(); + expect(expoUpdates.configureClassicExpoUpdatesAsync).toBeCalledTimes(1); + expect(isExpoUpdatesInstalledAsync).toHaveBeenCalledTimes(1); + }); +}); + +describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { + it('sets the release channel if it is supplied in ctx.job.releaseChannel', async () => { + (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); + jest.spyOn(expoUpdates, 'setReleaseChannelNativelyAsync').mockImplementation(); + + const managedCtx: ManagedBuildContext = { + job: { releaseChannel: 'default' }, + } as any; + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + + expect(expoUpdates.setReleaseChannelNativelyAsync).toBeCalledTimes(1); + }); + it('searches for the natively defined releaseChannel if it is not supplied by ctx.job.releaseChannel', async () => { + (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); + jest.spyOn(expoUpdates, 'getNativelyDefinedReleaseChannelAsync').mockImplementation(); + + const managedCtx: ManagedBuildContext = { + job: {}, + logger: { info: () => {}, warn: () => {} }, + } as any; + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + + expect(expoUpdates.getNativelyDefinedReleaseChannelAsync).toBeCalledTimes(1); + }); + it('uses the default release channel if the releaseChannel is not defined in ctx.job.releaseChannel nor natively.', async () => { + (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); + jest.spyOn(expoUpdates, 'getNativelyDefinedReleaseChannelAsync').mockImplementation(() => { + throw new Error(); + }); + + const infoLogger = jest.fn(); + const managedCtx: ManagedBuildContext = { + job: {}, + logger: { info: infoLogger }, + } as any; + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + + expect(infoLogger).toBeCalledWith(`Using default release channel for 'expo-updates' (default)`); + }); +}); + +describe(expoUpdates.configureEASExpoUpdatesAsync, () => { + // maybe an e2e test instead of just the mocked out calls for the two functions as well? +}); + +describe(expoUpdates.setReleaseChannelNativelyAsync, () => { + beforeAll(() => { + jest.restoreAllMocks(); + }); + beforeEach(async () => { + vol.reset(); + }); + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const releaseChannel = 'default'; + const ctx = { + reactNativeProjectDirectory, + job: { releaseChannel }, + logger: { info: () => {} }, + }; + + test(Platform.ANDROID, async () => { + const manifestDirectory = expoUpdates.getAndroidManifestDirectory(reactNativeProjectDirectory); + const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + + fs.ensureDirSync(manifestDirectory); + fs.writeFileSync(manifestPath, Buffer.from(noMetadataAndroidManifest)); + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + expect( + AndroidConfig.Manifest.getMainApplicationMetaDataValue(androidManifest, 'releaseChannel') + ).toBe(null); + + await expoUpdates.setReleaseChannelNativelyAsync(ctx as any, Platform.ANDROID); + + const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + expect( + AndroidConfig.Manifest.getMainApplicationMetaDataValue( + newAndroidManifest, + AndroidMetadataName.RELEASE_CHANNEL + ) + ).toBe(releaseChannel); + }); + test(Platform.IOS, async () => { + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); + fs.writeFileSync( + path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), + Buffer.from('placeholder') + ); + + const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); + const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + + fs.ensureDirSync(expoPlistDirectory); + fs.writeFileSync(expoPlistPath, noItemsExpoPlist); + + await expoUpdates.setReleaseChannelNativelyAsync(ctx as any, Platform.IOS); + + const newExpoPlist = await fs.readFile(expoPlistPath, 'utf8'); + expect(plist.parse(newExpoPlist)[IosMetadataName.RELEASE_CHANNEL]).toEqual(releaseChannel); + }); +}); + +describe(expoUpdates.setChannelNativelyAsync, () => { + beforeAll(() => { + jest.restoreAllMocks(); + }); + beforeEach(async () => { + vol.reset(); + }); + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const ctx = { + reactNativeProjectDirectory, + job: { updates: { channel } }, + logger: { info: () => {} }, + }; + it(Platform.ANDROID, async () => { + const manifestDirectory = expoUpdates.getAndroidManifestDirectory(reactNativeProjectDirectory); + const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + + fs.ensureDirSync(manifestDirectory); + fs.writeFileSync(manifestPath, noMetadataAndroidManifest); + + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + expect( + AndroidConfig.Manifest.getMainApplicationMetaDataValue( + androidManifest, + AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY + ) + ).toBe(null); + + await expoUpdates.setChannelNativelyAsync(ctx as any, Platform.ANDROID); + + const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + const newValue = AndroidConfig.Manifest.getMainApplicationMetaDataValue( + newAndroidManifest, + AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY + ); + expect(newValue).toBeDefined(); + expect(JSON.parse(newValue!)).toEqual({ 'expo-channel-name': channel }); + }); + it(Platform.IOS, async () => { + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); + fs.writeFileSync( + path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), + Buffer.from('placeholder') + ); + + const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); + const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + + fs.ensureDirSync(expoPlistDirectory); + fs.writeFileSync(expoPlistPath, noItemsExpoPlist); + + await expoUpdates.setChannelNativelyAsync(ctx as any, Platform.IOS); + + const newExpoPlist = await fs.readFile(expoPlistPath, 'utf8'); + expect( + plist.parse(newExpoPlist)[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] + ).toEqual({ 'expo-channel-name': channel }); + }); +}); + +describe(expoUpdates.getNativelyDefinedReleaseChannelAsync, () => { + beforeAll(() => { + jest.restoreAllMocks(); + }); + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const releaseChannel = 'default'; + const ctx = { + reactNativeProjectDirectory, + logger: { info: () => {} }, + }; + beforeEach(async () => { + vol.reset(); + }); + it(Platform.ANDROID, async () => { + const AndroidManifest = ` + + + + + + + + + + + +`; + const manifestDirectory = expoUpdates.getAndroidManifestDirectory(reactNativeProjectDirectory); + const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + + fs.ensureDirSync(manifestDirectory); + fs.writeFileSync(manifestPath, AndroidManifest); + + const nativelyDefinedReleaseChannel = await expoUpdates.getNativelyDefinedReleaseChannelAsync( + ctx as any, + Platform.ANDROID + ); + expect(nativelyDefinedReleaseChannel).toBe(releaseChannel); + }); + it(Platform.IOS, async () => { + const ExpoPlist = ` + + + + ${IosMetadataName.RELEASE_CHANNEL} + ${releaseChannel} + + `; + + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); + fs.writeFileSync( + path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), + Buffer.from('placeholder') + ); + + const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); + const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + + fs.ensureDirSync(expoPlistDirectory); + fs.writeFileSync(expoPlistPath, ExpoPlist); + + const nativelyDefinedReleaseChannel = await expoUpdates.getNativelyDefinedReleaseChannelAsync( + ctx as any, + Platform.IOS + ); + + expect(nativelyDefinedReleaseChannel).toBe(releaseChannel); + }); +}); diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts new file mode 100644 index 00000000..da9b1e4c --- /dev/null +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -0,0 +1,258 @@ +import assert from 'assert'; +import path from 'path'; + +import fs from 'fs-extra'; +import { Ios, Android, Platform } from '@expo/eas-build-job'; +import { AndroidConfig } from '@expo/config-plugins'; +import plist from '@expo/plist'; + +import { AndroidMetadataName } from '../android/expoUpdates'; +import { BuildContext } from '../context'; +import { getExpoPlistDirectoryAsync, IosMetadataName } from '../ios/expoUpdates'; +import { ManagedBuildContext, ManagedJob } from '../managed/context'; + +import isExpoUpdatesInstalledAsync from './isExpoUpdatesInstalled'; +export type GenericJob = Ios.GenericJob | Android.GenericJob; + +/** + * Used for when Expo Updates is pointed at an EAS server. + * @param ctx + * @param platform + */ +export async function setChannelNativelyAsync( + ctx: ManagedBuildContext | BuildContext, + platform: Platform +): Promise { + assert(ctx.job.updates?.channel, 'updates.channel must be defined'); + // TODO combine with pre-existing updateRequestHeaders + const newUpdateRequestHeaders: Record = { + 'expo-channel-name': ctx.job.updates.channel, + }; + + const configFile = ctx.job.platform === Platform.ANDROID ? 'AndroidManifest.xml' : 'Expo.plist'; + ctx.logger.info( + `Setting the update response headers in '${configFile}' to '${JSON.stringify( + newUpdateRequestHeaders + )}'` + ); + + switch (platform) { + case Platform.ANDROID: { + const manifestPath = path.join( + getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), + 'AndroidManifest.xml' + ); + if (!(await fs.pathExists(manifestPath))) { + throw new Error(`Couldn't find Android manifest at ${manifestPath}`); + } + + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + const mainApp = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest); + const stringifiedUpdatesRequestHeaders = AndroidConfig.Manifest.getMainApplicationMetaDataValue( + androidManifest, + AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY + ); + AndroidConfig.Manifest.addMetaDataItemToMainApplication( + mainApp, + AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY, + JSON.stringify({ + ...JSON.parse(stringifiedUpdatesRequestHeaders ?? '{}'), + 'expo-channel-name': ctx.job.updates.channel, + }), + 'value' + ); + await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); + return; + } + case Platform.IOS: { + const expoPlistPath = path.resolve( + await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), + 'Expo.plist' + ); + + let items: Record> = {}; + if (!(await fs.pathExists(expoPlistPath))) { + throw new Error(`${expoPlistPath} does no exist`); + } + + const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); + items = plist.parse(expoPlistContent); + items[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] = { + ...((items[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] as Record< + string, + string + >) ?? {}), + 'expo-channel-name': ctx.job.updates.channel, + }; + const expoPlist = plist.build(items); + + await fs.writeFile(expoPlistPath, expoPlist); + return; + } + default: + throw new Error(`Platform ${platform} is not supported.`); + } +} + +export function getAndroidManifestDirectory(reactNativeProjectDirectory: string): string { + return path.join(reactNativeProjectDirectory, 'android', 'app', 'src', 'main'); +} + +/** + * Used for classic Expo Updates + * @param ctx + * @param platform + */ +export const setReleaseChannelNativelyAsync = async ( + ctx: ManagedBuildContext | BuildContext, + platform: Platform +): Promise => { + assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); + + const configFile = ctx.job.platform === Platform.ANDROID ? 'AndroidManifest.xml' : 'Expo.plist'; + ctx.logger.info(`Setting the release channel in '${configFile}' to '${ctx.job.releaseChannel}'`); + + switch (platform) { + case Platform.ANDROID: { + const manifestPath = path.join( + getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), + 'AndroidManifest.xml' + ); + if (!(await fs.pathExists(manifestPath))) { + throw new Error(`Couldn't find Android manifest at ${manifestPath}`); + } + + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + const mainApp = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest); + AndroidConfig.Manifest.addMetaDataItemToMainApplication( + mainApp, + AndroidMetadataName.RELEASE_CHANNEL, + ctx.job.releaseChannel, + 'value' + ); + await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); + return; + } + case Platform.IOS: { + const expoPlistPath = path.resolve( + await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), + 'Expo.plist' + ); + + let items: Record> = {}; + if (!(await fs.pathExists(expoPlistPath))) { + throw new Error(`${expoPlistPath} does no exist`); + } + + const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); + items = plist.parse(expoPlistContent); + items[IosMetadataName.RELEASE_CHANNEL] = ctx.job.releaseChannel; + const expoPlist = plist.build(items); + + await fs.writeFile(expoPlistPath, expoPlist); + return; + } + default: + throw new Error(`Platform ${platform} is not supported.`); + } +}; + +/** + * Used for classic Expo Updates + * @param ctx + * @param platform + */ +export const getNativelyDefinedReleaseChannelAsync = async ( + ctx: ManagedBuildContext | BuildContext, + platform: Platform +): Promise => { + switch (platform) { + case Platform.ANDROID: { + const manifestPath = path.join( + getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), + 'AndroidManifest.xml' + ); + if (!(await fs.pathExists(manifestPath))) { + return; + } + + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + return AndroidConfig.Manifest.getMainApplicationMetaDataValue( + androidManifest, + AndroidMetadataName.RELEASE_CHANNEL + ); + } + case Platform.IOS: { + const expoPlistPath = path.resolve( + await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), + 'Expo.plist' + ); + if (!(await fs.pathExists(expoPlistPath))) { + return; + } + const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); + const parsedPlist = plist.parse(expoPlistContent); + if (!parsedPlist) { + return; + } + return parsedPlist[IosMetadataName.RELEASE_CHANNEL]; + } + default: + throw new Error(`Platform ${platform} is not supported.`); + } +}; + +export const configureClassicExpoUpdatesAsync = async ( + ctx: ManagedBuildContext | BuildContext, + platform: Platform +): Promise => { + if (ctx.job.releaseChannel) { + await setReleaseChannelNativelyAsync(ctx, platform); + } else { + /** + * If releaseChannel is not defined: + * 1. Try to infer it from the native value. + * 2. If it is not set, fallback to 'default'. + */ + try { + const releaseChannel = await getNativelyDefinedReleaseChannelAsync(ctx, platform); + ctx.logger.info( + `Using the release channel pre-configured in native project (${releaseChannel})` + ); + ctx.logger.warn('Please add the "releaseChannel" field to your build profile (eas.json)'); + } catch (_) { + // only difference between generic and managed is that managed doesn't even try to look for the release channel in native code. + // if (ctx instanceof ManagedBuildContext) { + // return undefined; + // } + + ctx.logger.info(`Using default release channel for 'expo-updates' (default)`); + } + } +}; + +export const configureEASExpoUpdatesAsync = async ( + ctx: ManagedBuildContext | BuildContext, + platform: Platform +): Promise => { + await setChannelNativelyAsync(ctx, platform); +}; + +export async function configureExpoUpdatesIfInstalledAsync( + ctx: ManagedBuildContext | BuildContext, + platform: Platform +): Promise { + if (!(await isExpoUpdatesInstalledAsync(ctx.reactNativeProjectDirectory))) { + return; + } + + switch (true) { + case !!ctx.job.updates?.channel: { + await configureEASExpoUpdatesAsync(ctx, platform); + return; + } + default: { + await configureClassicExpoUpdatesAsync(ctx, platform); + } + } +} diff --git a/packages/local-build-plugin/src/eject.ts b/packages/local-build-plugin/src/eject.ts index 77cff304..3c51e183 100644 --- a/packages/local-build-plugin/src/eject.ts +++ b/packages/local-build-plugin/src/eject.ts @@ -11,6 +11,7 @@ const expoCliPackage = require.resolve('expo-cli'); export class LocalExpoCliEjectProvider implements EjectProvider { async runEject(ctx: BuildContext): Promise { + console.log('LocalExpoCliEjectProvider runEject'); const { logger, job } = ctx; const spawnOptions = { @@ -43,7 +44,14 @@ export class LocalExpoCliEjectProvider implements EjectProvider { const expoCliBinPath = process.env.EXPO_CLI_PATH ?? path.resolve(path.dirname(expoCliPackage), '../bin/expo.js'); + console.log({ expoCliBinPath }); + console.log('dir', ctx.reactNativeProjectDirectory); logger.debug(`${expoCliBinPath} prebuild --non-interactive --platform ${job.platform}`); + + console.log(['prebuild', '--non-interactive', '--platform', job.platform], { + ...spawnOptions, + cwd: ctx.reactNativeProjectDirectory, + }); await spawnAsync( expoCliBinPath, ['prebuild', '--non-interactive', '--platform', job.platform], diff --git a/packages/local-build-plugin/src/ios.ts b/packages/local-build-plugin/src/ios.ts index 1f66b259..ba251f7e 100644 --- a/packages/local-build-plugin/src/ios.ts +++ b/packages/local-build-plugin/src/ios.ts @@ -31,6 +31,12 @@ function createBuildContext(job: Ios.Job, { env, workingdir }: BuildParams): Bui env, }); } else { + console.log('creating managed build context'); + console.log('creating managed build context'); + console.log('creating managed build context'); + console.log('creating managed build context'); + console.log('creating managed build context'); + console.log('creating managed build context'); return new ManagedBuildContext(job, { workingdir, logger, From c0e4f9ad869a38ee441345dd293bbe913ca4e996 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 20 May 2021 11:30:42 -0700 Subject: [PATCH 10/28] refactore to platform tests --- packages/build-tools/package.json | 3 +- .../src/android/__tests__/expoUpdates.test.ts | 148 +++++++++++ .../build-tools/src/android/expoUpdates.ts | 130 +++++----- .../src/ios/__tests__/expoUpdates.test.ts | 124 ++++++++++ packages/build-tools/src/ios/expoUpdates.ts | 114 +++++---- .../src/utils/__tests__/expoUpdates.test.ts | 233 ------------------ packages/build-tools/src/utils/expoUpdates.ts | 144 ++--------- 7 files changed, 415 insertions(+), 481 deletions(-) create mode 100644 packages/build-tools/src/android/__tests__/expoUpdates.test.ts create mode 100644 packages/build-tools/src/ios/__tests__/expoUpdates.test.ts diff --git a/packages/build-tools/package.json b/packages/build-tools/package.json index 7a52582d..629f8408 100644 --- a/packages/build-tools/package.json +++ b/packages/build-tools/package.json @@ -35,8 +35,7 @@ "node-forge": "^0.9.1", "nullthrows": "^1.1.1", "plist": "^3.0.1", - "uuid": "^3.3.3", - "xml2js": "^0.4.23" + "uuid": "^3.3.3" }, "devDependencies": { "@types/fs-extra": "^9.0.1", diff --git a/packages/build-tools/src/android/__tests__/expoUpdates.test.ts b/packages/build-tools/src/android/__tests__/expoUpdates.test.ts new file mode 100644 index 00000000..cca83bcd --- /dev/null +++ b/packages/build-tools/src/android/__tests__/expoUpdates.test.ts @@ -0,0 +1,148 @@ +import path from 'path'; + +import fs from 'fs-extra'; +import { AndroidConfig } from '@expo/config-plugins'; + +import { + AndroidMetadataName, + getAndroidManifestDirectory, + androidGetNativelyDefinedReleaseChannelAsync, + androidSetChannelNativelyAsync, + androidSetReleaseChannelNativelyAsync, +} from '../expoUpdates'; + +jest.mock('fs'); + +const channel = 'main'; +const noMetadataAndroidManifest = ` + + + + + + + + + + + + + + + +`; +describe(androidSetReleaseChannelNativelyAsync, () => { + test('sets the release channel', async () => { + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const releaseChannel = 'default'; + const ctx = { + reactNativeProjectDirectory, + job: { releaseChannel }, + logger: { info: () => {} }, + }; + + const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory); + const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + + fs.ensureDirSync(manifestDirectory); + fs.writeFileSync(manifestPath, Buffer.from(noMetadataAndroidManifest)); + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + expect( + AndroidConfig.Manifest.getMainApplicationMetaDataValue(androidManifest, 'releaseChannel') + ).toBe(null); + + await androidSetReleaseChannelNativelyAsync(ctx as any); + + const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + expect( + AndroidConfig.Manifest.getMainApplicationMetaDataValue( + newAndroidManifest, + AndroidMetadataName.RELEASE_CHANNEL + ) + ).toBe(releaseChannel); + }); +}); +describe(androidSetChannelNativelyAsync, () => { + it('sets the channel', async () => { + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const ctx = { + reactNativeProjectDirectory, + job: { updates: { channel } }, + logger: { info: () => {} }, + }; + + const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory); + const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + + fs.ensureDirSync(manifestDirectory); + fs.writeFileSync(manifestPath, noMetadataAndroidManifest); + + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + expect( + AndroidConfig.Manifest.getMainApplicationMetaDataValue( + androidManifest, + AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY + ) + ).toBe(null); + + await androidSetChannelNativelyAsync(ctx as any); + + const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + const newValue = AndroidConfig.Manifest.getMainApplicationMetaDataValue( + newAndroidManifest, + AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY + ); + expect(newValue).toBeDefined(); + expect(JSON.parse(newValue!)).toEqual({ 'expo-channel-name': channel }); + }); +}); +describe(androidGetNativelyDefinedReleaseChannelAsync, () => { + it('gets the natively defined release channel', async () => { + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const releaseChannel = 'default'; + const ctx = { + reactNativeProjectDirectory, + logger: { info: () => {} }, + }; + + const releaseChannelInAndroidManifest = ` + + + + + + + + + + + + + `; + const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory); + const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + + fs.ensureDirSync(manifestDirectory); + fs.writeFileSync(manifestPath, releaseChannelInAndroidManifest); + + const nativelyDefinedReleaseChannel = await androidGetNativelyDefinedReleaseChannelAsync( + ctx as any + ); + expect(nativelyDefinedReleaseChannel).toBe(releaseChannel); + }); +}); diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index ac795a31..de693c42 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -1,103 +1,89 @@ import path from 'path'; +import assert from 'assert'; -import * as xml from 'xml2js'; import fs from 'fs-extra'; +import { AndroidConfig } from '@expo/config-plugins'; +import { ManagedBuildContext, ManagedJob } from '../managed/context'; +import { BuildContext } from '../context'; +import { GenericJob } from '../utils/expoUpdates'; export enum AndroidMetadataName { UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY', RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL', } +export function getAndroidManifestDirectory(reactNativeProjectDirectory: string): string { + return path.join(reactNativeProjectDirectory, 'android', 'app', 'src', 'main'); +} + +export async function androidSetChannelNativelyAsync( + ctx: ManagedBuildContext | BuildContext +): Promise { + assert(ctx.job.updates?.channel, 'updates.channel must be defined'); -export async function setAndroidMetadataEntryAsync({ - reactNativeProjectDirectory, - androidMetadataValue, - androidMetadataName, -}: { - reactNativeProjectDirectory: string; - androidMetadataValue: string; - androidMetadataName: AndroidMetadataName; -}): Promise { const manifestPath = path.join( - reactNativeProjectDirectory, - 'android', - 'app', - 'src', - 'main', + getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), 'AndroidManifest.xml' ); - if (!(await fs.pathExists(manifestPath))) { throw new Error(`Couldn't find Android manifest at ${manifestPath}`); } - const manifestContent = await fs.readFile(manifestPath, 'utf8'); - const manifest = await xml.parseStringPromise(manifestContent); - - const mainApplication = manifest?.manifest?.application?.find( - (e: any) => e?.['$']?.['android:name'] === '.MainApplication' + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + const mainApp = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest); + const stringifiedUpdatesRequestHeaders = AndroidConfig.Manifest.getMainApplicationMetaDataValue( + androidManifest, + AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY ); - - if (!mainApplication) { - throw new Error(`Couldn't find '.MainApplication' in the manifest at ${manifestPath}`); - } - - const newItem = { - $: { - 'android:name': androidMetadataName, - 'android:value': androidMetadataValue, - }, - }; - - if (mainApplication['meta-data']) { - const existingMetaDataItem = mainApplication['meta-data'].find( - (e: any) => e.$['android:name'] === androidMetadataName - ); - - if (existingMetaDataItem) { - existingMetaDataItem.$['android:value'] = androidMetadataValue; - } else { - mainApplication['meta-data'].push(newItem); - } - } else { - mainApplication['meta-data'] = [newItem]; - } - - const manifestXml = new xml.Builder().buildObject(manifest); - await fs.writeFile(manifestPath, manifestXml); + AndroidConfig.Manifest.addMetaDataItemToMainApplication( + mainApp, + AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY, + JSON.stringify({ + ...JSON.parse(stringifiedUpdatesRequestHeaders ?? '{}'), + 'expo-channel-name': ctx.job.updates.channel, + }), + 'value' + ); + await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); } -export async function getAndroidMetadataEntryAsync({ - reactNativeProjectDirectory, - androidMetadataName, -}: { - reactNativeProjectDirectory: string; - androidMetadataName: AndroidMetadataName; -}): Promise { +export const androidSetReleaseChannelNativelyAsync = async ( + ctx: ManagedBuildContext | BuildContext +): Promise => { + assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); + const manifestPath = path.join( - reactNativeProjectDirectory, - 'android', - 'app', - 'src', - 'main', + getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), 'AndroidManifest.xml' ); - if (!(await fs.pathExists(manifestPath))) { throw new Error(`Couldn't find Android manifest at ${manifestPath}`); } - const manifestContent = await fs.readFile(manifestPath, 'utf8'); - const manifest = await xml.parseStringPromise(manifestContent); - - const mainApplication = manifest?.manifest?.application?.find( - (e: any) => e?.['$']?.['android:name'] === '.MainApplication' + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + const mainApp = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest); + AndroidConfig.Manifest.addMetaDataItemToMainApplication( + mainApp, + AndroidMetadataName.RELEASE_CHANNEL, + ctx.job.releaseChannel, + 'value' ); + await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); +}; - if (!mainApplication?.['meta-data']) { +export const androidGetNativelyDefinedReleaseChannelAsync = async ( + ctx: ManagedBuildContext | BuildContext, +): Promise => { + const manifestPath = path.join( + getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), + 'AndroidManifest.xml' + ); + if (!(await fs.pathExists(manifestPath))) { return; } - const existingMetaDataItem = mainApplication['meta-data'].find( - (e: any) => e.$['android:name'] === androidMetadataName + + const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); + return AndroidConfig.Manifest.getMainApplicationMetaDataValue( + androidManifest, + AndroidMetadataName.RELEASE_CHANNEL ); - return existingMetaDataItem?.$?.['android:value']; -} +}; diff --git a/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts b/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts new file mode 100644 index 00000000..c4bc6d69 --- /dev/null +++ b/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts @@ -0,0 +1,124 @@ +import path from 'path'; + +import fs from 'fs-extra'; +import plist from '@expo/plist'; + +import { + getExpoPlistDirectoryAsync, + iosGetNativelyDefinedReleaseChannelAsync, + IosMetadataName, + iosSetChannelNativelyAsync, + iosSetReleaseChannelNativelyAsync, +} from '../../ios/expoUpdates'; + +jest.mock('fs'); + +const noItemsExpoPlist = ` + + + + + +`; +const channel = 'main'; + +describe(iosSetReleaseChannelNativelyAsync, () => { + test('sets the release channel', async () => { + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const releaseChannel = 'default'; + const ctx = { + reactNativeProjectDirectory, + job: { releaseChannel }, + logger: { info: () => {} }, + }; + + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); + fs.writeFileSync( + path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), + Buffer.from('placeholder') + ); + + const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); + const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + + fs.ensureDirSync(expoPlistDirectory); + fs.writeFileSync(expoPlistPath, noItemsExpoPlist); + + await iosSetReleaseChannelNativelyAsync(ctx as any); + + const newExpoPlist = await fs.readFile(expoPlistPath, 'utf8'); + expect(plist.parse(newExpoPlist)[IosMetadataName.RELEASE_CHANNEL]).toEqual(releaseChannel); + }); +}); + +describe(iosSetChannelNativelyAsync, () => { + it('sets the channel', async () => { + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const ctx = { + reactNativeProjectDirectory, + job: { updates: { channel } }, + logger: { info: () => {} }, + }; + + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); + fs.writeFileSync( + path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), + Buffer.from('placeholder') + ); + + const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); + const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + + fs.ensureDirSync(expoPlistDirectory); + fs.writeFileSync(expoPlistPath, noItemsExpoPlist); + + await iosSetChannelNativelyAsync(ctx as any); + + const newExpoPlist = await fs.readFile(expoPlistPath, 'utf8'); + expect( + plist.parse(newExpoPlist)[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] + ).toEqual({ 'expo-channel-name': channel }); + }); +}); + +describe(iosGetNativelyDefinedReleaseChannelAsync, () => { + it('gets the natively defined release channel', async () => { + const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); + fs.ensureDirSync(reactNativeProjectDirectory); + const releaseChannel = 'default'; + const ctx = { + reactNativeProjectDirectory, + logger: { info: () => {} }, + }; + + const releaseChannelInPlist = ` + + + + + ${IosMetadataName.RELEASE_CHANNEL} + ${releaseChannel} + + `; + + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); + fs.writeFileSync( + path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), + Buffer.from('placeholder') + ); + + const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); + const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + + fs.ensureDirSync(expoPlistDirectory); + fs.writeFileSync(expoPlistPath, releaseChannelInPlist); + + const nativelyDefinedReleaseChannel = await iosGetNativelyDefinedReleaseChannelAsync( + ctx as any + ); + + expect(nativelyDefinedReleaseChannel).toBe(releaseChannel); + }); +}); diff --git a/packages/build-tools/src/ios/expoUpdates.ts b/packages/build-tools/src/ios/expoUpdates.ts index f49601bd..e88ef293 100644 --- a/packages/build-tools/src/ios/expoUpdates.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -1,81 +1,93 @@ import path from 'path'; +import assert from 'assert'; +import fs from 'fs-extra'; import plist from '@expo/plist'; import fg from 'fast-glob'; -import fs from 'fs-extra'; + +import { ManagedBuildContext, ManagedJob } from '../managed/context'; +import { BuildContext } from '../context'; +import { GenericJob } from '../utils/expoUpdates'; export enum IosMetadataName { UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'expo.modules.updates.EXUpdatesRequestHeaders', RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL', } -async function setIosMetadataEntryAsync({ - reactNativeProjectDirectory, - iosMetadataValue, - iosMetadataName, -}: { - reactNativeProjectDirectory: string; - iosMetadataValue: string | Record; - iosMetadataName: IosMetadataName; -}): Promise { - const pbxprojPaths = await fg('ios/*/project.pbxproj', { cwd: reactNativeProjectDirectory }); +export async function getExpoPlistDirectoryAsync( + reactNativeProjectDirectory: string +): Promise { + const pbxprojPaths = await fg('ios/*/project.pbxproj', { cwd: reactNativeProjectDirectory }); const pbxprojPath = pbxprojPaths.length > 0 ? pbxprojPaths[0] : undefined; - if (!pbxprojPath) { throw new Error(`Couldn't find an iOS project at '${reactNativeProjectDirectory}'`); } - const xcodeprojPath = path.resolve(pbxprojPath, '..'); - const expoPlistPath = path.resolve( + return path.resolve( reactNativeProjectDirectory, 'ios', path.basename(xcodeprojPath).replace(/\.xcodeproj$/, ''), - 'Supporting', + 'Supporting' + ); +} + +export async function iosSetChannelNativelyAsync( + ctx: ManagedBuildContext | BuildContext +): Promise { + assert(ctx.job.updates?.channel, 'updates.channel must be defined'); + + const expoPlistPath = path.resolve( + await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), 'Expo.plist' ); let items: Record> = {}; - - if (await fs.pathExists(expoPlistPath)) { - const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); - items = plist.parse(expoPlistContent); + if (!(await fs.pathExists(expoPlistPath))) { + throw new Error(`${expoPlistPath} does no exist`); } - items[iosMetadataName] = iosMetadataValue; - const expoPlist = plist.build(items); - if (!(await fs.pathExists(path.dirname(expoPlistPath)))) { - await fs.mkdirp(path.dirname(expoPlistPath)); - } + const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); + items = plist.parse(expoPlistContent); + items[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] = { + ...((items[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] as Record< + string, + string + >) ?? {}), + 'expo-channel-name': ctx.job.updates.channel, + }; + const expoPlist = plist.build(items); await fs.writeFile(expoPlistPath, expoPlist); } -export async function getExpoPlistDirectoryAsync( - reactNativeProjectDirectory: string -): Promise { - const pbxprojPaths = await fg('ios/*/project.pbxproj', { cwd: reactNativeProjectDirectory }); - const pbxprojPath = pbxprojPaths.length > 0 ? pbxprojPaths[0] : undefined; - if (!pbxprojPath) { - throw new Error(`Couldn't find an iOS project at '${reactNativeProjectDirectory}'`); - } - const xcodeprojPath = path.resolve(pbxprojPath, '..'); - return path.resolve( - reactNativeProjectDirectory, - 'ios', - path.basename(xcodeprojPath).replace(/\.xcodeproj$/, ''), - 'Supporting' +export const iosSetReleaseChannelNativelyAsync = async ( + ctx: ManagedBuildContext | BuildContext +): Promise => { + assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); + + const expoPlistPath = path.resolve( + await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), + 'Expo.plist' ); -} -// TODO, add some sort of dependent typing, -async function getIosMetadataEntryAsync({ - reactNativeProjectDirectory, - iosMetadataName, -}: { - reactNativeProjectDirectory: string; - iosMetadataName: IosMetadataName; -}): Promise | undefined> { + + let items: Record> = {}; + if (!(await fs.pathExists(expoPlistPath))) { + throw new Error(`${expoPlistPath} does no exist`); + } + + const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); + items = plist.parse(expoPlistContent); + items[IosMetadataName.RELEASE_CHANNEL] = ctx.job.releaseChannel; + const expoPlist = plist.build(items); + + await fs.writeFile(expoPlistPath, expoPlist); +}; + +export const iosGetNativelyDefinedReleaseChannelAsync = async ( + ctx: ManagedBuildContext | BuildContext +): Promise => { const expoPlistPath = path.resolve( - await getExpoPlistDirectoryAsync(reactNativeProjectDirectory), + await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), 'Expo.plist' ); if (!(await fs.pathExists(expoPlistPath))) { @@ -86,7 +98,5 @@ async function getIosMetadataEntryAsync({ if (!parsedPlist) { return; } - return parsedPlist[iosMetadataName]; -} - -export { setIosMetadataEntryAsync, getIosMetadataEntryAsync }; + return parsedPlist[IosMetadataName.RELEASE_CHANNEL]; +}; diff --git a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts index 8cfa7284..a1e739cb 100644 --- a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts @@ -1,56 +1,12 @@ -import path from 'path'; -import { vol } from 'memfs'; -import fs from 'fs-extra'; import { Platform } from '@expo/eas-build-job'; -import { AndroidConfig } from '@expo/config-plugins'; -import plist from '@expo/plist'; import { ManagedBuildContext, ManagedJob } from '../../managed/context'; import * as expoUpdates from '../expoUpdates'; import isExpoUpdatesInstalledAsync from '../isExpoUpdatesInstalled'; -import { AndroidMetadataName } from '../../android/expoUpdates'; -import { getExpoPlistDirectoryAsync, IosMetadataName } from '../../ios/expoUpdates'; jest.mock('../isExpoUpdatesInstalled', () => jest.fn()); jest.mock('fs'); -const noMetadataAndroidManifest = ` - - - - - - - - - - - - - - - -`; -const noItemsExpoPlist = ` - - - - - `; - -const channel = 'main'; describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { it('aborts if expo-updates is not installed', async () => { @@ -141,192 +97,3 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { describe(expoUpdates.configureEASExpoUpdatesAsync, () => { // maybe an e2e test instead of just the mocked out calls for the two functions as well? }); - -describe(expoUpdates.setReleaseChannelNativelyAsync, () => { - beforeAll(() => { - jest.restoreAllMocks(); - }); - beforeEach(async () => { - vol.reset(); - }); - const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); - fs.ensureDirSync(reactNativeProjectDirectory); - const releaseChannel = 'default'; - const ctx = { - reactNativeProjectDirectory, - job: { releaseChannel }, - logger: { info: () => {} }, - }; - - test(Platform.ANDROID, async () => { - const manifestDirectory = expoUpdates.getAndroidManifestDirectory(reactNativeProjectDirectory); - const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); - - fs.ensureDirSync(manifestDirectory); - fs.writeFileSync(manifestPath, Buffer.from(noMetadataAndroidManifest)); - const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); - expect( - AndroidConfig.Manifest.getMainApplicationMetaDataValue(androidManifest, 'releaseChannel') - ).toBe(null); - - await expoUpdates.setReleaseChannelNativelyAsync(ctx as any, Platform.ANDROID); - - const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); - expect( - AndroidConfig.Manifest.getMainApplicationMetaDataValue( - newAndroidManifest, - AndroidMetadataName.RELEASE_CHANNEL - ) - ).toBe(releaseChannel); - }); - test(Platform.IOS, async () => { - fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); - fs.writeFileSync( - path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), - Buffer.from('placeholder') - ); - - const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); - const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); - - fs.ensureDirSync(expoPlistDirectory); - fs.writeFileSync(expoPlistPath, noItemsExpoPlist); - - await expoUpdates.setReleaseChannelNativelyAsync(ctx as any, Platform.IOS); - - const newExpoPlist = await fs.readFile(expoPlistPath, 'utf8'); - expect(plist.parse(newExpoPlist)[IosMetadataName.RELEASE_CHANNEL]).toEqual(releaseChannel); - }); -}); - -describe(expoUpdates.setChannelNativelyAsync, () => { - beforeAll(() => { - jest.restoreAllMocks(); - }); - beforeEach(async () => { - vol.reset(); - }); - const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); - fs.ensureDirSync(reactNativeProjectDirectory); - const ctx = { - reactNativeProjectDirectory, - job: { updates: { channel } }, - logger: { info: () => {} }, - }; - it(Platform.ANDROID, async () => { - const manifestDirectory = expoUpdates.getAndroidManifestDirectory(reactNativeProjectDirectory); - const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); - - fs.ensureDirSync(manifestDirectory); - fs.writeFileSync(manifestPath, noMetadataAndroidManifest); - - const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); - expect( - AndroidConfig.Manifest.getMainApplicationMetaDataValue( - androidManifest, - AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY - ) - ).toBe(null); - - await expoUpdates.setChannelNativelyAsync(ctx as any, Platform.ANDROID); - - const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); - const newValue = AndroidConfig.Manifest.getMainApplicationMetaDataValue( - newAndroidManifest, - AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY - ); - expect(newValue).toBeDefined(); - expect(JSON.parse(newValue!)).toEqual({ 'expo-channel-name': channel }); - }); - it(Platform.IOS, async () => { - fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); - fs.writeFileSync( - path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), - Buffer.from('placeholder') - ); - - const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); - const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); - - fs.ensureDirSync(expoPlistDirectory); - fs.writeFileSync(expoPlistPath, noItemsExpoPlist); - - await expoUpdates.setChannelNativelyAsync(ctx as any, Platform.IOS); - - const newExpoPlist = await fs.readFile(expoPlistPath, 'utf8'); - expect( - plist.parse(newExpoPlist)[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] - ).toEqual({ 'expo-channel-name': channel }); - }); -}); - -describe(expoUpdates.getNativelyDefinedReleaseChannelAsync, () => { - beforeAll(() => { - jest.restoreAllMocks(); - }); - const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); - fs.ensureDirSync(reactNativeProjectDirectory); - const releaseChannel = 'default'; - const ctx = { - reactNativeProjectDirectory, - logger: { info: () => {} }, - }; - beforeEach(async () => { - vol.reset(); - }); - it(Platform.ANDROID, async () => { - const AndroidManifest = ` - - - - - - - - - - - -`; - const manifestDirectory = expoUpdates.getAndroidManifestDirectory(reactNativeProjectDirectory); - const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); - - fs.ensureDirSync(manifestDirectory); - fs.writeFileSync(manifestPath, AndroidManifest); - - const nativelyDefinedReleaseChannel = await expoUpdates.getNativelyDefinedReleaseChannelAsync( - ctx as any, - Platform.ANDROID - ); - expect(nativelyDefinedReleaseChannel).toBe(releaseChannel); - }); - it(Platform.IOS, async () => { - const ExpoPlist = ` - - - - ${IosMetadataName.RELEASE_CHANNEL} - ${releaseChannel} - - `; - - fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); - fs.writeFileSync( - path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), - Buffer.from('placeholder') - ); - - const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); - const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); - - fs.ensureDirSync(expoPlistDirectory); - fs.writeFileSync(expoPlistPath, ExpoPlist); - - const nativelyDefinedReleaseChannel = await expoUpdates.getNativelyDefinedReleaseChannelAsync( - ctx as any, - Platform.IOS - ); - - expect(nativelyDefinedReleaseChannel).toBe(releaseChannel); - }); -}); diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index da9b1e4c..66b031eb 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -1,14 +1,18 @@ import assert from 'assert'; -import path from 'path'; -import fs from 'fs-extra'; import { Ios, Android, Platform } from '@expo/eas-build-job'; -import { AndroidConfig } from '@expo/config-plugins'; -import plist from '@expo/plist'; -import { AndroidMetadataName } from '../android/expoUpdates'; +import { + androidSetChannelNativelyAsync, + androidSetReleaseChannelNativelyAsync, + androidGetNativelyDefinedReleaseChannelAsync, +} from '../android/expoUpdates'; +import { + iosSetChannelNativelyAsync, + iosSetReleaseChannelNativelyAsync, + iosGetNativelyDefinedReleaseChannelAsync, +} from '../ios/expoUpdates'; import { BuildContext } from '../context'; -import { getExpoPlistDirectoryAsync, IosMetadataName } from '../ios/expoUpdates'; import { ManagedBuildContext, ManagedJob } from '../managed/context'; import isExpoUpdatesInstalledAsync from './isExpoUpdatesInstalled'; @@ -19,12 +23,11 @@ export type GenericJob = Ios.GenericJob | Android.GenericJob; * @param ctx * @param platform */ -export async function setChannelNativelyAsync( +export const setChannelNativelyAsync = async ( ctx: ManagedBuildContext | BuildContext, platform: Platform -): Promise { +): Promise => { assert(ctx.job.updates?.channel, 'updates.channel must be defined'); - // TODO combine with pre-existing updateRequestHeaders const newUpdateRequestHeaders: Record = { 'expo-channel-name': ctx.job.updates.channel, }; @@ -38,65 +41,17 @@ export async function setChannelNativelyAsync( switch (platform) { case Platform.ANDROID: { - const manifestPath = path.join( - getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), - 'AndroidManifest.xml' - ); - if (!(await fs.pathExists(manifestPath))) { - throw new Error(`Couldn't find Android manifest at ${manifestPath}`); - } - - const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); - const mainApp = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest); - const stringifiedUpdatesRequestHeaders = AndroidConfig.Manifest.getMainApplicationMetaDataValue( - androidManifest, - AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY - ); - AndroidConfig.Manifest.addMetaDataItemToMainApplication( - mainApp, - AndroidMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY, - JSON.stringify({ - ...JSON.parse(stringifiedUpdatesRequestHeaders ?? '{}'), - 'expo-channel-name': ctx.job.updates.channel, - }), - 'value' - ); - await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); + await androidSetChannelNativelyAsync(ctx); return; } case Platform.IOS: { - const expoPlistPath = path.resolve( - await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), - 'Expo.plist' - ); - - let items: Record> = {}; - if (!(await fs.pathExists(expoPlistPath))) { - throw new Error(`${expoPlistPath} does no exist`); - } - - const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); - items = plist.parse(expoPlistContent); - items[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] = { - ...((items[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] as Record< - string, - string - >) ?? {}), - 'expo-channel-name': ctx.job.updates.channel, - }; - const expoPlist = plist.build(items); - - await fs.writeFile(expoPlistPath, expoPlist); + await iosSetChannelNativelyAsync(ctx); return; } default: throw new Error(`Platform ${platform} is not supported.`); } -} - -export function getAndroidManifestDirectory(reactNativeProjectDirectory: string): string { - return path.join(reactNativeProjectDirectory, 'android', 'app', 'src', 'main'); -} +}; /** * Used for classic Expo Updates @@ -114,42 +69,11 @@ export const setReleaseChannelNativelyAsync = async ( switch (platform) { case Platform.ANDROID: { - const manifestPath = path.join( - getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), - 'AndroidManifest.xml' - ); - if (!(await fs.pathExists(manifestPath))) { - throw new Error(`Couldn't find Android manifest at ${manifestPath}`); - } - - const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); - const mainApp = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest); - AndroidConfig.Manifest.addMetaDataItemToMainApplication( - mainApp, - AndroidMetadataName.RELEASE_CHANNEL, - ctx.job.releaseChannel, - 'value' - ); - await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); + await androidSetReleaseChannelNativelyAsync(ctx); return; } case Platform.IOS: { - const expoPlistPath = path.resolve( - await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), - 'Expo.plist' - ); - - let items: Record> = {}; - if (!(await fs.pathExists(expoPlistPath))) { - throw new Error(`${expoPlistPath} does no exist`); - } - - const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); - items = plist.parse(expoPlistContent); - items[IosMetadataName.RELEASE_CHANNEL] = ctx.job.releaseChannel; - const expoPlist = plist.build(items); - - await fs.writeFile(expoPlistPath, expoPlist); + await iosSetReleaseChannelNativelyAsync(ctx); return; } default: @@ -168,34 +92,10 @@ export const getNativelyDefinedReleaseChannelAsync = async ( ): Promise => { switch (platform) { case Platform.ANDROID: { - const manifestPath = path.join( - getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), - 'AndroidManifest.xml' - ); - if (!(await fs.pathExists(manifestPath))) { - return; - } - - const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); - return AndroidConfig.Manifest.getMainApplicationMetaDataValue( - androidManifest, - AndroidMetadataName.RELEASE_CHANNEL - ); + return androidGetNativelyDefinedReleaseChannelAsync(ctx); } case Platform.IOS: { - const expoPlistPath = path.resolve( - await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), - 'Expo.plist' - ); - if (!(await fs.pathExists(expoPlistPath))) { - return; - } - const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); - const parsedPlist = plist.parse(expoPlistContent); - if (!parsedPlist) { - return; - } - return parsedPlist[IosMetadataName.RELEASE_CHANNEL]; + return iosGetNativelyDefinedReleaseChannelAsync(ctx); } default: throw new Error(`Platform ${platform} is not supported.`); @@ -238,10 +138,10 @@ export const configureEASExpoUpdatesAsync = async ( await setChannelNativelyAsync(ctx, platform); }; -export async function configureExpoUpdatesIfInstalledAsync( +export const configureExpoUpdatesIfInstalledAsync = async ( ctx: ManagedBuildContext | BuildContext, platform: Platform -): Promise { +): Promise => { if (!(await isExpoUpdatesInstalledAsync(ctx.reactNativeProjectDirectory))) { return; } @@ -255,4 +155,4 @@ export async function configureExpoUpdatesIfInstalledAsync( await configureClassicExpoUpdatesAsync(ctx, platform); } } -} +}; From 324e14e5d6e04f4e61677278e69852dd36ae4d27 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 20 May 2021 11:33:34 -0700 Subject: [PATCH 11/28] remove stray logging --- packages/local-build-plugin/src/eject.ts | 8 -------- packages/local-build-plugin/src/ios.ts | 6 ------ 2 files changed, 14 deletions(-) diff --git a/packages/local-build-plugin/src/eject.ts b/packages/local-build-plugin/src/eject.ts index 3c51e183..77cff304 100644 --- a/packages/local-build-plugin/src/eject.ts +++ b/packages/local-build-plugin/src/eject.ts @@ -11,7 +11,6 @@ const expoCliPackage = require.resolve('expo-cli'); export class LocalExpoCliEjectProvider implements EjectProvider { async runEject(ctx: BuildContext): Promise { - console.log('LocalExpoCliEjectProvider runEject'); const { logger, job } = ctx; const spawnOptions = { @@ -44,14 +43,7 @@ export class LocalExpoCliEjectProvider implements EjectProvider { const expoCliBinPath = process.env.EXPO_CLI_PATH ?? path.resolve(path.dirname(expoCliPackage), '../bin/expo.js'); - console.log({ expoCliBinPath }); - console.log('dir', ctx.reactNativeProjectDirectory); logger.debug(`${expoCliBinPath} prebuild --non-interactive --platform ${job.platform}`); - - console.log(['prebuild', '--non-interactive', '--platform', job.platform], { - ...spawnOptions, - cwd: ctx.reactNativeProjectDirectory, - }); await spawnAsync( expoCliBinPath, ['prebuild', '--non-interactive', '--platform', job.platform], diff --git a/packages/local-build-plugin/src/ios.ts b/packages/local-build-plugin/src/ios.ts index ba251f7e..1f66b259 100644 --- a/packages/local-build-plugin/src/ios.ts +++ b/packages/local-build-plugin/src/ios.ts @@ -31,12 +31,6 @@ function createBuildContext(job: Ios.Job, { env, workingdir }: BuildParams): Bui env, }); } else { - console.log('creating managed build context'); - console.log('creating managed build context'); - console.log('creating managed build context'); - console.log('creating managed build context'); - console.log('creating managed build context'); - console.log('creating managed build context'); return new ManagedBuildContext(job, { workingdir, logger, From b038cb25829682aacb991f0056c455f1f9514471 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 20 May 2021 12:39:04 -0700 Subject: [PATCH 12/28] fix ios plist names --- packages/build-tools/src/ios/expoUpdates.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/build-tools/src/ios/expoUpdates.ts b/packages/build-tools/src/ios/expoUpdates.ts index e88ef293..f83091a9 100644 --- a/packages/build-tools/src/ios/expoUpdates.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -10,8 +10,8 @@ import { BuildContext } from '../context'; import { GenericJob } from '../utils/expoUpdates'; export enum IosMetadataName { - UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'expo.modules.updates.EXUpdatesRequestHeaders', - RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL', + UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'EXUpdatesRequestHeaders', + RELEASE_CHANNEL = 'EXUpdatesReleaseChannel', } export async function getExpoPlistDirectoryAsync( From 3ff6a410ec9efcaad5851c0732f538a7199bbdb6 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 22 May 2021 06:51:48 -0700 Subject: [PATCH 13/28] add assert --- packages/build-tools/src/utils/expoUpdates.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index 66b031eb..99b64b9e 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -116,6 +116,7 @@ export const configureClassicExpoUpdatesAsync = async ( */ try { const releaseChannel = await getNativelyDefinedReleaseChannelAsync(ctx, platform); + assert(releaseChannel, 'release channel is not defined natively'); ctx.logger.info( `Using the release channel pre-configured in native project (${releaseChannel})` ); From 5fd42d72ef2dfb46e32ab44ac611f518dac31df2 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 22 May 2021 07:43:27 -0700 Subject: [PATCH 14/28] clean up --- packages/build-tools/src/builders/androidGeneric.ts | 1 - packages/build-tools/src/builders/iosManaged.ts | 3 --- packages/build-tools/src/utils/expoUpdates.ts | 5 ----- 3 files changed, 9 deletions(-) diff --git a/packages/build-tools/src/builders/androidGeneric.ts b/packages/build-tools/src/builders/androidGeneric.ts index a670c227..07b52fe8 100644 --- a/packages/build-tools/src/builders/androidGeneric.ts +++ b/packages/build-tools/src/builders/androidGeneric.ts @@ -32,7 +32,6 @@ export default async function androidGenericBuilder( } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - console.log('In androidGeneric, starting configureExpoUpdatesIfInstalled'); await configureExpoUpdatesIfInstalledAsync(ctx, Platform.ANDROID); }); diff --git a/packages/build-tools/src/builders/iosManaged.ts b/packages/build-tools/src/builders/iosManaged.ts index 0e5a979a..ec4137f2 100644 --- a/packages/build-tools/src/builders/iosManaged.ts +++ b/packages/build-tools/src/builders/iosManaged.ts @@ -16,9 +16,6 @@ import { installPods } from '../ios/pod'; export default async function iosManagedBuilder( ctx: ManagedBuildContext ): Promise { - console.log('STARTING IOS MANAGED'); - console.log('STARTING IOS MANAGED'); - console.log('STARTING IOS MANAGED'); await setup(ctx); await ctx.runBuildPhase(BuildPhase.PREBUILD, async () => { diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index 99b64b9e..38ae08af 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -122,11 +122,6 @@ export const configureClassicExpoUpdatesAsync = async ( ); ctx.logger.warn('Please add the "releaseChannel" field to your build profile (eas.json)'); } catch (_) { - // only difference between generic and managed is that managed doesn't even try to look for the release channel in native code. - // if (ctx instanceof ManagedBuildContext) { - // return undefined; - // } - ctx.logger.info(`Using default release channel for 'expo-updates' (default)`); } } From 1f47d1932d7e972813801f925e0bda9f4666c696 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 22 May 2021 07:46:24 -0700 Subject: [PATCH 15/28] fix lint --- packages/build-tools/src/android/expoUpdates.ts | 2 +- packages/build-tools/src/utils/__tests__/expoUpdates.test.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index de693c42..65f910d1 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -71,7 +71,7 @@ export const androidSetReleaseChannelNativelyAsync = async ( }; export const androidGetNativelyDefinedReleaseChannelAsync = async ( - ctx: ManagedBuildContext | BuildContext, + ctx: ManagedBuildContext | BuildContext ): Promise => { const manifestPath = path.join( getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), diff --git a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts index a1e739cb..89a469de 100644 --- a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts @@ -1,4 +1,3 @@ - import { Platform } from '@expo/eas-build-job'; import { ManagedBuildContext, ManagedJob } from '../../managed/context'; From 803d78922612d8722dfaeff332ad8bbda75f2c34 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 22 May 2021 08:10:55 -0700 Subject: [PATCH 16/28] remove uuid --- packages/build-tools/package.json | 3 +-- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/build-tools/package.json b/packages/build-tools/package.json index 629f8408..e579a19a 100644 --- a/packages/build-tools/package.json +++ b/packages/build-tools/package.json @@ -34,8 +34,7 @@ "fs-extra": "^9.0.0", "node-forge": "^0.9.1", "nullthrows": "^1.1.1", - "plist": "^3.0.1", - "uuid": "^3.3.3" + "plist": "^3.0.1" }, "devDependencies": { "@types/fs-extra": "^9.0.1", diff --git a/yarn.lock b/yarn.lock index 679caf74..18a16598 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15225,7 +15225,7 @@ uuid@^2.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" integrity sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho= -uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2, uuid@^3.3.3, uuid@^3.4.0: +uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== From 1a637d18cc42175376578609de6135666cc7ae58 Mon Sep 17 00:00:00 2001 From: jkh Date: Mon, 24 May 2021 19:16:48 -0700 Subject: [PATCH 17/28] Apply suggestions from code review Co-authored-by: Wojciech Kozyra Co-authored-by: Dominik Sokal --- packages/build-tools/src/android/expoUpdates.ts | 2 ++ packages/build-tools/src/ios/expoUpdates.ts | 2 +- packages/eas-build-job/src/__tests__/android.test.ts | 2 +- packages/eas-build-job/src/__tests__/ios.test.ts | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index 65f910d1..dcc4702f 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -7,10 +7,12 @@ import { AndroidConfig } from '@expo/config-plugins'; import { ManagedBuildContext, ManagedJob } from '../managed/context'; import { BuildContext } from '../context'; import { GenericJob } from '../utils/expoUpdates'; + export enum AndroidMetadataName { UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY', RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL', } + export function getAndroidManifestDirectory(reactNativeProjectDirectory: string): string { return path.join(reactNativeProjectDirectory, 'android', 'app', 'src', 'main'); } diff --git a/packages/build-tools/src/ios/expoUpdates.ts b/packages/build-tools/src/ios/expoUpdates.ts index f83091a9..956ed1b4 100644 --- a/packages/build-tools/src/ios/expoUpdates.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -72,7 +72,7 @@ export const iosSetReleaseChannelNativelyAsync = async ( let items: Record> = {}; if (!(await fs.pathExists(expoPlistPath))) { - throw new Error(`${expoPlistPath} does no exist`); + throw new Error(`${expoPlistPath} does not exist`); } const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); diff --git a/packages/eas-build-job/src/__tests__/android.test.ts b/packages/eas-build-job/src/__tests__/android.test.ts index 8eba5c7d..b54ffc0e 100644 --- a/packages/eas-build-job/src/__tests__/android.test.ts +++ b/packages/eas-build-job/src/__tests__/android.test.ts @@ -142,7 +142,7 @@ describe('Android.ManagedJobSchema', () => { expect(value).toMatchObject(managedJob); expect(error).toBeFalsy(); }); - test('fails when both releaseChannel and updates.channel are defined.', () => { + test('fails when both releaseChannel and updates.channel are defined', () => { const managedJob = { secrets, type: Workflow.MANAGED, diff --git a/packages/eas-build-job/src/__tests__/ios.test.ts b/packages/eas-build-job/src/__tests__/ios.test.ts index d2f74f28..1cf14cd1 100644 --- a/packages/eas-build-job/src/__tests__/ios.test.ts +++ b/packages/eas-build-job/src/__tests__/ios.test.ts @@ -154,7 +154,7 @@ describe('Ios.ManagedJobSchema', () => { expect(value).toMatchObject(managedJob); expect(error).toBeFalsy(); }); - test('fails when both releaseChannel and updates.channel are defined.', () => { + test('fails when both releaseChannel and updates.channel are defined', () => { const managedJob = { secrets: { buildCredentials, From 300f55ec02866c486ad823f91ba81cf4ceeb6641 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 19:48:23 -0700 Subject: [PATCH 18/28] use getAndroidManifestAsync --- .../src/android/__tests__/expoUpdates.test.ts | 22 +++++++++++++------ .../build-tools/src/android/expoUpdates.ts | 21 ++++++------------ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/build-tools/src/android/__tests__/expoUpdates.test.ts b/packages/build-tools/src/android/__tests__/expoUpdates.test.ts index cca83bcd..6402e322 100644 --- a/packages/build-tools/src/android/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/android/__tests__/expoUpdates.test.ts @@ -5,7 +5,6 @@ import { AndroidConfig } from '@expo/config-plugins'; import { AndroidMetadataName, - getAndroidManifestDirectory, androidGetNativelyDefinedReleaseChannelAsync, androidSetChannelNativelyAsync, androidSetReleaseChannelNativelyAsync, @@ -54,8 +53,11 @@ describe(androidSetReleaseChannelNativelyAsync, () => { logger: { info: () => {} }, }; - const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory); - const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + fs.ensureDirSync(path.join(reactNativeProjectDirectory, 'android')); + const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( + reactNativeProjectDirectory + ); + const manifestDirectory = path.dirname(manifestPath); fs.ensureDirSync(manifestDirectory); fs.writeFileSync(manifestPath, Buffer.from(noMetadataAndroidManifest)); @@ -85,8 +87,11 @@ describe(androidSetChannelNativelyAsync, () => { logger: { info: () => {} }, }; - const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory); - const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + fs.ensureDirSync(path.join(reactNativeProjectDirectory, 'android')); + const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( + reactNativeProjectDirectory + ); + const manifestDirectory = path.dirname(manifestPath); fs.ensureDirSync(manifestDirectory); fs.writeFileSync(manifestPath, noMetadataAndroidManifest); @@ -134,8 +139,11 @@ describe(androidGetNativelyDefinedReleaseChannelAsync, () => { `; - const manifestDirectory = getAndroidManifestDirectory(reactNativeProjectDirectory); - const manifestPath = path.join(manifestDirectory, 'AndroidManifest.xml'); + fs.ensureDirSync(path.join(reactNativeProjectDirectory, 'android')); + const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( + reactNativeProjectDirectory + ); + const manifestDirectory = path.dirname(manifestPath); fs.ensureDirSync(manifestDirectory); fs.writeFileSync(manifestPath, releaseChannelInAndroidManifest); diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index dcc4702f..0d937e28 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -1,4 +1,3 @@ -import path from 'path'; import assert from 'assert'; import fs from 'fs-extra'; @@ -13,19 +12,15 @@ export enum AndroidMetadataName { RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL', } -export function getAndroidManifestDirectory(reactNativeProjectDirectory: string): string { - return path.join(reactNativeProjectDirectory, 'android', 'app', 'src', 'main'); -} - export async function androidSetChannelNativelyAsync( ctx: ManagedBuildContext | BuildContext ): Promise { assert(ctx.job.updates?.channel, 'updates.channel must be defined'); - const manifestPath = path.join( - getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), - 'AndroidManifest.xml' + const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( + ctx.reactNativeProjectDirectory ); + if (!(await fs.pathExists(manifestPath))) { throw new Error(`Couldn't find Android manifest at ${manifestPath}`); } @@ -53,9 +48,8 @@ export const androidSetReleaseChannelNativelyAsync = async ( ): Promise => { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); - const manifestPath = path.join( - getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), - 'AndroidManifest.xml' + const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( + ctx.reactNativeProjectDirectory ); if (!(await fs.pathExists(manifestPath))) { throw new Error(`Couldn't find Android manifest at ${manifestPath}`); @@ -75,9 +69,8 @@ export const androidSetReleaseChannelNativelyAsync = async ( export const androidGetNativelyDefinedReleaseChannelAsync = async ( ctx: ManagedBuildContext | BuildContext ): Promise => { - const manifestPath = path.join( - getAndroidManifestDirectory(ctx.reactNativeProjectDirectory), - 'AndroidManifest.xml' + const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( + ctx.reactNativeProjectDirectory ); if (!(await fs.pathExists(manifestPath))) { return; From 9b70011d799b62a5280aeef9df2bf8b6429a2794 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 19:50:17 -0700 Subject: [PATCH 19/28] rename to ...classic... --- .../build-tools/src/android/__tests__/expoUpdates.test.ts | 6 +++--- packages/build-tools/src/android/expoUpdates.ts | 2 +- packages/build-tools/src/utils/expoUpdates.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/build-tools/src/android/__tests__/expoUpdates.test.ts b/packages/build-tools/src/android/__tests__/expoUpdates.test.ts index 6402e322..bdc49e5e 100644 --- a/packages/build-tools/src/android/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/android/__tests__/expoUpdates.test.ts @@ -7,7 +7,7 @@ import { AndroidMetadataName, androidGetNativelyDefinedReleaseChannelAsync, androidSetChannelNativelyAsync, - androidSetReleaseChannelNativelyAsync, + androidSetClassicReleaseChannelNativelyAsync, } from '../expoUpdates'; jest.mock('fs'); @@ -42,7 +42,7 @@ const noMetadataAndroidManifest = ` `; -describe(androidSetReleaseChannelNativelyAsync, () => { +describe(androidSetClassicReleaseChannelNativelyAsync, () => { test('sets the release channel', async () => { const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); fs.ensureDirSync(reactNativeProjectDirectory); @@ -66,7 +66,7 @@ describe(androidSetReleaseChannelNativelyAsync, () => { AndroidConfig.Manifest.getMainApplicationMetaDataValue(androidManifest, 'releaseChannel') ).toBe(null); - await androidSetReleaseChannelNativelyAsync(ctx as any); + await androidSetClassicReleaseChannelNativelyAsync(ctx as any); const newAndroidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); expect( diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index 0d937e28..4aca7a02 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -43,7 +43,7 @@ export async function androidSetChannelNativelyAsync( await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); } -export const androidSetReleaseChannelNativelyAsync = async ( +export const androidSetClassicReleaseChannelNativelyAsync = async ( ctx: ManagedBuildContext | BuildContext ): Promise => { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index 38ae08af..bd7e8346 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -4,7 +4,7 @@ import { Ios, Android, Platform } from '@expo/eas-build-job'; import { androidSetChannelNativelyAsync, - androidSetReleaseChannelNativelyAsync, + androidSetClassicReleaseChannelNativelyAsync, androidGetNativelyDefinedReleaseChannelAsync, } from '../android/expoUpdates'; import { @@ -69,7 +69,7 @@ export const setReleaseChannelNativelyAsync = async ( switch (platform) { case Platform.ANDROID: { - await androidSetReleaseChannelNativelyAsync(ctx); + await androidSetClassicReleaseChannelNativelyAsync(ctx); return; } case Platform.IOS: { From eb3b79e27ea741bc292922a885a2661315c68d88 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 19:55:18 -0700 Subject: [PATCH 20/28] simplify typing --- packages/build-tools/src/android/expoUpdates.ts | 11 ++++------- packages/build-tools/src/ios/expoUpdates.ts | 13 ++++--------- packages/build-tools/src/utils/expoUpdates.ts | 15 +++++++-------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index 4aca7a02..166e6ecc 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -2,19 +2,16 @@ import assert from 'assert'; import fs from 'fs-extra'; import { AndroidConfig } from '@expo/config-plugins'; +import { Job } from '@expo/eas-build-job'; -import { ManagedBuildContext, ManagedJob } from '../managed/context'; import { BuildContext } from '../context'; -import { GenericJob } from '../utils/expoUpdates'; export enum AndroidMetadataName { UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY', RELEASE_CHANNEL = 'expo.modules.updates.EXPO_RELEASE_CHANNEL', } -export async function androidSetChannelNativelyAsync( - ctx: ManagedBuildContext | BuildContext -): Promise { +export async function androidSetChannelNativelyAsync(ctx: BuildContext): Promise { assert(ctx.job.updates?.channel, 'updates.channel must be defined'); const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( @@ -44,7 +41,7 @@ export async function androidSetChannelNativelyAsync( } export const androidSetClassicReleaseChannelNativelyAsync = async ( - ctx: ManagedBuildContext | BuildContext + ctx: BuildContext ): Promise => { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); @@ -67,7 +64,7 @@ export const androidSetClassicReleaseChannelNativelyAsync = async ( }; export const androidGetNativelyDefinedReleaseChannelAsync = async ( - ctx: ManagedBuildContext | BuildContext + ctx: BuildContext ): Promise => { const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( ctx.reactNativeProjectDirectory diff --git a/packages/build-tools/src/ios/expoUpdates.ts b/packages/build-tools/src/ios/expoUpdates.ts index 956ed1b4..0eca83dc 100644 --- a/packages/build-tools/src/ios/expoUpdates.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -4,10 +4,9 @@ import assert from 'assert'; import fs from 'fs-extra'; import plist from '@expo/plist'; import fg from 'fast-glob'; +import { Job } from '@expo/eas-build-job'; -import { ManagedBuildContext, ManagedJob } from '../managed/context'; import { BuildContext } from '../context'; -import { GenericJob } from '../utils/expoUpdates'; export enum IosMetadataName { UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = 'EXUpdatesRequestHeaders', @@ -31,9 +30,7 @@ export async function getExpoPlistDirectoryAsync( ); } -export async function iosSetChannelNativelyAsync( - ctx: ManagedBuildContext | BuildContext -): Promise { +export async function iosSetChannelNativelyAsync(ctx: BuildContext): Promise { assert(ctx.job.updates?.channel, 'updates.channel must be defined'); const expoPlistPath = path.resolve( @@ -60,9 +57,7 @@ export async function iosSetChannelNativelyAsync( await fs.writeFile(expoPlistPath, expoPlist); } -export const iosSetReleaseChannelNativelyAsync = async ( - ctx: ManagedBuildContext | BuildContext -): Promise => { +export const iosSetReleaseChannelNativelyAsync = async (ctx: BuildContext): Promise => { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); const expoPlistPath = path.resolve( @@ -84,7 +79,7 @@ export const iosSetReleaseChannelNativelyAsync = async ( }; export const iosGetNativelyDefinedReleaseChannelAsync = async ( - ctx: ManagedBuildContext | BuildContext + ctx: BuildContext ): Promise => { const expoPlistPath = path.resolve( await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index bd7e8346..0d667bd9 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -1,6 +1,6 @@ import assert from 'assert'; -import { Ios, Android, Platform } from '@expo/eas-build-job'; +import { Ios, Android, Platform, Job } from '@expo/eas-build-job'; import { androidSetChannelNativelyAsync, @@ -13,7 +13,6 @@ import { iosGetNativelyDefinedReleaseChannelAsync, } from '../ios/expoUpdates'; import { BuildContext } from '../context'; -import { ManagedBuildContext, ManagedJob } from '../managed/context'; import isExpoUpdatesInstalledAsync from './isExpoUpdatesInstalled'; export type GenericJob = Ios.GenericJob | Android.GenericJob; @@ -24,7 +23,7 @@ export type GenericJob = Ios.GenericJob | Android.GenericJob; * @param platform */ export const setChannelNativelyAsync = async ( - ctx: ManagedBuildContext | BuildContext, + ctx: BuildContext, platform: Platform ): Promise => { assert(ctx.job.updates?.channel, 'updates.channel must be defined'); @@ -59,7 +58,7 @@ export const setChannelNativelyAsync = async ( * @param platform */ export const setReleaseChannelNativelyAsync = async ( - ctx: ManagedBuildContext | BuildContext, + ctx: BuildContext, platform: Platform ): Promise => { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); @@ -87,7 +86,7 @@ export const setReleaseChannelNativelyAsync = async ( * @param platform */ export const getNativelyDefinedReleaseChannelAsync = async ( - ctx: ManagedBuildContext | BuildContext, + ctx: BuildContext, platform: Platform ): Promise => { switch (platform) { @@ -103,7 +102,7 @@ export const getNativelyDefinedReleaseChannelAsync = async ( }; export const configureClassicExpoUpdatesAsync = async ( - ctx: ManagedBuildContext | BuildContext, + ctx: BuildContext, platform: Platform ): Promise => { if (ctx.job.releaseChannel) { @@ -128,14 +127,14 @@ export const configureClassicExpoUpdatesAsync = async ( }; export const configureEASExpoUpdatesAsync = async ( - ctx: ManagedBuildContext | BuildContext, + ctx: BuildContext, platform: Platform ): Promise => { await setChannelNativelyAsync(ctx, platform); }; export const configureExpoUpdatesIfInstalledAsync = async ( - ctx: ManagedBuildContext | BuildContext, + ctx: BuildContext, platform: Platform ): Promise => { if (!(await isExpoUpdatesInstalledAsync(ctx.reactNativeProjectDirectory))) { From 75cf09580ed49f5437725f409efe20545e2fcafd Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 20:07:04 -0700 Subject: [PATCH 21/28] remove redundant platform parameter --- .../src/builders/androidGeneric.ts | 4 +- .../src/builders/androidManaged.ts | 4 +- .../build-tools/src/builders/iosGeneric.ts | 4 +- .../build-tools/src/builders/iosManaged.ts | 4 +- .../src/utils/__tests__/expoUpdates.test.ts | 24 +++++----- packages/build-tools/src/utils/expoUpdates.ts | 48 +++++++------------ 6 files changed, 38 insertions(+), 50 deletions(-) diff --git a/packages/build-tools/src/builders/androidGeneric.ts b/packages/build-tools/src/builders/androidGeneric.ts index 07b52fe8..7b549b09 100644 --- a/packages/build-tools/src/builders/androidGeneric.ts +++ b/packages/build-tools/src/builders/androidGeneric.ts @@ -1,4 +1,4 @@ -import { Android, BuildPhase, Platform } from '@expo/eas-build-job'; +import { Android, BuildPhase } from '@expo/eas-build-job'; import { BuildContext } from '../context'; import { setup } from '../utils/project'; @@ -32,7 +32,7 @@ export default async function androidGenericBuilder( } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - await configureExpoUpdatesIfInstalledAsync(ctx, Platform.ANDROID); + await configureExpoUpdatesIfInstalledAsync(ctx); }); await ctx.runBuildPhase(BuildPhase.RUN_GRADLEW, async () => { diff --git a/packages/build-tools/src/builders/androidManaged.ts b/packages/build-tools/src/builders/androidManaged.ts index bec3bdf5..9620c62e 100644 --- a/packages/build-tools/src/builders/androidManaged.ts +++ b/packages/build-tools/src/builders/androidManaged.ts @@ -1,5 +1,5 @@ import { AndroidConfig } from '@expo/config-plugins'; -import { Android, BuildPhase, Platform } from '@expo/eas-build-job'; +import { Android, BuildPhase } from '@expo/eas-build-job'; import { ManagedBuildContext } from '../managed/context'; import { configureExpoUpdatesIfInstalledAsync } from '../utils/expoUpdates'; @@ -32,7 +32,7 @@ export default async function androidManagedBuilder( }); } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - await configureExpoUpdatesIfInstalledAsync(ctx, Platform.ANDROID); + await configureExpoUpdatesIfInstalledAsync(ctx); }); await ctx.runBuildPhase(BuildPhase.RUN_GRADLEW, async () => { diff --git a/packages/build-tools/src/builders/iosGeneric.ts b/packages/build-tools/src/builders/iosGeneric.ts index 48b50419..f818f2be 100644 --- a/packages/build-tools/src/builders/iosGeneric.ts +++ b/packages/build-tools/src/builders/iosGeneric.ts @@ -1,4 +1,4 @@ -import { BuildPhase, Ios, Platform } from '@expo/eas-build-job'; +import { BuildPhase, Ios } from '@expo/eas-build-job'; import { BuildContext } from '../context'; import { setup } from '../utils/project'; @@ -39,7 +39,7 @@ export default async function iosGenericBuilder( } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - await configureExpoUpdatesIfInstalledAsync(ctx, Platform.IOS); + await configureExpoUpdatesIfInstalledAsync(ctx); }); await ctx.runBuildPhase(BuildPhase.RUN_FASTLANE, async () => { diff --git a/packages/build-tools/src/builders/iosManaged.ts b/packages/build-tools/src/builders/iosManaged.ts index ec4137f2..ac8c55e1 100644 --- a/packages/build-tools/src/builders/iosManaged.ts +++ b/packages/build-tools/src/builders/iosManaged.ts @@ -1,7 +1,7 @@ import assert from 'assert'; import { IOSConfig } from '@expo/config-plugins'; -import { BuildPhase, Ios, Platform } from '@expo/eas-build-job'; +import { BuildPhase, Ios } from '@expo/eas-build-job'; import { ManagedBuildContext } from '../managed/context'; import { configureExpoUpdatesIfInstalledAsync } from '../utils/expoUpdates'; @@ -46,7 +46,7 @@ export default async function iosManagedBuilder( } await ctx.runBuildPhase(BuildPhase.CONFIGURE_EXPO_UPDATES, async () => { - await configureExpoUpdatesIfInstalledAsync(ctx, Platform.IOS); + await configureExpoUpdatesIfInstalledAsync(ctx); }); await ctx.runBuildPhase(BuildPhase.RUN_FASTLANE, async () => { diff --git a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts index 89a469de..dac2d218 100644 --- a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts @@ -13,7 +13,9 @@ describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { jest.spyOn(expoUpdates, 'configureEASExpoUpdatesAsync'); jest.spyOn(expoUpdates, 'configureClassicExpoUpdatesAsync'); - await expoUpdates.configureExpoUpdatesIfInstalledAsync({} as any, Platform.IOS); + await expoUpdates.configureExpoUpdatesIfInstalledAsync({ + job: { Platform: Platform.IOS }, + } as any); expect(expoUpdates.configureEASExpoUpdatesAsync).not.toBeCalled(); expect(expoUpdates.configureClassicExpoUpdatesAsync).not.toBeCalled(); @@ -26,9 +28,9 @@ describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { jest.spyOn(expoUpdates, 'configureClassicExpoUpdatesAsync'); const managedCtx: ManagedBuildContext = { - job: { updates: { channel: 'main' } }, + job: { updates: { channel: 'main' }, Platform: Platform.IOS }, } as any; - await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx); expect(expoUpdates.configureEASExpoUpdatesAsync).toBeCalledTimes(1); expect(expoUpdates.configureClassicExpoUpdatesAsync).not.toBeCalled(); @@ -41,10 +43,10 @@ describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { jest.spyOn(expoUpdates, 'configureClassicExpoUpdatesAsync'); const managedCtx: ManagedBuildContext = { - job: {}, + job: { platform: Platform.IOS }, logger: { info: () => {} }, } as any; - await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx); expect(expoUpdates.configureEASExpoUpdatesAsync).not.toBeCalled(); expect(expoUpdates.configureClassicExpoUpdatesAsync).toBeCalledTimes(1); @@ -58,9 +60,9 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { jest.spyOn(expoUpdates, 'setReleaseChannelNativelyAsync').mockImplementation(); const managedCtx: ManagedBuildContext = { - job: { releaseChannel: 'default' }, + job: { releaseChannel: 'default', platform: Platform.IOS }, } as any; - await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx); expect(expoUpdates.setReleaseChannelNativelyAsync).toBeCalledTimes(1); }); @@ -69,10 +71,10 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { jest.spyOn(expoUpdates, 'getNativelyDefinedReleaseChannelAsync').mockImplementation(); const managedCtx: ManagedBuildContext = { - job: {}, + job: { platform: Platform.IOS }, logger: { info: () => {}, warn: () => {} }, } as any; - await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx); expect(expoUpdates.getNativelyDefinedReleaseChannelAsync).toBeCalledTimes(1); }); @@ -84,10 +86,10 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { const infoLogger = jest.fn(); const managedCtx: ManagedBuildContext = { - job: {}, + job: { platform: Platform.IOS }, logger: { info: infoLogger }, } as any; - await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx, Platform.IOS); + await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx); expect(infoLogger).toBeCalledWith(`Using default release channel for 'expo-updates' (default)`); }); diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index 0d667bd9..cb733ee8 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -22,10 +22,7 @@ export type GenericJob = Ios.GenericJob | Android.GenericJob; * @param ctx * @param platform */ -export const setChannelNativelyAsync = async ( - ctx: BuildContext, - platform: Platform -): Promise => { +export const setChannelNativelyAsync = async (ctx: BuildContext): Promise => { assert(ctx.job.updates?.channel, 'updates.channel must be defined'); const newUpdateRequestHeaders: Record = { 'expo-channel-name': ctx.job.updates.channel, @@ -38,7 +35,7 @@ export const setChannelNativelyAsync = async ( )}'` ); - switch (platform) { + switch (ctx.job.platform) { case Platform.ANDROID: { await androidSetChannelNativelyAsync(ctx); return; @@ -48,7 +45,7 @@ export const setChannelNativelyAsync = async ( return; } default: - throw new Error(`Platform ${platform} is not supported.`); + throw new Error(`Platform is not supported.`); } }; @@ -57,16 +54,13 @@ export const setChannelNativelyAsync = async ( * @param ctx * @param platform */ -export const setReleaseChannelNativelyAsync = async ( - ctx: BuildContext, - platform: Platform -): Promise => { +export const setReleaseChannelNativelyAsync = async (ctx: BuildContext): Promise => { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); const configFile = ctx.job.platform === Platform.ANDROID ? 'AndroidManifest.xml' : 'Expo.plist'; ctx.logger.info(`Setting the release channel in '${configFile}' to '${ctx.job.releaseChannel}'`); - switch (platform) { + switch (ctx.job.platform) { case Platform.ANDROID: { await androidSetClassicReleaseChannelNativelyAsync(ctx); return; @@ -76,7 +70,7 @@ export const setReleaseChannelNativelyAsync = async ( return; } default: - throw new Error(`Platform ${platform} is not supported.`); + throw new Error(`Platform is not supported.`); } }; @@ -86,10 +80,9 @@ export const setReleaseChannelNativelyAsync = async ( * @param platform */ export const getNativelyDefinedReleaseChannelAsync = async ( - ctx: BuildContext, - platform: Platform + ctx: BuildContext ): Promise => { - switch (platform) { + switch (ctx.job.platform) { case Platform.ANDROID: { return androidGetNativelyDefinedReleaseChannelAsync(ctx); } @@ -97,16 +90,13 @@ export const getNativelyDefinedReleaseChannelAsync = async ( return iosGetNativelyDefinedReleaseChannelAsync(ctx); } default: - throw new Error(`Platform ${platform} is not supported.`); + throw new Error(`Platform is not supported.`); } }; -export const configureClassicExpoUpdatesAsync = async ( - ctx: BuildContext, - platform: Platform -): Promise => { +export const configureClassicExpoUpdatesAsync = async (ctx: BuildContext): Promise => { if (ctx.job.releaseChannel) { - await setReleaseChannelNativelyAsync(ctx, platform); + await setReleaseChannelNativelyAsync(ctx); } else { /** * If releaseChannel is not defined: @@ -114,7 +104,7 @@ export const configureClassicExpoUpdatesAsync = async ( * 2. If it is not set, fallback to 'default'. */ try { - const releaseChannel = await getNativelyDefinedReleaseChannelAsync(ctx, platform); + const releaseChannel = await getNativelyDefinedReleaseChannelAsync(ctx); assert(releaseChannel, 'release channel is not defined natively'); ctx.logger.info( `Using the release channel pre-configured in native project (${releaseChannel})` @@ -126,16 +116,12 @@ export const configureClassicExpoUpdatesAsync = async ( } }; -export const configureEASExpoUpdatesAsync = async ( - ctx: BuildContext, - platform: Platform -): Promise => { - await setChannelNativelyAsync(ctx, platform); +export const configureEASExpoUpdatesAsync = async (ctx: BuildContext): Promise => { + await setChannelNativelyAsync(ctx); }; export const configureExpoUpdatesIfInstalledAsync = async ( - ctx: BuildContext, - platform: Platform + ctx: BuildContext ): Promise => { if (!(await isExpoUpdatesInstalledAsync(ctx.reactNativeProjectDirectory))) { return; @@ -143,11 +129,11 @@ export const configureExpoUpdatesIfInstalledAsync = async ( switch (true) { case !!ctx.job.updates?.channel: { - await configureEASExpoUpdatesAsync(ctx, platform); + await configureEASExpoUpdatesAsync(ctx); return; } default: { - await configureClassicExpoUpdatesAsync(ctx, platform); + await configureClassicExpoUpdatesAsync(ctx); } } }; From c1e7bab56d44c7eebeb7b88fd19bb3fc2860a2fd Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 20:26:42 -0700 Subject: [PATCH 22/28] use IOSConfig --- .../src/ios/__tests__/expoUpdates.test.ts | 30 ++++++++++---- packages/build-tools/src/ios/expoUpdates.ts | 41 +++---------------- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts b/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts index c4bc6d69..1fcaf155 100644 --- a/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts @@ -2,9 +2,9 @@ import path from 'path'; import fs from 'fs-extra'; import plist from '@expo/plist'; +import { IOSConfig } from '@expo/config-plugins'; import { - getExpoPlistDirectoryAsync, iosGetNativelyDefinedReleaseChannelAsync, IosMetadataName, iosSetChannelNativelyAsync, @@ -33,14 +33,14 @@ describe(iosSetReleaseChannelNativelyAsync, () => { logger: { info: () => {} }, }; - fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/')); + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/test/')); fs.writeFileSync( - path.join(reactNativeProjectDirectory, '/ios/Pods.xcodeproj/project.pbxproj'), + path.join(reactNativeProjectDirectory, '/ios/test/AppDelegate.m'), Buffer.from('placeholder') ); - const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); - const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); + const expoPlistDirectory = path.dirname(expoPlistPath); fs.ensureDirSync(expoPlistDirectory); fs.writeFileSync(expoPlistPath, noItemsExpoPlist); @@ -68,8 +68,14 @@ describe(iosSetChannelNativelyAsync, () => { Buffer.from('placeholder') ); - const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); - const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/test/')); + fs.writeFileSync( + path.join(reactNativeProjectDirectory, '/ios/test/AppDelegate.m'), + Buffer.from('placeholder') + ); + + const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); + const expoPlistDirectory = path.dirname(expoPlistPath); fs.ensureDirSync(expoPlistDirectory); fs.writeFileSync(expoPlistPath, noItemsExpoPlist); @@ -109,8 +115,14 @@ describe(iosGetNativelyDefinedReleaseChannelAsync, () => { Buffer.from('placeholder') ); - const expoPlistDirectory = await getExpoPlistDirectoryAsync(reactNativeProjectDirectory); - const expoPlistPath = path.join(expoPlistDirectory, 'Expo.plist'); + fs.ensureDirSync(path.join(reactNativeProjectDirectory, '/ios/test/')); + fs.writeFileSync( + path.join(reactNativeProjectDirectory, '/ios/test/AppDelegate.m'), + Buffer.from('placeholder') + ); + + const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); + const expoPlistDirectory = path.dirname(expoPlistPath); fs.ensureDirSync(expoPlistDirectory); fs.writeFileSync(expoPlistPath, releaseChannelInPlist); diff --git a/packages/build-tools/src/ios/expoUpdates.ts b/packages/build-tools/src/ios/expoUpdates.ts index 0eca83dc..c81a3ac0 100644 --- a/packages/build-tools/src/ios/expoUpdates.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -1,9 +1,8 @@ -import path from 'path'; import assert from 'assert'; +import { IOSConfig } from '@expo/config-plugins'; import fs from 'fs-extra'; import plist from '@expo/plist'; -import fg from 'fast-glob'; import { Job } from '@expo/eas-build-job'; import { BuildContext } from '../context'; @@ -13,38 +12,17 @@ export enum IosMetadataName { RELEASE_CHANNEL = 'EXUpdatesReleaseChannel', } -export async function getExpoPlistDirectoryAsync( - reactNativeProjectDirectory: string -): Promise { - const pbxprojPaths = await fg('ios/*/project.pbxproj', { cwd: reactNativeProjectDirectory }); - const pbxprojPath = pbxprojPaths.length > 0 ? pbxprojPaths[0] : undefined; - if (!pbxprojPath) { - throw new Error(`Couldn't find an iOS project at '${reactNativeProjectDirectory}'`); - } - const xcodeprojPath = path.resolve(pbxprojPath, '..'); - return path.resolve( - reactNativeProjectDirectory, - 'ios', - path.basename(xcodeprojPath).replace(/\.xcodeproj$/, ''), - 'Supporting' - ); -} - export async function iosSetChannelNativelyAsync(ctx: BuildContext): Promise { assert(ctx.job.updates?.channel, 'updates.channel must be defined'); - const expoPlistPath = path.resolve( - await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), - 'Expo.plist' - ); + const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); - let items: Record> = {}; if (!(await fs.pathExists(expoPlistPath))) { throw new Error(`${expoPlistPath} does no exist`); } const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); - items = plist.parse(expoPlistContent); + const items: Record> = plist.parse(expoPlistContent); items[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] = { ...((items[IosMetadataName.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY] as Record< string, @@ -60,18 +38,14 @@ export async function iosSetChannelNativelyAsync(ctx: BuildContext): Promis export const iosSetReleaseChannelNativelyAsync = async (ctx: BuildContext): Promise => { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); - const expoPlistPath = path.resolve( - await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), - 'Expo.plist' - ); + const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); - let items: Record> = {}; if (!(await fs.pathExists(expoPlistPath))) { throw new Error(`${expoPlistPath} does not exist`); } const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); - items = plist.parse(expoPlistContent); + const items: Record> = plist.parse(expoPlistContent); items[IosMetadataName.RELEASE_CHANNEL] = ctx.job.releaseChannel; const expoPlist = plist.build(items); @@ -81,10 +55,7 @@ export const iosSetReleaseChannelNativelyAsync = async (ctx: BuildContext): export const iosGetNativelyDefinedReleaseChannelAsync = async ( ctx: BuildContext ): Promise => { - const expoPlistPath = path.resolve( - await getExpoPlistDirectoryAsync(ctx.reactNativeProjectDirectory), - 'Expo.plist' - ); + const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); if (!(await fs.pathExists(expoPlistPath))) { return; } From e4aa8c26032b6797332e3e975a644a9d8368df19 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 20:49:16 -0700 Subject: [PATCH 23/28] remove switch --- packages/build-tools/src/android/expoUpdates.ts | 12 ++++++------ packages/build-tools/src/ios/expoUpdates.ts | 10 +++++----- packages/build-tools/src/utils/expoUpdates.ts | 12 ++++-------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index 166e6ecc..9807fc03 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -40,9 +40,9 @@ export async function androidSetChannelNativelyAsync(ctx: BuildContext): Pr await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); } -export const androidSetClassicReleaseChannelNativelyAsync = async ( +export async function androidSetClassicReleaseChannelNativelyAsync( ctx: BuildContext -): Promise => { +): Promise { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( @@ -61,11 +61,11 @@ export const androidSetClassicReleaseChannelNativelyAsync = async ( 'value' ); await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); -}; +} -export const androidGetNativelyDefinedReleaseChannelAsync = async ( +export async function androidGetNativelyDefinedReleaseChannelAsync( ctx: BuildContext -): Promise => { +): Promise { const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( ctx.reactNativeProjectDirectory ); @@ -78,4 +78,4 @@ export const androidGetNativelyDefinedReleaseChannelAsync = async ( androidManifest, AndroidMetadataName.RELEASE_CHANNEL ); -}; +} diff --git a/packages/build-tools/src/ios/expoUpdates.ts b/packages/build-tools/src/ios/expoUpdates.ts index c81a3ac0..00c76b6e 100644 --- a/packages/build-tools/src/ios/expoUpdates.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -35,7 +35,7 @@ export async function iosSetChannelNativelyAsync(ctx: BuildContext): Promis await fs.writeFile(expoPlistPath, expoPlist); } -export const iosSetReleaseChannelNativelyAsync = async (ctx: BuildContext): Promise => { +export async function iosSetReleaseChannelNativelyAsync(ctx: BuildContext): Promise { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); @@ -50,11 +50,11 @@ export const iosSetReleaseChannelNativelyAsync = async (ctx: BuildContext): const expoPlist = plist.build(items); await fs.writeFile(expoPlistPath, expoPlist); -}; +} -export const iosGetNativelyDefinedReleaseChannelAsync = async ( +export async function iosGetNativelyDefinedReleaseChannelAsync( ctx: BuildContext -): Promise => { +): Promise { const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); if (!(await fs.pathExists(expoPlistPath))) { return; @@ -65,4 +65,4 @@ export const iosGetNativelyDefinedReleaseChannelAsync = async ( return; } return parsedPlist[IosMetadataName.RELEASE_CHANNEL]; -}; +} diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index cb733ee8..511f0843 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -127,13 +127,9 @@ export const configureExpoUpdatesIfInstalledAsync = async ( return; } - switch (true) { - case !!ctx.job.updates?.channel: { - await configureEASExpoUpdatesAsync(ctx); - return; - } - default: { - await configureClassicExpoUpdatesAsync(ctx); - } + if (ctx.job.updates?.channel) { + await configureEASExpoUpdatesAsync(ctx); + } else { + await configureClassicExpoUpdatesAsync(ctx); } }; From bccfc345c668459a037c87f8c6f9ea0f288bb8c8 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 20:53:17 -0700 Subject: [PATCH 24/28] remove undefined --- packages/build-tools/src/android/expoUpdates.ts | 4 ++-- packages/build-tools/src/ios/expoUpdates.ts | 8 ++++---- packages/build-tools/src/utils/expoUpdates.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index 9807fc03..4b29ddd9 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -65,12 +65,12 @@ export async function androidSetClassicReleaseChannelNativelyAsync( export async function androidGetNativelyDefinedReleaseChannelAsync( ctx: BuildContext -): Promise { +): Promise { const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( ctx.reactNativeProjectDirectory ); if (!(await fs.pathExists(manifestPath))) { - return; + return null; } const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(manifestPath); diff --git a/packages/build-tools/src/ios/expoUpdates.ts b/packages/build-tools/src/ios/expoUpdates.ts index 00c76b6e..58c9f08b 100644 --- a/packages/build-tools/src/ios/expoUpdates.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -54,15 +54,15 @@ export async function iosSetReleaseChannelNativelyAsync(ctx: BuildContext): export async function iosGetNativelyDefinedReleaseChannelAsync( ctx: BuildContext -): Promise { +): Promise { const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); if (!(await fs.pathExists(expoPlistPath))) { - return; + return null; } const expoPlistContent = await fs.readFile(expoPlistPath, 'utf8'); const parsedPlist = plist.parse(expoPlistContent); if (!parsedPlist) { - return; + return null; } - return parsedPlist[IosMetadataName.RELEASE_CHANNEL]; + return parsedPlist[IosMetadataName.RELEASE_CHANNEL] ?? null; } diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index 511f0843..053c4883 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -81,7 +81,7 @@ export const setReleaseChannelNativelyAsync = async (ctx: BuildContext): Pr */ export const getNativelyDefinedReleaseChannelAsync = async ( ctx: BuildContext -): Promise => { +): Promise => { switch (ctx.job.platform) { case Platform.ANDROID: { return androidGetNativelyDefinedReleaseChannelAsync(ctx); From d3327c10b50b1cda42b795dce058c0ef418733ee Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 20:56:24 -0700 Subject: [PATCH 25/28] remove assertion --- packages/build-tools/src/utils/expoUpdates.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index 053c4883..d95d3f6f 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -103,14 +103,13 @@ export const configureClassicExpoUpdatesAsync = async (ctx: BuildContext): * 1. Try to infer it from the native value. * 2. If it is not set, fallback to 'default'. */ - try { - const releaseChannel = await getNativelyDefinedReleaseChannelAsync(ctx); - assert(releaseChannel, 'release channel is not defined natively'); + const releaseChannel = await getNativelyDefinedReleaseChannelAsync(ctx); + if (releaseChannel) { ctx.logger.info( `Using the release channel pre-configured in native project (${releaseChannel})` ); ctx.logger.warn('Please add the "releaseChannel" field to your build profile (eas.json)'); - } catch (_) { + } else { ctx.logger.info(`Using default release channel for 'expo-updates' (default)`); } } From fb60fc26f9efaa3fcab08765a468c3f9f90e572b Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 20:59:46 -0700 Subject: [PATCH 26/28] rename more classic --- .../src/android/__tests__/expoUpdates.test.ts | 6 ++--- .../build-tools/src/android/expoUpdates.ts | 2 +- .../src/ios/__tests__/expoUpdates.test.ts | 12 +++++----- packages/build-tools/src/ios/expoUpdates.ts | 6 +++-- .../src/utils/__tests__/expoUpdates.test.ts | 4 ++-- packages/build-tools/src/utils/expoUpdates.ts | 22 ++++++++++--------- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/packages/build-tools/src/android/__tests__/expoUpdates.test.ts b/packages/build-tools/src/android/__tests__/expoUpdates.test.ts index bdc49e5e..acd5583f 100644 --- a/packages/build-tools/src/android/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/android/__tests__/expoUpdates.test.ts @@ -5,7 +5,7 @@ import { AndroidConfig } from '@expo/config-plugins'; import { AndroidMetadataName, - androidGetNativelyDefinedReleaseChannelAsync, + androidGetNativelyDefinedClassicReleaseChannelAsync, androidSetChannelNativelyAsync, androidSetClassicReleaseChannelNativelyAsync, } from '../expoUpdates'; @@ -115,7 +115,7 @@ describe(androidSetChannelNativelyAsync, () => { expect(JSON.parse(newValue!)).toEqual({ 'expo-channel-name': channel }); }); }); -describe(androidGetNativelyDefinedReleaseChannelAsync, () => { +describe(androidGetNativelyDefinedClassicReleaseChannelAsync, () => { it('gets the natively defined release channel', async () => { const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); fs.ensureDirSync(reactNativeProjectDirectory); @@ -148,7 +148,7 @@ describe(androidGetNativelyDefinedReleaseChannelAsync, () => { fs.ensureDirSync(manifestDirectory); fs.writeFileSync(manifestPath, releaseChannelInAndroidManifest); - const nativelyDefinedReleaseChannel = await androidGetNativelyDefinedReleaseChannelAsync( + const nativelyDefinedReleaseChannel = await androidGetNativelyDefinedClassicReleaseChannelAsync( ctx as any ); expect(nativelyDefinedReleaseChannel).toBe(releaseChannel); diff --git a/packages/build-tools/src/android/expoUpdates.ts b/packages/build-tools/src/android/expoUpdates.ts index 4b29ddd9..f28f8b98 100644 --- a/packages/build-tools/src/android/expoUpdates.ts +++ b/packages/build-tools/src/android/expoUpdates.ts @@ -63,7 +63,7 @@ export async function androidSetClassicReleaseChannelNativelyAsync( await AndroidConfig.Manifest.writeAndroidManifestAsync(manifestPath, androidManifest); } -export async function androidGetNativelyDefinedReleaseChannelAsync( +export async function androidGetNativelyDefinedClassicReleaseChannelAsync( ctx: BuildContext ): Promise { const manifestPath = await AndroidConfig.Paths.getAndroidManifestAsync( diff --git a/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts b/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts index 1fcaf155..2c9b99d8 100644 --- a/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/ios/__tests__/expoUpdates.test.ts @@ -5,10 +5,10 @@ import plist from '@expo/plist'; import { IOSConfig } from '@expo/config-plugins'; import { - iosGetNativelyDefinedReleaseChannelAsync, + iosGetNativelyDefinedClassicReleaseChannelAsync, IosMetadataName, iosSetChannelNativelyAsync, - iosSetReleaseChannelNativelyAsync, + iosSetClassicReleaseChannelNativelyAsync, } from '../../ios/expoUpdates'; jest.mock('fs'); @@ -22,7 +22,7 @@ const noItemsExpoPlist = ` `; const channel = 'main'; -describe(iosSetReleaseChannelNativelyAsync, () => { +describe(iosSetClassicReleaseChannelNativelyAsync, () => { test('sets the release channel', async () => { const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); fs.ensureDirSync(reactNativeProjectDirectory); @@ -45,7 +45,7 @@ describe(iosSetReleaseChannelNativelyAsync, () => { fs.ensureDirSync(expoPlistDirectory); fs.writeFileSync(expoPlistPath, noItemsExpoPlist); - await iosSetReleaseChannelNativelyAsync(ctx as any); + await iosSetClassicReleaseChannelNativelyAsync(ctx as any); const newExpoPlist = await fs.readFile(expoPlistPath, 'utf8'); expect(plist.parse(newExpoPlist)[IosMetadataName.RELEASE_CHANNEL]).toEqual(releaseChannel); @@ -89,7 +89,7 @@ describe(iosSetChannelNativelyAsync, () => { }); }); -describe(iosGetNativelyDefinedReleaseChannelAsync, () => { +describe(iosGetNativelyDefinedClassicReleaseChannelAsync, () => { it('gets the natively defined release channel', async () => { const reactNativeProjectDirectory = fs.mkdtempSync('/expo-project-'); fs.ensureDirSync(reactNativeProjectDirectory); @@ -127,7 +127,7 @@ describe(iosGetNativelyDefinedReleaseChannelAsync, () => { fs.ensureDirSync(expoPlistDirectory); fs.writeFileSync(expoPlistPath, releaseChannelInPlist); - const nativelyDefinedReleaseChannel = await iosGetNativelyDefinedReleaseChannelAsync( + const nativelyDefinedReleaseChannel = await iosGetNativelyDefinedClassicReleaseChannelAsync( ctx as any ); diff --git a/packages/build-tools/src/ios/expoUpdates.ts b/packages/build-tools/src/ios/expoUpdates.ts index 58c9f08b..ace05019 100644 --- a/packages/build-tools/src/ios/expoUpdates.ts +++ b/packages/build-tools/src/ios/expoUpdates.ts @@ -35,7 +35,9 @@ export async function iosSetChannelNativelyAsync(ctx: BuildContext): Promis await fs.writeFile(expoPlistPath, expoPlist); } -export async function iosSetReleaseChannelNativelyAsync(ctx: BuildContext): Promise { +export async function iosSetClassicReleaseChannelNativelyAsync( + ctx: BuildContext +): Promise { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); @@ -52,7 +54,7 @@ export async function iosSetReleaseChannelNativelyAsync(ctx: BuildContext): await fs.writeFile(expoPlistPath, expoPlist); } -export async function iosGetNativelyDefinedReleaseChannelAsync( +export async function iosGetNativelyDefinedClassicReleaseChannelAsync( ctx: BuildContext ): Promise { const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(ctx.reactNativeProjectDirectory); diff --git a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts index dac2d218..12f44c26 100644 --- a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts @@ -64,7 +64,7 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { } as any; await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx); - expect(expoUpdates.setReleaseChannelNativelyAsync).toBeCalledTimes(1); + expect(expoUpdates.setClassicReleaseChannelNativelyAsync).toBeCalledTimes(1); }); it('searches for the natively defined releaseChannel if it is not supplied by ctx.job.releaseChannel', async () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); @@ -76,7 +76,7 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { } as any; await expoUpdates.configureExpoUpdatesIfInstalledAsync(managedCtx); - expect(expoUpdates.getNativelyDefinedReleaseChannelAsync).toBeCalledTimes(1); + expect(expoUpdates.getNativelyDefinedClassicReleaseChannelAsync).toBeCalledTimes(1); }); it('uses the default release channel if the releaseChannel is not defined in ctx.job.releaseChannel nor natively.', async () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index d95d3f6f..60a50441 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -5,12 +5,12 @@ import { Ios, Android, Platform, Job } from '@expo/eas-build-job'; import { androidSetChannelNativelyAsync, androidSetClassicReleaseChannelNativelyAsync, - androidGetNativelyDefinedReleaseChannelAsync, + androidGetNativelyDefinedClassicReleaseChannelAsync, } from '../android/expoUpdates'; import { iosSetChannelNativelyAsync, - iosSetReleaseChannelNativelyAsync, - iosGetNativelyDefinedReleaseChannelAsync, + iosSetClassicReleaseChannelNativelyAsync, + iosGetNativelyDefinedClassicReleaseChannelAsync, } from '../ios/expoUpdates'; import { BuildContext } from '../context'; @@ -54,7 +54,9 @@ export const setChannelNativelyAsync = async (ctx: BuildContext): Promise): Promise => { +export const setClassicReleaseChannelNativelyAsync = async ( + ctx: BuildContext +): Promise => { assert(ctx.job.releaseChannel, 'releaseChannel must be defined'); const configFile = ctx.job.platform === Platform.ANDROID ? 'AndroidManifest.xml' : 'Expo.plist'; @@ -66,7 +68,7 @@ export const setReleaseChannelNativelyAsync = async (ctx: BuildContext): Pr return; } case Platform.IOS: { - await iosSetReleaseChannelNativelyAsync(ctx); + await iosSetClassicReleaseChannelNativelyAsync(ctx); return; } default: @@ -79,15 +81,15 @@ export const setReleaseChannelNativelyAsync = async (ctx: BuildContext): Pr * @param ctx * @param platform */ -export const getNativelyDefinedReleaseChannelAsync = async ( +export const getNativelyDefinedClassicReleaseChannelAsync = async ( ctx: BuildContext ): Promise => { switch (ctx.job.platform) { case Platform.ANDROID: { - return androidGetNativelyDefinedReleaseChannelAsync(ctx); + return androidGetNativelyDefinedClassicReleaseChannelAsync(ctx); } case Platform.IOS: { - return iosGetNativelyDefinedReleaseChannelAsync(ctx); + return iosGetNativelyDefinedClassicReleaseChannelAsync(ctx); } default: throw new Error(`Platform is not supported.`); @@ -96,14 +98,14 @@ export const getNativelyDefinedReleaseChannelAsync = async ( export const configureClassicExpoUpdatesAsync = async (ctx: BuildContext): Promise => { if (ctx.job.releaseChannel) { - await setReleaseChannelNativelyAsync(ctx); + await setClassicReleaseChannelNativelyAsync(ctx); } else { /** * If releaseChannel is not defined: * 1. Try to infer it from the native value. * 2. If it is not set, fallback to 'default'. */ - const releaseChannel = await getNativelyDefinedReleaseChannelAsync(ctx); + const releaseChannel = await getNativelyDefinedClassicReleaseChannelAsync(ctx); if (releaseChannel) { ctx.logger.info( `Using the release channel pre-configured in native project (${releaseChannel})` From eb8333ba390247e13a7b435729f05bcedee4adb0 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 21:29:36 -0700 Subject: [PATCH 27/28] fix tests --- .../src/utils/__tests__/expoUpdates.test.ts | 12 +++++++----- packages/build-tools/src/utils/expoUpdates.ts | 7 ++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts index 12f44c26..a6f8097a 100644 --- a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts @@ -57,7 +57,7 @@ describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { it('sets the release channel if it is supplied in ctx.job.releaseChannel', async () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); - jest.spyOn(expoUpdates, 'setReleaseChannelNativelyAsync').mockImplementation(); + jest.spyOn(expoUpdates, 'setClassicReleaseChannelNativelyAsync').mockImplementation(); const managedCtx: ManagedBuildContext = { job: { releaseChannel: 'default', platform: Platform.IOS }, @@ -68,7 +68,7 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { }); it('searches for the natively defined releaseChannel if it is not supplied by ctx.job.releaseChannel', async () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); - jest.spyOn(expoUpdates, 'getNativelyDefinedReleaseChannelAsync').mockImplementation(); + jest.spyOn(expoUpdates, 'getNativelyDefinedClassicReleaseChannelAsync').mockImplementation(); const managedCtx: ManagedBuildContext = { job: { platform: Platform.IOS }, @@ -80,9 +80,11 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { }); it('uses the default release channel if the releaseChannel is not defined in ctx.job.releaseChannel nor natively.', async () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); - jest.spyOn(expoUpdates, 'getNativelyDefinedReleaseChannelAsync').mockImplementation(() => { - throw new Error(); - }); + jest + .spyOn(expoUpdates, 'getNativelyDefinedClassicReleaseChannelAsync') + .mockImplementation(() => { + throw new Error(); + }); const infoLogger = jest.fn(); const managedCtx: ManagedBuildContext = { diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index 60a50441..f36a5486 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -105,13 +105,14 @@ export const configureClassicExpoUpdatesAsync = async (ctx: BuildContext): * 1. Try to infer it from the native value. * 2. If it is not set, fallback to 'default'. */ - const releaseChannel = await getNativelyDefinedClassicReleaseChannelAsync(ctx); - if (releaseChannel) { + try { + const releaseChannel = await getNativelyDefinedClassicReleaseChannelAsync(ctx); + assert(releaseChannel, 'release channel is not defined natively'); ctx.logger.info( `Using the release channel pre-configured in native project (${releaseChannel})` ); ctx.logger.warn('Please add the "releaseChannel" field to your build profile (eas.json)'); - } else { + } catch (_) { ctx.logger.info(`Using default release channel for 'expo-updates' (default)`); } } From c24eee834517ace0bdaeb32bb88d81f3e404ff22 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 24 May 2021 21:59:21 -0700 Subject: [PATCH 28/28] fix tests --- .../src/utils/__tests__/expoUpdates.test.ts | 18 ++++++++++-------- packages/build-tools/src/utils/expoUpdates.ts | 7 +++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts index a6f8097a..c8f6cb50 100644 --- a/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts +++ b/packages/build-tools/src/utils/__tests__/expoUpdates.test.ts @@ -8,6 +8,9 @@ jest.mock('../isExpoUpdatesInstalled', () => jest.fn()); jest.mock('fs'); describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { + beforeAll(() => { + jest.restoreAllMocks(); + }); it('aborts if expo-updates is not installed', async () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(false); jest.spyOn(expoUpdates, 'configureEASExpoUpdatesAsync'); @@ -39,8 +42,8 @@ describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { it('configures for classic updates if the updates.channel field is not set', async () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); - jest.spyOn(expoUpdates, 'configureEASExpoUpdatesAsync').mockImplementation(); - jest.spyOn(expoUpdates, 'configureClassicExpoUpdatesAsync'); + jest.spyOn(expoUpdates, 'configureEASExpoUpdatesAsync'); + jest.spyOn(expoUpdates, 'configureClassicExpoUpdatesAsync').mockImplementation(); const managedCtx: ManagedBuildContext = { job: { platform: Platform.IOS }, @@ -55,6 +58,9 @@ describe(expoUpdates.configureExpoUpdatesIfInstalledAsync, () => { }); describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { + beforeAll(() => { + jest.restoreAllMocks(); + }); it('sets the release channel if it is supplied in ctx.job.releaseChannel', async () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); jest.spyOn(expoUpdates, 'setClassicReleaseChannelNativelyAsync').mockImplementation(); @@ -82,8 +88,8 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { (isExpoUpdatesInstalledAsync as jest.Mock).mockReturnValue(true); jest .spyOn(expoUpdates, 'getNativelyDefinedClassicReleaseChannelAsync') - .mockImplementation(() => { - throw new Error(); + .mockImplementation(async () => { + return null; }); const infoLogger = jest.fn(); @@ -96,7 +102,3 @@ describe(expoUpdates.configureClassicExpoUpdatesAsync, () => { expect(infoLogger).toBeCalledWith(`Using default release channel for 'expo-updates' (default)`); }); }); - -describe(expoUpdates.configureEASExpoUpdatesAsync, () => { - // maybe an e2e test instead of just the mocked out calls for the two functions as well? -}); diff --git a/packages/build-tools/src/utils/expoUpdates.ts b/packages/build-tools/src/utils/expoUpdates.ts index f36a5486..60a50441 100644 --- a/packages/build-tools/src/utils/expoUpdates.ts +++ b/packages/build-tools/src/utils/expoUpdates.ts @@ -105,14 +105,13 @@ export const configureClassicExpoUpdatesAsync = async (ctx: BuildContext): * 1. Try to infer it from the native value. * 2. If it is not set, fallback to 'default'. */ - try { - const releaseChannel = await getNativelyDefinedClassicReleaseChannelAsync(ctx); - assert(releaseChannel, 'release channel is not defined natively'); + const releaseChannel = await getNativelyDefinedClassicReleaseChannelAsync(ctx); + if (releaseChannel) { ctx.logger.info( `Using the release channel pre-configured in native project (${releaseChannel})` ); ctx.logger.warn('Please add the "releaseChannel" field to your build profile (eas.json)'); - } catch (_) { + } else { ctx.logger.info(`Using default release channel for 'expo-updates' (default)`); } }