From a064483be1bd0e4b50f737bb7aba90760b283b17 Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Mon, 2 Sep 2024 16:19:44 +0200 Subject: [PATCH 1/9] [eas-cli] use getConfig from projectDir/node_modules/@expo/config if available --- packages/eas-cli/src/project/expoConfig.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/eas-cli/src/project/expoConfig.ts b/packages/eas-cli/src/project/expoConfig.ts index 77811d4d29..3042be0f2e 100644 --- a/packages/eas-cli/src/project/expoConfig.ts +++ b/packages/eas-cli/src/project/expoConfig.ts @@ -1,9 +1,16 @@ -import { ExpoConfig, getConfig, getConfigFilePaths, modifyConfigAsync } from '@expo/config'; +import { + ExpoConfig, + getConfig as _getConfig, + getConfigFilePaths, + modifyConfigAsync, +} from '@expo/config'; import { Env } from '@expo/eas-build-job'; import fs from 'fs-extra'; import Joi from 'joi'; import path from 'path'; +import Log from '../log'; + export type PublicExpoConfig = Omit< ExpoConfig, '_internal' | 'hooks' | 'ios' | 'android' | 'updates' @@ -47,6 +54,16 @@ function getExpoConfigInternal( ...process.env, ...opts.env, }; + let getConfig: typeof _getConfig; + try { + const expoConfig = require(`${projectDir}/node_modules/@expo/config`); + getConfig = expoConfig.getConfig; + } catch (error: any) { + Log.warn( + `Failed to load getConfig function from ${projectDir}/node_modules/@expo/config: ${error.message}` + ); + getConfig = _getConfig; + } const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true, ...(opts.isPublicConfig ? { isPublicConfig: true } : {}), From 0c18caf5e1ab41257d718f53c64fce52d32314af Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Mon, 2 Sep 2024 16:35:53 +0200 Subject: [PATCH 2/9] add one more log --- packages/eas-cli/src/project/expoConfig.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/eas-cli/src/project/expoConfig.ts b/packages/eas-cli/src/project/expoConfig.ts index 3042be0f2e..04916ba63a 100644 --- a/packages/eas-cli/src/project/expoConfig.ts +++ b/packages/eas-cli/src/project/expoConfig.ts @@ -62,6 +62,7 @@ function getExpoConfigInternal( Log.warn( `Failed to load getConfig function from ${projectDir}/node_modules/@expo/config: ${error.message}` ); + Log.warn('Falling back to the version of @expo/config shipped with the EAS CLI.'); getConfig = _getConfig; } const { exp } = getConfig(projectDir, { From 2eb847c4a7a9442b02cb6dfc5e54e6affa30cc40 Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Mon, 2 Sep 2024 17:04:32 +0200 Subject: [PATCH 3/9] further improvments --- packages/eas-cli/src/project/expoConfig.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/eas-cli/src/project/expoConfig.ts b/packages/eas-cli/src/project/expoConfig.ts index 04916ba63a..e0a3d398a0 100644 --- a/packages/eas-cli/src/project/expoConfig.ts +++ b/packages/eas-cli/src/project/expoConfig.ts @@ -54,14 +54,13 @@ function getExpoConfigInternal( ...process.env, ...opts.env, }; + const projectExpoConfigPath = path.join(projectDir, 'node_modules', '@expo', 'config'); let getConfig: typeof _getConfig; try { - const expoConfig = require(`${projectDir}/node_modules/@expo/config`); + const expoConfig = require(projectExpoConfigPath); getConfig = expoConfig.getConfig; } catch (error: any) { - Log.warn( - `Failed to load getConfig function from ${projectDir}/node_modules/@expo/config: ${error.message}` - ); + Log.warn(`Failed to load getConfig function from ${projectExpoConfigPath}: ${error.message}`); Log.warn('Falling back to the version of @expo/config shipped with the EAS CLI.'); getConfig = _getConfig; } From c50d3ad57c0cc094629c8c5e2cf953c20d4c24e8 Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Mon, 2 Sep 2024 18:12:42 +0200 Subject: [PATCH 4/9] use @expo/prebuild-config and @expo/config-plugins shipped with expo package as well --- .../eas-cli/src/project/ios/entitlements.ts | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/eas-cli/src/project/ios/entitlements.ts b/packages/eas-cli/src/project/ios/entitlements.ts index 9beec7f749..11c9b1f784 100644 --- a/packages/eas-cli/src/project/ios/entitlements.ts +++ b/packages/eas-cli/src/project/ios/entitlements.ts @@ -1,7 +1,9 @@ -import { IOSConfig, compileModsAsync } from '@expo/config-plugins'; +import { IOSConfig, compileModsAsync as _compileModsAsync } from '@expo/config-plugins'; import { JSONObject } from '@expo/json-file'; -import { getPrebuildConfigAsync } from '@expo/prebuild-config'; +import { getPrebuildConfigAsync as _getPrebuildConfigAsync } from '@expo/prebuild-config'; +import path from 'path'; +import Log from '../../log'; import { readPlistAsync } from '../../utils/plist'; import { Client } from '../../vcs/vcs'; import { hasIgnoredIosProjectAsync } from '../workflow'; @@ -22,8 +24,42 @@ export async function getManagedApplicationTargetEntitlementsAsync( ...process.env, ...env, }; + const projectExpoPrebuildConfigPath = path.join( + projectDir, + 'node_modules', + '@expo', + 'prebuild-config' + ); + let getPrebuildConfigAsync: typeof _getPrebuildConfigAsync; + try { + const expoPrebuildConfig = require(projectExpoPrebuildConfigPath); + getPrebuildConfigAsync = expoPrebuildConfig.getPrebuildConfigAsync; + } catch (error: any) { + Log.warn( + `Failed to load getPrebuildConfigAsync function from ${projectExpoPrebuildConfigPath}: ${error.message}` + ); + Log.warn('Falling back to the version of @expo/prebuild-config shipped with the EAS CLI.'); + getPrebuildConfigAsync = _getPrebuildConfigAsync; + } const { exp } = await getPrebuildConfigAsync(projectDir, { platforms: ['ios'] }); + const projectExpoConfigPluginsPath = path.join( + projectDir, + 'node_modules', + '@expo', + 'config-plugins' + ); + let compileModsAsync: typeof _compileModsAsync; + try { + const expoConfigPlugins = require(projectExpoConfigPluginsPath); + compileModsAsync = expoConfigPlugins.compileModsAsync; + } catch (error: any) { + Log.warn( + `Failed to load compileModsAsync function from ${projectExpoConfigPluginsPath}: ${error.message}` + ); + Log.warn('Falling back to the version of @expo/confi-plugins shipped with the EAS CLI.'); + compileModsAsync = _compileModsAsync; + } const expWithMods = await compileModsAsync(exp, { projectRoot: projectDir, platforms: ['ios'], From 0e60aaa131f34556b7196e0c28148271e30e218b Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Mon, 2 Sep 2024 18:42:46 +0200 Subject: [PATCH 5/9] fix warns --- packages/eas-cli/src/project/expoConfig.ts | 11 ++++++-- .../eas-cli/src/project/ios/entitlements.ts | 28 ++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/eas-cli/src/project/expoConfig.ts b/packages/eas-cli/src/project/expoConfig.ts index e0a3d398a0..2009103d0f 100644 --- a/packages/eas-cli/src/project/expoConfig.ts +++ b/packages/eas-cli/src/project/expoConfig.ts @@ -44,6 +44,8 @@ export async function createOrModifyExpoConfigAsync( } } +let wasExpoConfigWarnPrinted = false; + function getExpoConfigInternal( projectDir: string, opts: ExpoConfigOptionsInternal = {} @@ -59,9 +61,12 @@ function getExpoConfigInternal( try { const expoConfig = require(projectExpoConfigPath); getConfig = expoConfig.getConfig; - } catch (error: any) { - Log.warn(`Failed to load getConfig function from ${projectExpoConfigPath}: ${error.message}`); - Log.warn('Falling back to the version of @expo/config shipped with the EAS CLI.'); + } catch { + if (!wasExpoConfigWarnPrinted) { + Log.warn(`Failed to load getConfig function from ${projectExpoConfigPath}`); + Log.warn('Falling back to the version of @expo/config shipped with the EAS CLI.'); + wasExpoConfigWarnPrinted = true; + } getConfig = _getConfig; } const { exp } = getConfig(projectDir, { diff --git a/packages/eas-cli/src/project/ios/entitlements.ts b/packages/eas-cli/src/project/ios/entitlements.ts index 11c9b1f784..d88a804ed7 100644 --- a/packages/eas-cli/src/project/ios/entitlements.ts +++ b/packages/eas-cli/src/project/ios/entitlements.ts @@ -12,6 +12,10 @@ interface Target { buildConfiguration?: string; targetName: string; } + +let wasExooPrebuildConfigWarnPrinted = false; +let wasExpoConfigPluginsWarnPrinted = false; + export async function getManagedApplicationTargetEntitlementsAsync( projectDir: string, env: Record, @@ -34,11 +38,14 @@ export async function getManagedApplicationTargetEntitlementsAsync( try { const expoPrebuildConfig = require(projectExpoPrebuildConfigPath); getPrebuildConfigAsync = expoPrebuildConfig.getPrebuildConfigAsync; - } catch (error: any) { - Log.warn( - `Failed to load getPrebuildConfigAsync function from ${projectExpoPrebuildConfigPath}: ${error.message}` - ); - Log.warn('Falling back to the version of @expo/prebuild-config shipped with the EAS CLI.'); + } catch { + if (!wasExooPrebuildConfigWarnPrinted) { + Log.warn( + `Failed to load getPrebuildConfigAsync function from ${projectExpoPrebuildConfigPath}` + ); + Log.warn('Falling back to the version of @expo/prebuild-config shipped with the EAS CLI.'); + wasExooPrebuildConfigWarnPrinted = true; + } getPrebuildConfigAsync = _getPrebuildConfigAsync; } const { exp } = await getPrebuildConfigAsync(projectDir, { platforms: ['ios'] }); @@ -53,11 +60,12 @@ export async function getManagedApplicationTargetEntitlementsAsync( try { const expoConfigPlugins = require(projectExpoConfigPluginsPath); compileModsAsync = expoConfigPlugins.compileModsAsync; - } catch (error: any) { - Log.warn( - `Failed to load compileModsAsync function from ${projectExpoConfigPluginsPath}: ${error.message}` - ); - Log.warn('Falling back to the version of @expo/confi-plugins shipped with the EAS CLI.'); + } catch { + if (!wasExpoConfigPluginsWarnPrinted) { + Log.warn(`Failed to load compileModsAsync function from ${projectExpoConfigPluginsPath}`); + Log.warn('Falling back to the version of @expo/config-plugins shipped with the EAS CLI.'); + wasExpoConfigPluginsWarnPrinted = true; + } compileModsAsync = _compileModsAsync; } const expWithMods = await compileModsAsync(exp, { From d0d97d98b9a2019d74461821a0b62678fffdf9b8 Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Mon, 14 Oct 2024 13:23:53 +0200 Subject: [PATCH 6/9] use expo cli --- .../DynamicProjectConfigContextField.ts | 12 ++-- ...ptionalPrivateProjectConfigContextField.ts | 6 +- .../PrivateProjectConfigContextField.ts | 6 +- .../context/contextUtils/getProjectIdAsync.ts | 7 +- packages/eas-cli/src/commands/project/init.ts | 8 +-- .../src/commands/project/onboarding.ts | 6 +- .../android/actions/BuildCredentialsUtils.ts | 8 +-- .../android/actions/CreateKeystore.ts | 2 +- packages/eas-cli/src/credentials/context.ts | 14 ++-- .../ios/actions/BuildCredentialsUtils.ts | 6 +- .../src/credentials/manager/ManageIos.ts | 9 +-- .../manager/SetUpIosBuildCredentials.ts | 2 +- packages/eas-cli/src/project/expoConfig.ts | 72 +++++++++++-------- .../eas-cli/src/project/ios/entitlements.ts | 69 +++++++----------- packages/eas-cli/src/project/projectUtils.ts | 5 ++ 15 files changed, 118 insertions(+), 114 deletions(-) diff --git a/packages/eas-cli/src/commandUtils/context/DynamicProjectConfigContextField.ts b/packages/eas-cli/src/commandUtils/context/DynamicProjectConfigContextField.ts index c6eef0033f..4826544b2b 100644 --- a/packages/eas-cli/src/commandUtils/context/DynamicProjectConfigContextField.ts +++ b/packages/eas-cli/src/commandUtils/context/DynamicProjectConfigContextField.ts @@ -5,8 +5,8 @@ import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findPro import { getProjectIdAsync } from './contextUtils/getProjectIdAsync'; import { ExpoConfigOptions, - getPrivateExpoConfig, - getPublicExpoConfig, + getPrivateExpoConfigAsync, + getPublicExpoConfigAsync, } from '../../project/expoConfig'; export type DynamicConfigContextFn = (options?: ExpoConfigOptions) => Promise<{ @@ -22,12 +22,12 @@ export class DynamicPublicProjectConfigContextField extends ContextField { const projectDir = await findProjectDirAndVerifyProjectSetupAsync(); return async (options?: ExpoConfigOptions) => { - const expBefore = getPublicExpoConfig(projectDir, options); + const expBefore = await getPublicExpoConfigAsync(projectDir, options); const projectId = await getProjectIdAsync(sessionManager, expBefore, { nonInteractive, env: options?.env, }); - const exp = getPublicExpoConfig(projectDir, options); + const exp = await getPublicExpoConfigAsync(projectDir, options); return { exp, projectDir, @@ -44,12 +44,12 @@ export class DynamicPrivateProjectConfigContextField extends ContextField { const projectDir = await findProjectDirAndVerifyProjectSetupAsync(); return async (options?: ExpoConfigOptions) => { - const expBefore = getPrivateExpoConfig(projectDir, options); + const expBefore = await getPrivateExpoConfigAsync(projectDir, options); const projectId = await getProjectIdAsync(sessionManager, expBefore, { nonInteractive, env: options?.env, }); - const exp = getPrivateExpoConfig(projectDir, options); + const exp = await getPrivateExpoConfigAsync(projectDir, options); return { exp, projectDir, diff --git a/packages/eas-cli/src/commandUtils/context/OptionalPrivateProjectConfigContextField.ts b/packages/eas-cli/src/commandUtils/context/OptionalPrivateProjectConfigContextField.ts index 2b5516611e..833f165f24 100644 --- a/packages/eas-cli/src/commandUtils/context/OptionalPrivateProjectConfigContextField.ts +++ b/packages/eas-cli/src/commandUtils/context/OptionalPrivateProjectConfigContextField.ts @@ -4,7 +4,7 @@ import { InvalidEasJsonError } from '@expo/eas-json/build/errors'; import ContextField, { ContextOptions } from './ContextField'; import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync'; import { getProjectIdAsync } from './contextUtils/getProjectIdAsync'; -import { getPrivateExpoConfig } from '../../project/expoConfig'; +import { getPrivateExpoConfigAsync } from '../../project/expoConfig'; export class OptionalPrivateProjectConfigContextField extends ContextField< | { @@ -35,11 +35,11 @@ export class OptionalPrivateProjectConfigContextField extends ContextField< return undefined; } - const expBefore = getPrivateExpoConfig(projectDir); + const expBefore = await getPrivateExpoConfigAsync(projectDir); const projectId = await getProjectIdAsync(sessionManager, expBefore, { nonInteractive, }); - const exp = getPrivateExpoConfig(projectDir); + const exp = await getPrivateExpoConfigAsync(projectDir); return { exp, projectDir, diff --git a/packages/eas-cli/src/commandUtils/context/PrivateProjectConfigContextField.ts b/packages/eas-cli/src/commandUtils/context/PrivateProjectConfigContextField.ts index cb0d0f7c13..95f3eb1d92 100644 --- a/packages/eas-cli/src/commandUtils/context/PrivateProjectConfigContextField.ts +++ b/packages/eas-cli/src/commandUtils/context/PrivateProjectConfigContextField.ts @@ -3,7 +3,7 @@ import { ExpoConfig } from '@expo/config'; import ContextField, { ContextOptions } from './ContextField'; import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync'; import { getProjectIdAsync } from './contextUtils/getProjectIdAsync'; -import { getPrivateExpoConfig } from '../../project/expoConfig'; +import { getPrivateExpoConfigAsync } from '../../project/expoConfig'; export class PrivateProjectConfigContextField extends ContextField<{ projectId: string; @@ -16,11 +16,11 @@ export class PrivateProjectConfigContextField extends ContextField<{ projectDir: string; }> { const projectDir = await findProjectDirAndVerifyProjectSetupAsync(); - const expBefore = getPrivateExpoConfig(projectDir); + const expBefore = await getPrivateExpoConfigAsync(projectDir); const projectId = await getProjectIdAsync(sessionManager, expBefore, { nonInteractive, }); - const exp = getPrivateExpoConfig(projectDir); + const exp = await getPrivateExpoConfigAsync(projectDir); return { projectId, diff --git a/packages/eas-cli/src/commandUtils/context/contextUtils/getProjectIdAsync.ts b/packages/eas-cli/src/commandUtils/context/contextUtils/getProjectIdAsync.ts index d559cbfbf2..465a05f453 100644 --- a/packages/eas-cli/src/commandUtils/context/contextUtils/getProjectIdAsync.ts +++ b/packages/eas-cli/src/commandUtils/context/contextUtils/getProjectIdAsync.ts @@ -7,7 +7,10 @@ import { findProjectRootAsync } from './findProjectDirAndVerifyProjectSetupAsync import { AppQuery } from '../../../graphql/queries/AppQuery'; import Log, { learnMore } from '../../../log'; import { ora } from '../../../ora'; -import { createOrModifyExpoConfigAsync, getPrivateExpoConfig } from '../../../project/expoConfig'; +import { + createOrModifyExpoConfigAsync, + getPrivateExpoConfigAsync, +} from '../../../project/expoConfig'; import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync'; import { toAppPrivacy } from '../../../project/projectUtils'; import SessionManager from '../../../user/SessionManager'; @@ -25,7 +28,7 @@ export async function saveProjectIdToAppConfigAsync( options: { env?: Env } = {} ): Promise { // NOTE(cedric): we disable plugins to avoid writing plugin-generated content to `expo.extra` - const exp = getPrivateExpoConfig(projectDir, { skipPlugins: true, ...options }); + const exp = await getPrivateExpoConfigAsync(projectDir, { skipPlugins: true, ...options }); const result = await createOrModifyExpoConfigAsync( projectDir, { diff --git a/packages/eas-cli/src/commands/project/init.ts b/packages/eas-cli/src/commands/project/init.ts index 837d72280a..9a31a5777f 100644 --- a/packages/eas-cli/src/commands/project/init.ts +++ b/packages/eas-cli/src/commands/project/init.ts @@ -13,7 +13,7 @@ import { AppMutation } from '../../graphql/mutations/AppMutation'; import { AppQuery } from '../../graphql/queries/AppQuery'; import Log, { link } from '../../log'; import { ora } from '../../ora'; -import { createOrModifyExpoConfigAsync, getPrivateExpoConfig } from '../../project/expoConfig'; +import { createOrModifyExpoConfigAsync, getPrivateExpoConfigAsync } from '../../project/expoConfig'; import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync'; import { toAppPrivacy } from '../../project/projectUtils'; import { Choice, confirmAsync, promptAsync } from '../../prompts'; @@ -106,7 +106,7 @@ export default class ProjectInit extends EasCommand { projectDir: string, { force, nonInteractive }: InitializeMethodOptions ): Promise { - const exp = getPrivateExpoConfig(projectDir); + const exp = await getPrivateExpoConfigAsync(projectDir); const appForProjectId = await AppQuery.byIdAsync(graphqlClient, projectId); const correctOwner = appForProjectId.ownerAccount.name; const correctSlug = appForProjectId.slug; @@ -161,7 +161,7 @@ export default class ProjectInit extends EasCommand { projectDir: string, { force, nonInteractive }: InitializeMethodOptions ): Promise { - const exp = getPrivateExpoConfig(projectDir); + const exp = await getPrivateExpoConfigAsync(projectDir); const existingProjectId = exp.extra?.eas?.projectId; if (projectId === existingProjectId) { @@ -218,7 +218,7 @@ export default class ProjectInit extends EasCommand { projectDir: string, { force, nonInteractive }: InitializeMethodOptions ): Promise { - const exp = getPrivateExpoConfig(projectDir); + const exp = await getPrivateExpoConfigAsync(projectDir); const existingProjectId = exp.extra?.eas?.projectId; if (existingProjectId) { diff --git a/packages/eas-cli/src/commands/project/onboarding.ts b/packages/eas-cli/src/commands/project/onboarding.ts index ae9b3e39d9..c2a0e4285e 100644 --- a/packages/eas-cli/src/commands/project/onboarding.ts +++ b/packages/eas-cli/src/commands/project/onboarding.ts @@ -31,7 +31,7 @@ import { import { installDependenciesAsync } from '../../onboarding/installDependencies'; import { runCommandAsync } from '../../onboarding/runCommand'; import { RequestedPlatform } from '../../platform'; -import { ExpoConfigOptions, getPrivateExpoConfig } from '../../project/expoConfig'; +import { ExpoConfigOptions, getPrivateExpoConfigAsync } from '../../project/expoConfig'; import { promptAsync } from '../../prompts'; import { Actor } from '../../user/User'; import { easCliVersion } from '../../utils/easCli'; @@ -339,7 +339,7 @@ async function getPrivateExpoConfigWithProjectIdAsync({ actor: Actor; options?: ExpoConfigOptions; }): Promise { - const expBefore = getPrivateExpoConfig(projectDir, options); + const expBefore = await getPrivateExpoConfigAsync(projectDir, options); const projectId = await validateOrSetProjectIdAsync({ exp: expBefore, graphqlClient, @@ -349,7 +349,7 @@ async function getPrivateExpoConfigWithProjectIdAsync({ }, cwd: projectDir, }); - const exp = getPrivateExpoConfig(projectDir, options); + const exp = await getPrivateExpoConfigAsync(projectDir, options); return { exp, projectId, diff --git a/packages/eas-cli/src/credentials/android/actions/BuildCredentialsUtils.ts b/packages/eas-cli/src/credentials/android/actions/BuildCredentialsUtils.ts index 71a8632c38..bd4494975e 100644 --- a/packages/eas-cli/src/credentials/android/actions/BuildCredentialsUtils.ts +++ b/packages/eas-cli/src/credentials/android/actions/BuildCredentialsUtils.ts @@ -101,14 +101,14 @@ export async function getAppLookupParamsFromContextAsync( ctx: CredentialsContext, gradleContext?: GradleBuildContext ): Promise { - ctx.ensureProjectContext(); - const projectName = ctx.exp.slug; - const projectId = ctx.projectId; + const exp = await ctx.getExpoConfigAsync(); + const projectName = exp.slug; + const projectId = await ctx.getProjectIdAsync(); const account = await getOwnerAccountForProjectIdAsync(ctx.graphqlClient, projectId); const androidApplicationIdentifier = await getApplicationIdAsync( ctx.projectDir, - ctx.exp, + exp, ctx.vcsClient, gradleContext ); diff --git a/packages/eas-cli/src/credentials/android/actions/CreateKeystore.ts b/packages/eas-cli/src/credentials/android/actions/CreateKeystore.ts index 3953b8a1e1..9da141f7fb 100644 --- a/packages/eas-cli/src/credentials/android/actions/CreateKeystore.ts +++ b/packages/eas-cli/src/credentials/android/actions/CreateKeystore.ts @@ -16,7 +16,7 @@ export class CreateKeystore { throw new Error(`New keystore cannot be created in non-interactive mode.`); } - const projectId = ctx.projectId; + const projectId = await ctx.getProjectIdAsync(); const keystore = await this.provideOrGenerateAsync(ctx.graphqlClient, ctx.analytics, projectId); const keystoreFragment = await ctx.android.createKeystoreAsync( ctx.graphqlClient, diff --git a/packages/eas-cli/src/credentials/context.ts b/packages/eas-cli/src/credentials/context.ts index f6cfaaee77..827273564a 100644 --- a/packages/eas-cli/src/credentials/context.ts +++ b/packages/eas-cli/src/credentials/context.ts @@ -10,7 +10,7 @@ import { AuthenticationMode } from './ios/appstore/authenticateTypes'; import { Analytics } from '../analytics/AnalyticsManager'; import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient'; import Log from '../log'; -import { getPrivateExpoConfig } from '../project/expoConfig'; +import { getPrivateExpoConfigAsync } from '../project/expoConfig'; import { confirmAsync } from '../prompts'; import { Actor } from '../user/User'; import { Client } from '../vcs/vcs'; @@ -67,22 +67,22 @@ export class CredentialsContext { return !!this.projectInfo; } - get exp(): ExpoConfig { - this.ensureProjectContext(); + public async getExpoConfigAsync(): Promise { + await this.ensureProjectContextAsync(); return this.projectInfo!.exp; } - get projectId(): string { - this.ensureProjectContext(); + public async getProjectIdAsync(): Promise { + await this.ensureProjectContextAsync(); return this.projectInfo!.projectId; } - public ensureProjectContext(): void { + public async ensureProjectContextAsync(): Promise { if (this.hasProjectContext) { return; } // trigger getConfig error - getPrivateExpoConfig(this.options.projectDir); + await getPrivateExpoConfigAsync(this.options.projectDir); } async bestEffortAppStoreAuthenticateAsync(): Promise { diff --git a/packages/eas-cli/src/credentials/ios/actions/BuildCredentialsUtils.ts b/packages/eas-cli/src/credentials/ios/actions/BuildCredentialsUtils.ts index 5ed178edca..ee4466ec11 100644 --- a/packages/eas-cli/src/credentials/ios/actions/BuildCredentialsUtils.ts +++ b/packages/eas-cli/src/credentials/ios/actions/BuildCredentialsUtils.ts @@ -91,9 +91,9 @@ export async function assignBuildCredentialsAsync( } export async function getAppFromContextAsync(ctx: CredentialsContext): Promise { - ctx.ensureProjectContext(); - const projectName = ctx.exp.slug; - const projectId = ctx.projectId; + const exp = await ctx.getExpoConfigAsync(); + const projectName = exp.slug; + const projectId = await ctx.getProjectIdAsync(); const account = await getOwnerAccountForProjectIdAsync(ctx.graphqlClient, projectId); return { account, diff --git a/packages/eas-cli/src/credentials/manager/ManageIos.ts b/packages/eas-cli/src/credentials/manager/ManageIos.ts index c48eefe937..36c1865cb0 100644 --- a/packages/eas-cli/src/credentials/manager/ManageIos.ts +++ b/packages/eas-cli/src/credentials/manager/ManageIos.ts @@ -94,7 +94,7 @@ export class ManageIos { }; const account = ctx.hasProjectContext - ? await getAccountForProjectAsync(ctx.projectId) + ? await getAccountForProjectAsync(await ctx.getProjectIdAsync()) : ensureActorHasPrimaryAccount(ctx.user); let app = null; @@ -195,18 +195,19 @@ export class ManageIos { }> { assert(ctx.hasProjectContext, 'createProjectContextAsync: must have project context.'); - const app = { account, projectName: ctx.exp.slug }; + const exp = await ctx.getExpoConfigAsync(); + const app = { account, projectName: exp.slug }; const xcodeBuildContext = await resolveXcodeBuildContextAsync( { projectDir: ctx.projectDir, nonInteractive: ctx.nonInteractive, - exp: ctx.exp, + exp, vcsClient: ctx.vcsClient, }, buildProfile ); const targets = await resolveTargetsAsync({ - exp: ctx.exp, + exp, projectDir: ctx.projectDir, xcodeBuildContext, env: buildProfile.env, diff --git a/packages/eas-cli/src/credentials/manager/SetUpIosBuildCredentials.ts b/packages/eas-cli/src/credentials/manager/SetUpIosBuildCredentials.ts index d659bdc0eb..3681d90117 100644 --- a/packages/eas-cli/src/credentials/manager/SetUpIosBuildCredentials.ts +++ b/packages/eas-cli/src/credentials/manager/SetUpIosBuildCredentials.ts @@ -55,7 +55,7 @@ export class SetUpIosBuildCredentials extends ManageIos { }; const account = ctx.hasProjectContext - ? await getAccountForProjectAsync(ctx.projectId) + ? await getAccountForProjectAsync(await ctx.getProjectIdAsync()) : ensureActorHasPrimaryAccount(ctx.user); let app = null; diff --git a/packages/eas-cli/src/project/expoConfig.ts b/packages/eas-cli/src/project/expoConfig.ts index 2009103d0f..5197f2f7e1 100644 --- a/packages/eas-cli/src/project/expoConfig.ts +++ b/packages/eas-cli/src/project/expoConfig.ts @@ -1,14 +1,11 @@ -import { - ExpoConfig, - getConfig as _getConfig, - getConfigFilePaths, - modifyConfigAsync, -} from '@expo/config'; +import { ExpoConfig, getConfig, getConfigFilePaths, modifyConfigAsync } from '@expo/config'; import { Env } from '@expo/eas-build-job'; +import spawnAsync from '@expo/spawn-async'; import fs from 'fs-extra'; import Joi from 'joi'; import path from 'path'; +import { isExpoInstalled } from './projectUtils'; import Log from '../log'; export type PublicExpoConfig = Omit< @@ -46,34 +43,48 @@ export async function createOrModifyExpoConfigAsync( let wasExpoConfigWarnPrinted = false; -function getExpoConfigInternal( +async function getExpoConfigInternalAsync( projectDir: string, opts: ExpoConfigOptionsInternal = {} -): ExpoConfig { +): Promise { const originalProcessEnv: NodeJS.ProcessEnv = process.env; try { process.env = { ...process.env, ...opts.env, }; - const projectExpoConfigPath = path.join(projectDir, 'node_modules', '@expo', 'config'); - let getConfig: typeof _getConfig; - try { - const expoConfig = require(projectExpoConfigPath); - getConfig = expoConfig.getConfig; - } catch { - if (!wasExpoConfigWarnPrinted) { - Log.warn(`Failed to load getConfig function from ${projectExpoConfigPath}`); - Log.warn('Falling back to the version of @expo/config shipped with the EAS CLI.'); - wasExpoConfigWarnPrinted = true; + + let exp: ExpoConfig; + if (isExpoInstalled(projectDir)) { + try { + const { stdout } = await spawnAsync( + 'npx', + ['expo', 'config', '--json', ...(opts.isPublicConfig ? ['--type', 'public'] : [])], + + { + cwd: projectDir, + } + ); + exp = JSON.parse(stdout); + } catch (err: any) { + if (!wasExpoConfigWarnPrinted) { + Log.warn( + `Failed to read the app config from the project using npx expo config command: ${err.message}.` + ); + Log.warn('Falling back to the version of @expo/config shipped with the EAS CLI.'); + wasExpoConfigWarnPrinted = true; + } + exp = getConfig(projectDir, { + skipSDKVersionRequirement: true, + ...(opts.isPublicConfig ? { isPublicConfig: true } : {}), + }).exp; } - getConfig = _getConfig; + } else { + exp = getConfig(projectDir, { + skipSDKVersionRequirement: true, + ...(opts.isPublicConfig ? { isPublicConfig: true } : {}), + }).exp; } - const { exp } = getConfig(projectDir, { - skipSDKVersionRequirement: true, - ...(opts.isPublicConfig ? { isPublicConfig: true } : {}), - ...(opts.skipPlugins ? { skipPlugins: true } : {}), - }); const { error } = MinimalAppConfigSchema.validate(exp, { allowUnknown: true, @@ -100,10 +111,13 @@ const MinimalAppConfigSchema = Joi.object({ }), }); -export function getPrivateExpoConfig(projectDir: string, opts: ExpoConfigOptions = {}): ExpoConfig { +export async function getPrivateExpoConfigAsync( + projectDir: string, + opts: ExpoConfigOptions = {} +): Promise { ensureExpoConfigExists(projectDir); - return getExpoConfigInternal(projectDir, { ...opts, isPublicConfig: false }); + return await getExpoConfigInternalAsync(projectDir, { ...opts, isPublicConfig: false }); } export function ensureExpoConfigExists(projectDir: string): void { @@ -119,11 +133,11 @@ export function isUsingStaticExpoConfig(projectDir: string): boolean { return !!(paths.staticConfigPath?.endsWith('app.json') && !paths.dynamicConfigPath); } -export function getPublicExpoConfig( +export async function getPublicExpoConfigAsync( projectDir: string, opts: ExpoConfigOptions = {} -): PublicExpoConfig { +): Promise { ensureExpoConfigExists(projectDir); - return getExpoConfigInternal(projectDir, { ...opts, isPublicConfig: true }); + return await getExpoConfigInternalAsync(projectDir, { ...opts, isPublicConfig: true }); } diff --git a/packages/eas-cli/src/project/ios/entitlements.ts b/packages/eas-cli/src/project/ios/entitlements.ts index d88a804ed7..556e0d491d 100644 --- a/packages/eas-cli/src/project/ios/entitlements.ts +++ b/packages/eas-cli/src/project/ios/entitlements.ts @@ -1,7 +1,7 @@ -import { IOSConfig, compileModsAsync as _compileModsAsync } from '@expo/config-plugins'; +import { ExportedConfig, IOSConfig, compileModsAsync } from '@expo/config-plugins'; import { JSONObject } from '@expo/json-file'; -import { getPrebuildConfigAsync as _getPrebuildConfigAsync } from '@expo/prebuild-config'; -import path from 'path'; +import { getPrebuildConfigAsync } from '@expo/prebuild-config'; +import spawnAsync from '@expo/spawn-async'; import Log from '../../log'; import { readPlistAsync } from '../../utils/plist'; @@ -13,7 +13,6 @@ interface Target { targetName: string; } -let wasExooPrebuildConfigWarnPrinted = false; let wasExpoConfigPluginsWarnPrinted = false; export async function getManagedApplicationTargetEntitlementsAsync( @@ -28,52 +27,34 @@ export async function getManagedApplicationTargetEntitlementsAsync( ...process.env, ...env, }; - const projectExpoPrebuildConfigPath = path.join( - projectDir, - 'node_modules', - '@expo', - 'prebuild-config' - ); - let getPrebuildConfigAsync: typeof _getPrebuildConfigAsync; - try { - const expoPrebuildConfig = require(projectExpoPrebuildConfigPath); - getPrebuildConfigAsync = expoPrebuildConfig.getPrebuildConfigAsync; - } catch { - if (!wasExooPrebuildConfigWarnPrinted) { - Log.warn( - `Failed to load getPrebuildConfigAsync function from ${projectExpoPrebuildConfigPath}` - ); - Log.warn('Falling back to the version of @expo/prebuild-config shipped with the EAS CLI.'); - wasExooPrebuildConfigWarnPrinted = true; - } - getPrebuildConfigAsync = _getPrebuildConfigAsync; - } - const { exp } = await getPrebuildConfigAsync(projectDir, { platforms: ['ios'] }); - const projectExpoConfigPluginsPath = path.join( - projectDir, - 'node_modules', - '@expo', - 'config-plugins' - ); - let compileModsAsync: typeof _compileModsAsync; + let expWithMods: ExportedConfig; try { - const expoConfigPlugins = require(projectExpoConfigPluginsPath); - compileModsAsync = expoConfigPlugins.compileModsAsync; - } catch { + const { stdout } = await spawnAsync( + 'npx', + ['expo', 'config', '--json', '--type', 'introspect'], + + { + cwd: projectDir, + } + ); + expWithMods = JSON.parse(stdout); + } catch (err: any) { if (!wasExpoConfigPluginsWarnPrinted) { - Log.warn(`Failed to load compileModsAsync function from ${projectExpoConfigPluginsPath}`); - Log.warn('Falling back to the version of @expo/config-plugins shipped with the EAS CLI.'); + Log.warn( + `Failed to read the app config from the project using npx expo config command: ${err.message}.` + ); + Log.warn('Falling back to the version of @expo/config shipped with the EAS CLI.'); wasExpoConfigPluginsWarnPrinted = true; } - compileModsAsync = _compileModsAsync; + const { exp } = await getPrebuildConfigAsync(projectDir, { platforms: ['ios'] }); + expWithMods = await compileModsAsync(exp, { + projectRoot: projectDir, + platforms: ['ios'], + introspect: true, + ignoreExistingNativeFiles: await hasIgnoredIosProjectAsync(projectDir, vcsClient), + }); } - const expWithMods = await compileModsAsync(exp, { - projectRoot: projectDir, - platforms: ['ios'], - introspect: true, - ignoreExistingNativeFiles: await hasIgnoredIosProjectAsync(projectDir, vcsClient), - }); return expWithMods.ios?.entitlements ?? {}; } finally { process.env = originalProcessEnv; diff --git a/packages/eas-cli/src/project/projectUtils.ts b/packages/eas-cli/src/project/projectUtils.ts index 82289feb21..18610c4bf7 100644 --- a/packages/eas-cli/src/project/projectUtils.ts +++ b/packages/eas-cli/src/project/projectUtils.ts @@ -72,6 +72,11 @@ export function isExpoNotificationsInstalled(projectDir: string): boolean { return !!(packageJson.dependencies && 'expo-notifications' in packageJson.dependencies); } +export function isExpoInstalled(projectDir: string): boolean { + const packageJson = getPackageJson(projectDir); + return !!(packageJson.dependencies && 'expo' in packageJson.dependencies); +} + export function isExpoUpdatesInstalledAsDevDependency(projectDir: string): boolean { const packageJson = getPackageJson(projectDir); return !!(packageJson.devDependencies && 'expo-updates' in packageJson.devDependencies); From ad86d28afdd37d2a5088248c466199b466d55703 Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Mon, 14 Oct 2024 13:37:50 +0200 Subject: [PATCH 7/9] add skip plugins again --- packages/eas-cli/src/project/expoConfig.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/eas-cli/src/project/expoConfig.ts b/packages/eas-cli/src/project/expoConfig.ts index 5197f2f7e1..59921eaa41 100644 --- a/packages/eas-cli/src/project/expoConfig.ts +++ b/packages/eas-cli/src/project/expoConfig.ts @@ -77,12 +77,14 @@ async function getExpoConfigInternalAsync( exp = getConfig(projectDir, { skipSDKVersionRequirement: true, ...(opts.isPublicConfig ? { isPublicConfig: true } : {}), + ...(opts.skipPlugins ? { skipPlugins: true } : {}), }).exp; } } else { exp = getConfig(projectDir, { skipSDKVersionRequirement: true, ...(opts.isPublicConfig ? { isPublicConfig: true } : {}), + ...(opts.skipPlugins ? { skipPlugins: true } : {}), }).exp; } From a97f7c9908c52d41cac154ea953484b5e66a69cb Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Mon, 14 Oct 2024 14:26:24 +0200 Subject: [PATCH 8/9] fix tests --- .../src/commands/project/__tests__/init.test.ts | 3 +++ .../src/commands/update/__tests__/index.test.ts | 11 +---------- .../update/__tests__/roll-back-to-embedded.test.ts | 11 +---------- .../src/credentials/__tests__/fixtures-context.ts | 3 ++- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/packages/eas-cli/src/commands/project/__tests__/init.test.ts b/packages/eas-cli/src/commands/project/__tests__/init.test.ts index 7ea1c0f237..937096ad0f 100644 --- a/packages/eas-cli/src/commands/project/__tests__/init.test.ts +++ b/packages/eas-cli/src/commands/project/__tests__/init.test.ts @@ -14,6 +14,7 @@ import { AppMutation } from '../../../graphql/mutations/AppMutation'; import { AppQuery } from '../../../graphql/queries/AppQuery'; import { createOrModifyExpoConfigAsync } from '../../../project/expoConfig'; import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync'; +import { isExpoInstalled } from '../../../project/projectUtils'; import { confirmAsync, promptAsync } from '../../../prompts'; import ProjectInit from '../init'; @@ -34,6 +35,7 @@ jest.mock('../../../ora', () => ({ })); jest.mock('../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync'); jest.mock('../../../commandUtils/context/contextUtils/getProjectIdAsync'); +jest.mock('../../../project/projectUtils'); let originalProcessArgv: string[]; @@ -91,6 +93,7 @@ function mockTestProject(options: { featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()), graphqlClient, }); + jest.mocked(isExpoInstalled).mockReturnValue(true); } const commandOptions = { root: '/test-project' } as any; diff --git a/packages/eas-cli/src/commands/update/__tests__/index.test.ts b/packages/eas-cli/src/commands/update/__tests__/index.test.ts index b942b30860..e30b20172e 100644 --- a/packages/eas-cli/src/commands/update/__tests__/index.test.ts +++ b/packages/eas-cli/src/commands/update/__tests__/index.test.ts @@ -140,16 +140,7 @@ describe(UpdatePublish.name, () => { // Add configuration to the project that should not be included in the update const { appJson } = mockTestProject({ - expoConfig: { - hooks: { - postPublish: [ - { - file: 'custom-hook.js', - config: { some: 'config' }, - }, - ], - }, - }, + expoConfig: {}, }); const { platforms, runtimeVersion } = mockTestExport({ platforms: ['ios'] }); diff --git a/packages/eas-cli/src/commands/update/__tests__/roll-back-to-embedded.test.ts b/packages/eas-cli/src/commands/update/__tests__/roll-back-to-embedded.test.ts index 0ffd93be63..61706cc97e 100644 --- a/packages/eas-cli/src/commands/update/__tests__/roll-back-to-embedded.test.ts +++ b/packages/eas-cli/src/commands/update/__tests__/roll-back-to-embedded.test.ts @@ -146,16 +146,7 @@ describe(UpdateRollBackToEmbedded.name, () => { // Add configuration to the project that should not be included in the update mockTestProject({ - expoConfig: { - hooks: { - postPublish: [ - { - file: 'custom-hook.js', - config: { some: 'config' }, - }, - ], - }, - }, + expoConfig: {}, }); const platforms = ['ios']; diff --git a/packages/eas-cli/src/credentials/__tests__/fixtures-context.ts b/packages/eas-cli/src/credentials/__tests__/fixtures-context.ts index 67f01b8d0b..14dbcecd3f 100644 --- a/packages/eas-cli/src/credentials/__tests__/fixtures-context.ts +++ b/packages/eas-cli/src/credentials/__tests__/fixtures-context.ts @@ -21,8 +21,9 @@ export function createCtxMock(mockOverride: Record = {}): Credentia }, hasAppleCtx: jest.fn(() => true), hasProjectContext: true, - exp: testAppJson, + getExpoConfigAsync: async () => testAppJson, projectDir: '.', + getProjectIdAsync: async () => 'test-project-id', }; return merge(defaultMock, mockOverride) as any; } From 8536a7a228f348645bbc20706c295ffbc2a0a663 Mon Sep 17 00:00:00 2001 From: Szymon Dziedzic Date: Tue, 15 Oct 2024 10:27:59 +0200 Subject: [PATCH 9/9] really fix tests --- .../context/contextUtils/__tests__/getProjectIdAsync-test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/eas-cli/src/commandUtils/context/contextUtils/__tests__/getProjectIdAsync-test.ts b/packages/eas-cli/src/commandUtils/context/contextUtils/__tests__/getProjectIdAsync-test.ts index a14d41b029..d0c10bde48 100644 --- a/packages/eas-cli/src/commandUtils/context/contextUtils/__tests__/getProjectIdAsync-test.ts +++ b/packages/eas-cli/src/commandUtils/context/contextUtils/__tests__/getProjectIdAsync-test.ts @@ -6,6 +6,7 @@ import { Role } from '../../../../graphql/generated'; import { AppQuery } from '../../../../graphql/queries/AppQuery'; import { learnMore } from '../../../../log'; import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync'; +import { isExpoInstalled } from '../../../../project/projectUtils'; import SessionManager from '../../../../user/SessionManager'; import { findProjectRootAsync } from '../findProjectDirAndVerifyProjectSetupAsync'; import { getProjectIdAsync } from '../getProjectIdAsync'; @@ -21,6 +22,7 @@ jest.mock('../../../../ora', () => ({ }), })); jest.mock('../../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync'); +jest.mock('../../../../project/projectUtils'); describe(getProjectIdAsync, () => { let sessionManager: SessionManager; @@ -71,6 +73,7 @@ describe(getProjectIdAsync, () => { ); jest.mocked(findProjectRootAsync).mockResolvedValue('/app'); + jest.mocked(isExpoInstalled).mockReturnValue(true); }); it('gets the project ID from app config if exists', async () => {