From 743ae7c6483c015de941e55f8078d45694712995 Mon Sep 17 00:00:00 2001 From: Josh McFarlin <32019743+Josh-McFarlin@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:18:20 -0700 Subject: [PATCH] [eas-cli] VCS Context Fields to replace helper functions (#2086) * [eas-cli] Add context fields for vcs clients * [eas-cli] Replace vcs client helper with new context field * [eas-cli] Pass instance of vcs client in helpers instead of re-calling vcs helper * update CHANGELOG.md --- CHANGELOG.md | 1 + packages/eas-cli/src/branch/utils.ts | 7 ++- .../build/android/__tests__/version-test.ts | 9 +++- packages/eas-cli/src/build/android/build.ts | 16 +++++- .../eas-cli/src/build/android/prepareJob.ts | 3 +- .../build/android/syncProjectConfiguration.ts | 5 +- packages/eas-cli/src/build/android/version.ts | 10 ++-- packages/eas-cli/src/build/build.ts | 8 +-- packages/eas-cli/src/build/configure.ts | 25 ++++++---- packages/eas-cli/src/build/context.ts | 2 + packages/eas-cli/src/build/createContext.ts | 7 ++- .../src/build/ios/__tests__/version-test.ts | 33 +++++++++---- packages/eas-cli/src/build/ios/build.ts | 4 ++ packages/eas-cli/src/build/ios/prepareJob.ts | 3 +- .../src/build/ios/syncProjectConfiguration.ts | 7 ++- packages/eas-cli/src/build/ios/version.ts | 28 +++++++---- packages/eas-cli/src/build/metadata.ts | 14 +++--- .../eas-cli/src/build/runBuildAndSubmit.ts | 18 +++++-- packages/eas-cli/src/build/utils/devClient.ts | 13 +++-- .../eas-cli/src/build/utils/repository.ts | 49 +++++++++++-------- packages/eas-cli/src/build/validate.ts | 5 +- .../eas-cli/src/commandUtils/EasCommand.ts | 4 ++ .../context/VcsClientContextField.ts | 9 ++++ .../eas-cli/src/commands/branch/create.ts | 4 +- packages/eas-cli/src/commands/build/cancel.ts | 1 + .../eas-cli/src/commands/build/configure.ts | 20 ++++++-- packages/eas-cli/src/commands/build/index.ts | 3 ++ .../eas-cli/src/commands/build/inspect.ts | 9 ++-- .../eas-cli/src/commands/build/internal.ts | 3 ++ packages/eas-cli/src/commands/build/list.ts | 1 + packages/eas-cli/src/commands/build/resign.ts | 5 ++ packages/eas-cli/src/commands/build/run.ts | 1 + .../eas-cli/src/commands/build/version/get.ts | 3 ++ .../eas-cli/src/commands/build/version/set.ts | 3 ++ .../src/commands/build/version/sync.ts | 13 ++++- packages/eas-cli/src/commands/build/view.ts | 1 + packages/eas-cli/src/commands/credentials.ts | 3 ++ packages/eas-cli/src/commands/diagnostics.ts | 12 +++-- .../eas-cli/src/commands/metadata/pull.ts | 5 +- .../eas-cli/src/commands/metadata/push.ts | 5 +- packages/eas-cli/src/commands/submit.ts | 3 ++ .../update/__tests__/configure.test.ts | 3 ++ .../eas-cli/src/commands/update/configure.ts | 6 ++- packages/eas-cli/src/commands/update/index.ts | 16 ++++-- .../commands/update/roll-back-to-embedded.ts | 16 ++++-- .../android/actions/BuildCredentialsUtils.ts | 1 + packages/eas-cli/src/credentials/context.ts | 4 ++ .../src/credentials/credentialsJson/update.ts | 15 +++--- .../SetUpAdhocProvisioningProfile-test.ts | 2 + .../src/credentials/manager/HelperActions.ts | 2 + .../src/credentials/manager/ManageAndroid.ts | 3 +- .../src/credentials/manager/ManageIos.ts | 3 ++ .../src/credentials/manager/SelectPlatform.ts | 2 + .../manager/__tests__/ManageAndroid-test.ts | 2 + packages/eas-cli/src/metadata/auth.ts | 15 ++++-- .../src/project/__tests__/workflow-test.ts | 22 ++++++--- .../android/__tests__/applicationId-test.ts | 32 ++++++++---- .../project/android/__tests__/gradle-test.ts | 37 +++++++++----- .../src/project/android/applicationId.ts | 10 ++-- .../eas-cli/src/project/android/gradle.ts | 6 ++- .../src/project/applicationIdentifier.ts | 18 ++++--- .../ios/__tests__/bundleIdentifier-test.ts | 24 ++++++--- .../src/project/ios/bundleIdentifier.ts | 10 ++-- .../eas-cli/src/project/ios/entitlements.ts | 6 ++- packages/eas-cli/src/project/ios/scheme.ts | 6 ++- packages/eas-cli/src/project/ios/target.ts | 41 +++++++++++----- packages/eas-cli/src/project/publish.ts | 42 +++++++++------- packages/eas-cli/src/project/workflow.ts | 19 ++++--- .../submit/android/AndroidSubmitCommand.ts | 4 +- .../__tests__/AndroidSubmitCommand-test.ts | 7 +++ .../__tests__/ServiceAccountSource-test.ts | 6 +++ packages/eas-cli/src/submit/context.ts | 5 ++ packages/eas-cli/src/submit/ios/AppProduce.ts | 2 +- .../eas-cli/src/submit/ios/AscApiKeySource.ts | 2 +- .../ios/__tests__/AscApiKeySource-test.ts | 5 ++ .../ios/__tests__/IosSubmitCommand-test.ts | 5 ++ packages/eas-cli/src/update/configure.ts | 10 ++-- .../eas-cli/src/update/ios/UpdatesModule.ts | 8 +-- 78 files changed, 560 insertions(+), 232 deletions(-) create mode 100644 packages/eas-cli/src/commandUtils/context/VcsClientContextField.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 440a9f53cd..2603629672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This is the log of notable changes to EAS CLI and related packages. ### 🧹 Chores - Add `requiredPackageManager` to metadata. ([#2067](https://github.com/expo/eas-cli/pull/2067) by [@kadikraman](https://github.com/kadikraman)) +- Move `getVcsClient` into command context. ([#2086](https://github.com/expo/eas-cli/pull/2086) by [@Josh-McFarlin](https://github.com/Josh-McFarlin)) - Display Apple device creation date when listing devices. ([#2092](https://github.com/expo/eas-cli/pull/2092) by [@radoslawkrzemien](https://github.com/radoslawkrzemien)) ## [5.4.0](https://github.com/expo/eas-cli/releases/tag/v5.4.0) - 2023-09-28 diff --git a/packages/eas-cli/src/branch/utils.ts b/packages/eas-cli/src/branch/utils.ts index fcb73b1003..d9affe89f9 100644 --- a/packages/eas-cli/src/branch/utils.ts +++ b/packages/eas-cli/src/branch/utils.ts @@ -1,9 +1,8 @@ -import { getVcsClient } from '../vcs'; +import { Client } from '../vcs/vcs'; -export async function getDefaultBranchNameAsync(): Promise { +export async function getDefaultBranchNameAsync(vcsClient: Client): Promise { return ( - (await getVcsClient().getBranchNameAsync()) || - `branch-${Math.random().toString(36).substring(2, 4)}` + (await vcsClient.getBranchNameAsync()) || `branch-${Math.random().toString(36).substring(2, 4)}` ); } diff --git a/packages/eas-cli/src/build/android/__tests__/version-test.ts b/packages/eas-cli/src/build/android/__tests__/version-test.ts index 01eb6c3cf7..9cca21117c 100644 --- a/packages/eas-cli/src/build/android/__tests__/version-test.ts +++ b/packages/eas-cli/src/build/android/__tests__/version-test.ts @@ -8,6 +8,7 @@ import os from 'os'; import path from 'path'; import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils'; +import { getVcsClient } from '../../../vcs'; import { BumpStrategy, bumpVersionAsync, @@ -18,6 +19,8 @@ import { const fsReal = jest.requireActual('fs').promises as typeof fs; jest.mock('fs'); +const vcsClient = getVcsClient(); + afterAll(async () => { // do not remove the following line // this fixes a weird error with tempy in @expo/image-utils @@ -191,7 +194,8 @@ describe(maybeResolveVersionsAsync, () => { const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync( '/app', exp, - {} as BuildProfile + {} as BuildProfile, + vcsClient ); expect(appVersion).toBe('3.0.0'); expect(appBuildVersion).toBe('123'); @@ -203,7 +207,8 @@ describe(maybeResolveVersionsAsync, () => { const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync( '/app', exp, - {} as BuildProfile + {} as BuildProfile, + vcsClient ); expect(appVersion).toBe('5.0.0'); expect(appBuildVersion).toBe('126'); diff --git a/packages/eas-cli/src/build/android/build.ts b/packages/eas-cli/src/build/android/build.ts index 6b1d8b6398..16c2559f48 100644 --- a/packages/eas-cli/src/build/android/build.ts +++ b/packages/eas-cli/src/build/android/build.ts @@ -59,13 +59,22 @@ This means that it will most likely produce an AAB and you will not be able to i await checkGoogleServicesFileAsync(ctx); await validatePNGsForManagedProjectAsync(ctx); - const gradleContext = await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile); + const gradleContext = await resolveGradleBuildContextAsync( + ctx.projectDir, + buildProfile, + ctx.vcsClient + ); if (ctx.workflow === Workflow.MANAGED) { await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx); } - const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext); + const applicationId = await getApplicationIdAsync( + ctx.projectDir, + ctx.exp, + ctx.vcsClient, + gradleContext + ); const versionCodeOverride = ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE ? await resolveRemoteVersionCodeAsync(ctx.graphqlClient, { @@ -74,6 +83,7 @@ This means that it will most likely produce an AAB and you will not be able to i exp: ctx.exp, applicationId, buildProfile, + vcsClient: ctx.vcsClient, }) : undefined; @@ -97,6 +107,7 @@ export async function prepareAndroidBuildAsync( ? false : ctx.buildProfile.autoIncrement, projectId: ctx.projectId, + vcsClient: ctx.vcsClient, }); }, prepareJobAsync: async ( @@ -136,6 +147,7 @@ async function ensureAndroidCredentialsAsync( const androidApplicationIdentifier = await getApplicationIdAsync( ctx.projectDir, ctx.exp, + ctx.vcsClient, ctx.android.gradleContext ); const provider = new AndroidCredentialsProvider(ctx.credentialsCtx, { diff --git a/packages/eas-cli/src/build/android/prepareJob.ts b/packages/eas-cli/src/build/android/prepareJob.ts index eebb7cb3d7..01600acb81 100644 --- a/packages/eas-cli/src/build/android/prepareJob.ts +++ b/packages/eas-cli/src/build/android/prepareJob.ts @@ -14,7 +14,6 @@ import slash from 'slash'; import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider'; import { getCustomBuildConfigPath } from '../../project/customBuildConfig'; import { getUsername } from '../../project/projectUtils'; -import { getVcsClient } from '../../vcs'; import { BuildContext } from '../context'; interface JobData { @@ -34,7 +33,7 @@ export async function prepareJobAsync( const username = getUsername(ctx.exp, ctx.user); const buildProfile: BuildProfile = ctx.buildProfile; const projectRootDirectory = - slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.'; + slash(path.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.'; const { credentials } = jobData; const buildCredentials = credentials ? { diff --git a/packages/eas-cli/src/build/android/syncProjectConfiguration.ts b/packages/eas-cli/src/build/android/syncProjectConfiguration.ts index 691df2b53c..d1a774ccd8 100644 --- a/packages/eas-cli/src/build/android/syncProjectConfiguration.ts +++ b/packages/eas-cli/src/build/android/syncProjectConfiguration.ts @@ -11,6 +11,7 @@ import Log from '../../log'; import { isExpoUpdatesInstalled } from '../../project/projectUtils'; import { resolveWorkflowAsync } from '../../project/workflow'; import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule'; +import { Client } from '../../vcs/vcs'; import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version'; export async function syncProjectConfigurationAsync( @@ -20,14 +21,16 @@ export async function syncProjectConfigurationAsync( exp, localAutoIncrement, projectId, + vcsClient, }: { projectDir: string; exp: ExpoConfig; localAutoIncrement?: AndroidVersionAutoIncrement; projectId: string; + vcsClient: Client; } ): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID); + const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient); const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false); if (workflow === Workflow.GENERIC) { diff --git a/packages/eas-cli/src/build/android/version.ts b/packages/eas-cli/src/build/android/version.ts index e2651a0d43..165d460bba 100644 --- a/packages/eas-cli/src/build/android/version.ts +++ b/packages/eas-cli/src/build/android/version.ts @@ -18,6 +18,7 @@ import { } from '../../project/android/gradleUtils'; import { getNextVersionCode } from '../../project/android/versions'; import { resolveWorkflowAsync } from '../../project/workflow'; +import { Client } from '../../vcs/vcs'; import { updateAppJsonConfigAsync } from '../utils/appJson'; import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version'; @@ -96,9 +97,10 @@ export async function bumpVersionInAppJsonAsync({ export async function maybeResolveVersionsAsync( projectDir: string, exp: ExpoConfig, - buildProfile: BuildProfile + buildProfile: BuildProfile, + vcsClient: Client ): Promise<{ appVersion?: string; appBuildVersion?: string }> { - const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID); + const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient); if (workflow === Workflow.GENERIC) { const buildGradle = await getAppBuildGradleAsync(projectDir); try { @@ -183,12 +185,14 @@ export async function resolveRemoteVersionCodeAsync( exp, applicationId, buildProfile, + vcsClient, }: { projectDir: string; projectId: string; exp: ExpoConfig; applicationId: string; buildProfile: BuildProfile; + vcsClient: Client; } ): Promise { const remoteVersions = await AppVersionQuery.latestVersionAsync( @@ -198,7 +202,7 @@ export async function resolveRemoteVersionCodeAsync( applicationId ); - const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile); + const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile, vcsClient); let currentBuildVersion: string; if (remoteVersions?.buildVersion) { currentBuildVersion = remoteVersions.buildVersion; diff --git a/packages/eas-cli/src/build/build.ts b/packages/eas-cli/src/build/build.ts index 62d129a3ee..a3453135fb 100644 --- a/packages/eas-cli/src/build/build.ts +++ b/packages/eas-cli/src/build/build.ts @@ -36,7 +36,6 @@ import { formatBytes } from '../utils/files'; import { printJsonOnlyOutput } from '../utils/json'; import { createProgressTracker } from '../utils/progress'; import { sleepAsync } from '../utils/promise'; -import { getVcsClient } from '../vcs'; import { BuildContext } from './context'; import { EasBuildDownForMaintenanceError, @@ -125,9 +124,10 @@ export async function prepareBuildRequestForPlatformAsync< } ); - if (await getVcsClient().isCommitRequiredAsync()) { + if (await ctx.vcsClient.isCommitRequiredAsync()) { Log.addNewLineIfNone(); await reviewAndCommitChangesAsync( + ctx.vcsClient, `[EAS Build] Run EAS Build for ${requestedPlatformDisplayNames[ctx.platform as Platform]}`, { nonInteractive: ctx.nonInteractive } ); @@ -137,7 +137,7 @@ export async function prepareBuildRequestForPlatformAsync< if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) { projectArchive = { type: ArchiveSourceType.PATH, - path: (await makeProjectTarballAsync()).path, + path: (await makeProjectTarballAsync(ctx.vcsClient)).path, }; } else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) { projectArchive = { @@ -245,7 +245,7 @@ async function uploadProjectAsync( 'https://expo.fyi/eas-build-archive' )}` ); - const projectTarball = await makeProjectTarballAsync(); + const projectTarball = await makeProjectTarballAsync(ctx.vcsClient); if (projectTarball.size > 1024 * 1024 * 100) { Log.warn( diff --git a/packages/eas-cli/src/build/configure.ts b/packages/eas-cli/src/build/configure.ts index 79ae710b6d..fc098a4094 100644 --- a/packages/eas-cli/src/build/configure.ts +++ b/packages/eas-cli/src/build/configure.ts @@ -6,12 +6,13 @@ import fs from 'fs-extra'; import Log, { learnMore } from '../log'; import { resolveWorkflowAsync } from '../project/workflow'; import { easCliVersion } from '../utils/easCli'; -import { getVcsClient } from '../vcs'; +import { Client } from '../vcs/vcs'; import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository'; interface ConfigureParams { projectDir: string; nonInteractive: boolean; + vcsClient: Client; } export async function easJsonExistsAsync(projectDir: string): Promise { @@ -36,14 +37,18 @@ export async function ensureProjectConfiguredAsync( return true; } -async function configureAsync({ projectDir, nonInteractive }: ConfigureParams): Promise { - await maybeBailOnRepoStatusAsync(); +async function configureAsync({ + projectDir, + nonInteractive, + vcsClient, +}: ConfigureParams): Promise { + await maybeBailOnRepoStatusAsync(vcsClient); - await createEasJsonAsync(projectDir); + await createEasJsonAsync(projectDir, vcsClient); - if (await getVcsClient().isCommitRequiredAsync()) { + if (await vcsClient.isCommitRequiredAsync()) { Log.newLine(); - await reviewAndCommitChangesAsync('Configure EAS Build', { + await reviewAndCommitChangesAsync(vcsClient, 'Configure EAS Build', { nonInteractive, }); } @@ -92,20 +97,20 @@ const EAS_JSON_BARE_DEFAULT: EasJson = { }, }; -async function createEasJsonAsync(projectDir: string): Promise { +async function createEasJsonAsync(projectDir: string, vcsClient: Client): Promise { const easJsonPath = EasJsonAccessor.formatEasJsonPath(projectDir); const hasAndroidNativeProject = - (await resolveWorkflowAsync(projectDir, Platform.ANDROID)) === Workflow.GENERIC; + (await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient)) === Workflow.GENERIC; const hasIosNativeProject = - (await resolveWorkflowAsync(projectDir, Platform.IOS)) === Workflow.GENERIC; + (await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient)) === Workflow.GENERIC; const easJson = hasAndroidNativeProject || hasIosNativeProject ? EAS_JSON_BARE_DEFAULT : EAS_JSON_MANAGED_DEFAULT; await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`); - await getVcsClient().trackFileAsync(easJsonPath); + await vcsClient.trackFileAsync(easJsonPath); Log.withTick( `Generated ${chalk.bold('eas.json')}. ${learnMore( 'https://docs.expo.dev/build-reference/eas-json/' diff --git a/packages/eas-cli/src/build/context.ts b/packages/eas-cli/src/build/context.ts index 87ad149786..3c8a54da82 100644 --- a/packages/eas-cli/src/build/context.ts +++ b/packages/eas-cli/src/build/context.ts @@ -12,6 +12,7 @@ import { GradleBuildContext } from '../project/android/gradle'; import { CustomBuildConfigMetadata } from '../project/customBuildConfig'; import { XcodeBuildContext } from '../project/ios/scheme'; import { Actor } from '../user/User'; +import { Client } from '../vcs/vcs'; import { LocalBuildOptions } from './local'; export type CommonContext = Omit, 'android' | 'ios'>; @@ -58,4 +59,5 @@ export interface BuildContext { ios: T extends Platform.IOS ? IosBuildContext : undefined; developmentClient: boolean; requiredPackageManager: NodePackageManager['name'] | null; + vcsClient: Client; } diff --git a/packages/eas-cli/src/build/createContext.ts b/packages/eas-cli/src/build/createContext.ts index 0996996349..6da379ce99 100644 --- a/packages/eas-cli/src/build/createContext.ts +++ b/packages/eas-cli/src/build/createContext.ts @@ -14,6 +14,7 @@ import { CustomBuildConfigMetadata } from '../project/customBuildConfig'; import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils'; import { resolveWorkflowAsync } from '../project/workflow'; import { Actor } from '../user/User'; +import { Client } from '../vcs/vcs'; import { createAndroidContextAsync } from './android/build'; import { BuildContext, CommonContext } from './context'; import { createIosContextAsync } from './ios/build'; @@ -35,6 +36,7 @@ export async function createBuildContextAsync({ actor, graphqlClient, analytics, + vcsClient, getDynamicPrivateProjectConfigAsync, customBuildConfigMetadata, }: { @@ -52,13 +54,14 @@ export async function createBuildContextAsync({ actor: Actor; graphqlClient: ExpoGraphqlClient; analytics: Analytics; + vcsClient: Client; getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn; customBuildConfigMetadata?: CustomBuildConfigMetadata; }): Promise> { const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env }); const projectName = exp.slug; const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId); - const workflow = await resolveWorkflowAsync(projectDir, platform); + const workflow = await resolveWorkflowAsync(projectDir, platform, vcsClient); const accountId = account.id; const runFromCI = getenv.boolish('CI', false); const developmentClient = @@ -79,6 +82,7 @@ export async function createBuildContextAsync({ analytics, env: buildProfile.env, easJsonCliConfig, + vcsClient, }); const devClientProperties = getDevClientEventProperties({ @@ -125,6 +129,7 @@ export async function createBuildContextAsync({ user: actor, graphqlClient, analytics, + vcsClient, workflow, message, runFromCI, diff --git a/packages/eas-cli/src/build/ios/__tests__/version-test.ts b/packages/eas-cli/src/build/ios/__tests__/version-test.ts index 2438a92b8a..339f6e5ad4 100644 --- a/packages/eas-cli/src/build/ios/__tests__/version-test.ts +++ b/packages/eas-cli/src/build/ios/__tests__/version-test.ts @@ -6,6 +6,7 @@ import os from 'os'; import type { XCBuildConfiguration } from 'xcode'; import { readPlistAsync } from '../../../utils/plist'; +import { getVcsClient } from '../../../vcs'; import { BumpStrategy, bumpVersionAsync, @@ -18,6 +19,8 @@ import { jest.mock('fs'); +const vcsClient = getVcsClient(); + const getXCBuildConfigurationFromPbxproj = jest.spyOn( IOSConfig.Target, 'getXCBuildConfigurationFromPbxproj' @@ -262,7 +265,7 @@ describe(readBuildNumberAsync, () => { describe('bare project', () => { it('reads the build number from native code', async () => { const exp = initBareWorkflowProject(); - const buildNumber = await readBuildNumberAsync('/app', exp, {}); + const buildNumber = await readBuildNumberAsync('/app', exp, {}, vcsClient); expect(buildNumber).toBe('1'); }); }); @@ -270,7 +273,7 @@ describe(readBuildNumberAsync, () => { describe('managed project', () => { it('reads the build number from expo config', async () => { const exp = initManagedProject(); - const buildNumber = await readBuildNumberAsync('/app', exp, {}); + const buildNumber = await readBuildNumberAsync('/app', exp, {}, vcsClient); expect(buildNumber).toBe('1'); }); }); @@ -280,16 +283,21 @@ describe(readShortVersionAsync, () => { describe('bare project', () => { it('reads the short version from native code', async () => { const exp = initBareWorkflowProject(); - const appVersion = await readShortVersionAsync('/app', exp, {}); + const appVersion = await readShortVersionAsync('/app', exp, {}, vcsClient); expect(appVersion).toBe('1.0.0'); }); it('evaluates interpolated build number', async () => { const exp = initBareWorkflowProject({ appVersion: '$(CURRENT_PROJECT_VERSION)', }); - const buildNumber = await readShortVersionAsync('/app', exp, { - CURRENT_PROJECT_VERSION: '1.0.0', - }); + const buildNumber = await readShortVersionAsync( + '/app', + exp, + { + CURRENT_PROJECT_VERSION: '1.0.0', + }, + vcsClient + ); expect(buildNumber).toBe('1.0.0'); }); @@ -299,9 +307,14 @@ describe(readShortVersionAsync, () => { }); await expect( - readShortVersionAsync('/app', exp, { - CURRENT_PROJECT_VERSION: '0.0.7.1.028', - }) + readShortVersionAsync( + '/app', + exp, + { + CURRENT_PROJECT_VERSION: '0.0.7.1.028', + }, + vcsClient + ) ).rejects.toThrowError( 'CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: 0.0.7.1.028' ); @@ -311,7 +324,7 @@ describe(readShortVersionAsync, () => { describe('managed project', () => { it('reads the version from app config', async () => { const exp = initBareWorkflowProject(); - const appVersion = await readShortVersionAsync('/app', exp, {}); + const appVersion = await readShortVersionAsync('/app', exp, {}, vcsClient); expect(appVersion).toBe('1.0.0'); }); }); diff --git a/packages/eas-cli/src/build/ios/build.ts b/packages/eas-cli/src/build/ios/build.ts index c77b168558..031a5e1318 100644 --- a/packages/eas-cli/src/build/ios/build.ts +++ b/packages/eas-cli/src/build/ios/build.ts @@ -39,6 +39,7 @@ export async function createIosContextAsync( projectDir: ctx.projectDir, nonInteractive: ctx.nonInteractive, exp: ctx.exp, + vcsClient: ctx.vcsClient, }, buildProfile ); @@ -47,6 +48,7 @@ export async function createIosContextAsync( exp: ctx.exp, xcodeBuildContext, env: buildProfile.env, + vcsClient: ctx.vcsClient, }); const applicationTarget = findApplicationTarget(targets); const buildNumberOverride = @@ -57,6 +59,7 @@ export async function createIosContextAsync( exp: ctx.exp, applicationTarget, buildProfile, + vcsClient: ctx.vcsClient, }) : undefined; return { @@ -86,6 +89,7 @@ export async function prepareIosBuildAsync( ? false : ctx.buildProfile.autoIncrement, projectId: ctx.projectId, + vcsClient: ctx.vcsClient, }); }, prepareJobAsync: async ( diff --git a/packages/eas-cli/src/build/ios/prepareJob.ts b/packages/eas-cli/src/build/ios/prepareJob.ts index 724db17767..921bf8fec4 100644 --- a/packages/eas-cli/src/build/ios/prepareJob.ts +++ b/packages/eas-cli/src/build/ios/prepareJob.ts @@ -16,7 +16,6 @@ import { IosCredentials, TargetCredentials } from '../../credentials/ios/types'; import { IosJobSecretsInput } from '../../graphql/generated'; import { getCustomBuildConfigPath } from '../../project/customBuildConfig'; import { getUsername } from '../../project/projectUtils'; -import { getVcsClient } from '../../vcs'; import { BuildContext } from '../context'; interface JobData { @@ -37,7 +36,7 @@ export async function prepareJobAsync( const username = getUsername(ctx.exp, ctx.user); const buildProfile: BuildProfile = ctx.buildProfile; const projectRootDirectory = - slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.'; + slash(path.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.'; const buildCredentials: Ios.BuildSecrets['buildCredentials'] = {}; if (jobData.credentials) { const targetNames = Object.keys(jobData.credentials); diff --git a/packages/eas-cli/src/build/ios/syncProjectConfiguration.ts b/packages/eas-cli/src/build/ios/syncProjectConfiguration.ts index 50822975b1..ca810537c1 100644 --- a/packages/eas-cli/src/build/ios/syncProjectConfiguration.ts +++ b/packages/eas-cli/src/build/ios/syncProjectConfiguration.ts @@ -7,6 +7,7 @@ import { Target } from '../../credentials/ios/types'; import { isExpoUpdatesInstalled } from '../../project/projectUtils'; import { resolveWorkflowAsync } from '../../project/workflow'; import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule'; +import { Client } from '../../vcs/vcs'; import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version'; export async function syncProjectConfigurationAsync( @@ -17,20 +18,22 @@ export async function syncProjectConfigurationAsync( targets, localAutoIncrement, projectId, + vcsClient, }: { projectDir: string; exp: ExpoConfig; targets: Target[]; localAutoIncrement?: IosVersionAutoIncrement; projectId: string; + vcsClient: Client; } ): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false); if (workflow === Workflow.GENERIC) { if (isExpoUpdatesInstalled(projectDir)) { - await syncUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId); + await syncUpdatesConfigurationAsync(graphqlClient, vcsClient, projectDir, exp, projectId); } await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets }); } else { diff --git a/packages/eas-cli/src/build/ios/version.ts b/packages/eas-cli/src/build/ios/version.ts index cde0bfdc7c..dddd846532 100644 --- a/packages/eas-cli/src/build/ios/version.ts +++ b/packages/eas-cli/src/build/ios/version.ts @@ -19,6 +19,7 @@ import { resolveWorkflowAsync } from '../../project/workflow'; import { promptAsync } from '../../prompts'; import uniqBy from '../../utils/expodash/uniqBy'; import { readPlistAsync, writePlistAsync } from '../../utils/plist'; +import { Client } from '../../vcs/vcs'; import { updateAppJsonConfigAsync } from '../utils/appJson'; import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version'; @@ -110,9 +111,10 @@ function validateShortVersion(shortVersion: string | undefined): void { export async function readShortVersionAsync( projectDir: string, exp: ExpoConfig, - buildSettings: XCBuildConfiguration['buildSettings'] + buildSettings: XCBuildConfiguration['buildSettings'], + vcsClient: Client ): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); if (workflow === Workflow.GENERIC) { const infoPlist = await readInfoPlistAsync(projectDir, buildSettings); @@ -131,9 +133,10 @@ export async function readShortVersionAsync( export async function readBuildNumberAsync( projectDir: string, exp: ExpoConfig, - buildSettings: XCBuildConfiguration['buildSettings'] + buildSettings: XCBuildConfiguration['buildSettings'], + vcsClient: Client ): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); if (workflow === Workflow.GENERIC) { const infoPlist = await readInfoPlistAsync(projectDir, buildSettings); return ( @@ -147,7 +150,8 @@ export async function readBuildNumberAsync( export async function maybeResolveVersionsAsync( projectDir: string, exp: ExpoConfig, - targets: Target[] + targets: Target[], + vcsClient: Client ): Promise<{ appVersion?: string; appBuildVersion?: string }> { const applicationTarget = findApplicationTarget(targets); try { @@ -155,12 +159,14 @@ export async function maybeResolveVersionsAsync( appBuildVersion: await readBuildNumberAsync( projectDir, exp, - applicationTarget.buildSettings ?? {} + applicationTarget.buildSettings ?? {}, + vcsClient ), appVersion: await readShortVersionAsync( projectDir, exp, - applicationTarget.buildSettings ?? {} + applicationTarget.buildSettings ?? {}, + vcsClient ), }; } catch (err: any) { @@ -278,12 +284,14 @@ export async function resolveRemoteBuildNumberAsync( exp, applicationTarget, buildProfile, + vcsClient, }: { projectDir: string; projectId: string; exp: ExpoConfig; applicationTarget: Target; buildProfile: BuildProfile; + vcsClient: Client; } ): Promise { const remoteVersions = await AppVersionQuery.latestVersionAsync( @@ -296,12 +304,14 @@ export async function resolveRemoteBuildNumberAsync( const localBuildNumber = await readBuildNumberAsync( projectDir, exp, - applicationTarget.buildSettings ?? {} + applicationTarget.buildSettings ?? {}, + vcsClient ); const localShortVersion = await readShortVersionAsync( projectDir, exp, - applicationTarget.buildSettings ?? {} + applicationTarget.buildSettings ?? {}, + vcsClient ); let currentBuildVersion: string; if (remoteVersions?.buildVersion) { diff --git a/packages/eas-cli/src/build/metadata.ts b/packages/eas-cli/src/build/metadata.ts index 5416867476..4b7b4359d5 100644 --- a/packages/eas-cli/src/build/metadata.ts +++ b/packages/eas-cli/src/build/metadata.ts @@ -15,7 +15,6 @@ import { readReleaseChannelSafelyAsync as readIosReleaseChannelSafelyAsync, } from '../update/ios/UpdatesModule'; import { easCliVersion } from '../utils/easCli'; -import { getVcsClient } from '../vcs'; import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version'; import { BuildContext } from './context'; import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version'; @@ -24,7 +23,6 @@ import { LocalBuildMode } from './local'; export async function collectMetadataAsync( ctx: BuildContext ): Promise { - const vcsClient = getVcsClient(); const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx); const distribution = ('simulator' in ctx.buildProfile && ctx.buildProfile.simulator @@ -44,14 +42,14 @@ export async function collectMetadataAsync( appName: ctx.exp.name, appIdentifier: resolveAppIdentifier(ctx), buildProfile: ctx.buildProfileName, - gitCommitHash: await vcsClient.getCommitHashAsync(), + gitCommitHash: await ctx.vcsClient.getCommitHashAsync(), gitCommitMessage: truncateGitCommitMessage( - (await vcsClient.getLastCommitMessageAsync()) ?? undefined + (await ctx.vcsClient.getLastCommitMessageAsync()) ?? undefined ), isGitWorkingTreeDirty: ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL ? false - : await vcsClient.hasUncommittedChangesAsync(), + : await ctx.vcsClient.hasUncommittedChangesAsync(), username: getUsername(ctx.exp, ctx.user), message: ctx.message, ...(ctx.platform === Platform.IOS && { @@ -77,7 +75,8 @@ async function maybeResolveVersionsAsync( const resolvedVersion = await maybeResolveIosVersionsAsync( ctx.projectDir, ctx.exp, - iosContext.ios.targets + iosContext.ios.targets, + ctx.vcsClient ); if (iosContext.ios.buildNumberOverride) { return { @@ -91,7 +90,8 @@ async function maybeResolveVersionsAsync( const resolvedVersion = await maybeResolveAndroidVersionsAsync( ctx.projectDir, ctx.exp, - androidCtx.buildProfile + androidCtx.buildProfile, + ctx.vcsClient ); if (androidCtx.android.versionCodeOverride) { return { diff --git a/packages/eas-cli/src/build/runBuildAndSubmit.ts b/packages/eas-cli/src/build/runBuildAndSubmit.ts index 9240386e1e..b96fac14a6 100644 --- a/packages/eas-cli/src/build/runBuildAndSubmit.ts +++ b/packages/eas-cli/src/build/runBuildAndSubmit.ts @@ -66,7 +66,7 @@ import { downloadAndMaybeExtractAppAsync } from '../utils/download'; import { truthy } from '../utils/expodash/filter'; import { printJsonOnlyOutput } from '../utils/json'; import { ProfileData, getProfilesAsync } from '../utils/profiles'; -import { getVcsClient } from '../vcs'; +import { Client } from '../vcs/vcs'; import { prepareAndroidBuildAsync } from './android/build'; import { BuildRequestSender, MaybeBuildFragment, waitForBuildEndAsync } from './build'; import { ensureProjectConfiguredAsync } from './configure'; @@ -98,17 +98,19 @@ export interface BuildFlags { export async function runBuildAndSubmitAsync( graphqlClient: ExpoGraphqlClient, analytics: Analytics, + vcsClient: Client, projectDir: string, flags: BuildFlags, actor: Actor, getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn ): Promise { - await getVcsClient().ensureRepoExistsAsync(); - await ensureRepoIsCleanAsync(flags.nonInteractive); + await vcsClient.ensureRepoExistsAsync(); + await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive); await ensureProjectConfiguredAsync({ projectDir, nonInteractive: flags.nonInteractive, + vcsClient, }); const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir); const easJsonCliConfig: EasJson['cli'] = @@ -134,6 +136,7 @@ export async function runBuildAndSubmitAsync( projectDir, nonInteractive: flags.nonInteractive, buildProfiles, + vcsClient, }); const customBuildConfigMetadataByPlatform: { [p in AppPlatform]?: CustomBuildConfigMetadata } = @@ -163,6 +166,7 @@ export async function runBuildAndSubmitAsync( actor, graphqlClient, analytics, + vcsClient, getDynamicPrivateProjectConfigAsync, customBuildConfigMetadata: customBuildConfigMetadataByPlatform[platform], }); @@ -274,6 +278,7 @@ async function prepareAndStartBuildAsync({ actor, graphqlClient, analytics, + vcsClient, getDynamicPrivateProjectConfigAsync, customBuildConfigMetadata, }: { @@ -285,6 +290,7 @@ async function prepareAndStartBuildAsync({ actor: Actor; graphqlClient: ExpoGraphqlClient; analytics: Analytics; + vcsClient: Client; getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn; customBuildConfigMetadata?: CustomBuildConfigMetadata; }): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext }> { @@ -303,6 +309,7 @@ async function prepareAndStartBuildAsync({ actor, graphqlClient, analytics, + vcsClient, getDynamicPrivateProjectConfigAsync, customBuildConfigMetadata, }); @@ -329,6 +336,7 @@ async function prepareAndStartBuildAsync({ exp: buildCtx.exp, projectId: buildCtx.projectId, projectDir, + vcsClient: buildCtx.vcsClient, sdkVersion: buildCtx.exp.sdkVersion, nonInteractive: flags.nonInteractive, buildProfile, @@ -411,6 +419,7 @@ async function prepareAndStartSubmissionAsync({ analytics: buildCtx.analytics, projectId: buildCtx.projectId, exp: buildCtx.exp, + vcsClient: buildCtx.vcsClient, }); if (moreBuilds) { @@ -471,6 +480,7 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({ graphqlClient, projectId, projectDir, + vcsClient, buildProfile, nonInteractive, sdkVersion, @@ -479,6 +489,7 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({ exp: ExpoConfig; projectId: string; projectDir: string; + vcsClient: Client; buildProfile: ProfileData<'build'>; nonInteractive: boolean; sdkVersion?: string; @@ -508,6 +519,7 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({ projectId, projectDir, platform: RequestedPlatform.All, + vcsClient, }); Log.withTick('Installed expo-updates and configured EAS Update.'); throw new Error('Command must be re-run to pick up new updates configuration.'); diff --git a/packages/eas-cli/src/build/utils/devClient.ts b/packages/eas-cli/src/build/utils/devClient.ts index d6ade50126..45ad523280 100644 --- a/packages/eas-cli/src/build/utils/devClient.ts +++ b/packages/eas-cli/src/build/utils/devClient.ts @@ -10,15 +10,17 @@ import { resolveWorkflowAsync } from '../../project/workflow'; import { confirmAsync } from '../../prompts'; import { expoCommandAsync } from '../../utils/expoCli'; import { ProfileData } from '../../utils/profiles'; -import { getVcsClient } from '../../vcs'; +import { Client } from '../../vcs/vcs'; import { reviewAndCommitChangesAsync } from './repository'; export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({ projectDir, + vcsClient, nonInteractive = false, buildProfiles = [], }: { projectDir: string; + vcsClient: Client; nonInteractive?: boolean; buildProfiles?: ProfileData<'build'>[]; }): Promise { @@ -41,7 +43,7 @@ export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({ ); const workflowPerPlatformList = await Promise.all( - platformsToCheck.map(platform => resolveWorkflowAsync(projectDir, platform)) + platformsToCheck.map(platform => resolveWorkflowAsync(projectDir, platform, vcsClient)) ); Log.newLine(); @@ -62,7 +64,7 @@ export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({ instructions: 'The command will abort unless you agree.', }); if (install) { - await installExpoDevClientAsync(projectDir, { nonInteractive }); + await installExpoDevClientAsync(projectDir, vcsClient, { nonInteractive }); } else { Errors.error(`Install ${chalk.bold('expo-dev-client')} manually and come back later.`, { exit: 1, @@ -100,6 +102,7 @@ async function isExpoDevClientInstalledAsync(projectDir: string): Promise { Log.newLine(); @@ -107,8 +110,8 @@ async function installExpoDevClientAsync( Log.newLine(); await expoCommandAsync(projectDir, ['install', 'expo-dev-client']); Log.newLine(); - if (await getVcsClient().isCommitRequiredAsync()) { - await reviewAndCommitChangesAsync('Install expo-dev-client', { + if (await vcsClient.isCommitRequiredAsync()) { + await reviewAndCommitChangesAsync(vcsClient, 'Install expo-dev-client', { nonInteractive, }); } diff --git a/packages/eas-cli/src/build/utils/repository.ts b/packages/eas-cli/src/build/utils/repository.ts index 9d91d8ffff..5c6874c4c4 100644 --- a/packages/eas-cli/src/build/utils/repository.ts +++ b/packages/eas-cli/src/build/utils/repository.ts @@ -10,10 +10,10 @@ import { confirmAsync, promptAsync } from '../../prompts'; import { formatBytes } from '../../utils/files'; import { getTmpDirectory } from '../../utils/paths'; import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer'; -import { getVcsClient } from '../../vcs'; +import { Client } from '../../vcs/vcs'; -export async function maybeBailOnRepoStatusAsync(): Promise { - if (!(await getVcsClient().isCommitRequiredAsync())) { +export async function maybeBailOnRepoStatusAsync(vcsClient: Client): Promise { + if (!(await vcsClient.isCommitRequiredAsync())) { return; } Log.addNewLineIfNone(); @@ -32,8 +32,11 @@ export async function maybeBailOnRepoStatusAsync(): Promise { } } -export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise { - if (!(await getVcsClient().isCommitRequiredAsync())) { +export async function ensureRepoIsCleanAsync( + vcsClient: Client, + nonInteractive = false +): Promise { + if (!(await vcsClient.isCommitRequiredAsync())) { return; } Log.addNewLineIfNone(); @@ -50,19 +53,22 @@ export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise { +export async function commitPromptAsync( + vcsClient: Client, + { + initialCommitMessage, + commitAllFiles, + }: { + initialCommitMessage?: string; + commitAllFiles?: boolean; + } = {} +): Promise { const { message } = await promptAsync({ type: 'text', name: 'message', @@ -70,14 +76,16 @@ export async function commitPromptAsync({ initial: initialCommitMessage, validate: (input: string) => input !== '', }); - await getVcsClient().commitAsync({ + await vcsClient.commitAsync({ commitAllFiles, commitMessage: message, nonInteractive: false, }); } -export async function makeProjectTarballAsync(): Promise<{ path: string; size: number }> { +export async function makeProjectTarballAsync( + vcsClient: Client +): Promise<{ path: string; size: number }> { const spinner = ora('Compressing project files'); await fs.mkdirp(getTmpDirectory()); @@ -99,7 +107,7 @@ export async function makeProjectTarballAsync(): Promise<{ path: string; size: n startTimer(compressTimerLabel); try { - await getVcsClient().makeShallowCopyAsync(shallowClonePath); + await vcsClient.makeShallowCopyAsync(shallowClonePath); await tar.create({ cwd: shallowClonePath, file: tarPath, prefix: 'project', gzip: true }, [ '.', ]); @@ -133,11 +141,12 @@ enum ShouldCommitChanges { } export async function reviewAndCommitChangesAsync( + vcsClient: Client, initialCommitMessage: string, { nonInteractive, askedFirstTime = true }: { nonInteractive: boolean; askedFirstTime?: boolean } ): Promise { if (process.env.EAS_BUILD_AUTOCOMMIT) { - await getVcsClient().commitAsync({ + await vcsClient.commitAsync({ commitMessage: initialCommitMessage, commitAllFiles: false, nonInteractive, @@ -171,11 +180,11 @@ export async function reviewAndCommitChangesAsync( "Aborting, run the command again once you're ready. Make sure to commit any changes you've made." ); } else if (selected === ShouldCommitChanges.Yes) { - await commitPromptAsync({ initialCommitMessage }); + await commitPromptAsync(vcsClient, { initialCommitMessage }); Log.withTick('Committed changes.'); } else if (selected === ShouldCommitChanges.ShowDiffFirst) { - await getVcsClient().showDiffAsync(); - await reviewAndCommitChangesAsync(initialCommitMessage, { + await vcsClient.showDiffAsync(); + await reviewAndCommitChangesAsync(vcsClient, initialCommitMessage, { nonInteractive, askedFirstTime: false, }); diff --git a/packages/eas-cli/src/build/validate.ts b/packages/eas-cli/src/build/validate.ts index df8fda82f4..666cbd1889 100644 --- a/packages/eas-cli/src/build/validate.ts +++ b/packages/eas-cli/src/build/validate.ts @@ -6,7 +6,6 @@ import semver from 'semver'; import Log, { learnMore } from '../log'; import { isPNGAsync } from '../utils/image'; -import { getVcsClient } from '../vcs'; import { CommonContext } from './context'; export function checkNodeEnvVariable(ctx: CommonContext): void { @@ -28,12 +27,12 @@ export async function checkGoogleServicesFileAsync( if (!googleServicesFilePath) { return; } - const rootDir = path.normalize(await getVcsClient().getRootPathAsync()); + const rootDir = path.normalize(await ctx.vcsClient.getRootPathAsync()); const absGoogleServicesFilePath = path.resolve(ctx.projectDir, googleServicesFilePath); if ( (await fs.pathExists(absGoogleServicesFilePath)) && (!isInsideDirectory(absGoogleServicesFilePath, rootDir) || - (await getVcsClient().isFileIgnoredAsync(path.relative(rootDir, absGoogleServicesFilePath)))) + (await ctx.vcsClient.isFileIgnoredAsync(path.relative(rootDir, absGoogleServicesFilePath)))) ) { Log.warn( `File specified via "${ctx.platform}.googleServicesFile" field in your app.json is not checked in to your repository and won't be uploaded to the builder.` diff --git a/packages/eas-cli/src/commandUtils/EasCommand.ts b/packages/eas-cli/src/commandUtils/EasCommand.ts index f3c1c1fa93..6f2429b61e 100644 --- a/packages/eas-cli/src/commandUtils/EasCommand.ts +++ b/packages/eas-cli/src/commandUtils/EasCommand.ts @@ -22,6 +22,7 @@ import { OptionalPrivateProjectConfigContextField } from './context/OptionalPriv import { PrivateProjectConfigContextField } from './context/PrivateProjectConfigContextField'; import ProjectDirContextField from './context/ProjectDirContextField'; import SessionManagementContextField from './context/SessionManagementContextField'; +import VcsClientContextField from './context/VcsClientContextField'; import { EasCommandError } from './errors'; export type ContextInput< @@ -101,6 +102,9 @@ export default abstract class EasCommand extends Command { Analytics: { analytics: new AnalyticsContextField(), }, + Vcs: { + vcsClient: new VcsClientContextField(), + }, }; /** diff --git a/packages/eas-cli/src/commandUtils/context/VcsClientContextField.ts b/packages/eas-cli/src/commandUtils/context/VcsClientContextField.ts new file mode 100644 index 0000000000..8f09538010 --- /dev/null +++ b/packages/eas-cli/src/commandUtils/context/VcsClientContextField.ts @@ -0,0 +1,9 @@ +import { getVcsClient } from '../../vcs'; +import { Client } from '../../vcs/vcs'; +import ContextField from './ContextField'; + +export default class VcsClientContextField extends ContextField { + async getValueAsync(): Promise { + return getVcsClient(); + } +} diff --git a/packages/eas-cli/src/commands/branch/create.ts b/packages/eas-cli/src/commands/branch/create.ts index 1982f8c71c..4731c657bd 100644 --- a/packages/eas-cli/src/commands/branch/create.ts +++ b/packages/eas-cli/src/commands/branch/create.ts @@ -27,6 +27,7 @@ export default class BranchCreate extends EasCommand { static override contextDefinition = { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -37,6 +38,7 @@ export default class BranchCreate extends EasCommand { const { privateProjectConfig: { projectId }, loggedIn: { graphqlClient }, + vcsClient, } = await this.getContextAsync(BranchCreate, { nonInteractive, }); @@ -56,7 +58,7 @@ export default class BranchCreate extends EasCommand { type: 'text', name: 'name', message: 'Provide a branch name:', - initial: await getDefaultBranchNameAsync(), + initial: await getDefaultBranchNameAsync(vcsClient), validate: value => (value ? true : validationMessage), })); } diff --git a/packages/eas-cli/src/commands/build/cancel.ts b/packages/eas-cli/src/commands/build/cancel.ts index acee88871f..28db2d99f9 100644 --- a/packages/eas-cli/src/commands/build/cancel.ts +++ b/packages/eas-cli/src/commands/build/cancel.ts @@ -139,6 +139,7 @@ export default class BuildCancel extends EasCommand { static override contextDefinition = { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { diff --git a/packages/eas-cli/src/commands/build/configure.ts b/packages/eas-cli/src/commands/build/configure.ts index 0e40b1fd3b..2bde23eef0 100644 --- a/packages/eas-cli/src/commands/build/configure.ts +++ b/packages/eas-cli/src/commands/build/configure.ts @@ -16,7 +16,6 @@ import { ensureUseClassicUpdatesIsRemovedAsync, } from '../../update/configure'; import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule'; -import { getVcsClient } from '../../vcs'; export default class BuildConfigure extends EasCommand { static override description = 'configure the project to support EAS Build'; @@ -32,6 +31,7 @@ export default class BuildConfigure extends EasCommand { static override contextDefinition = { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -39,6 +39,7 @@ export default class BuildConfigure extends EasCommand { const { privateProjectConfig: { exp, projectId, projectDir }, loggedIn: { graphqlClient }, + vcsClient, } = await this.getContextAsync(BuildConfigure, { nonInteractive: false, }); @@ -47,7 +48,9 @@ export default class BuildConfigure extends EasCommand { '💡 The following process will configure your iOS and/or Android project to be compatible with EAS Build. These changes only apply to your local project files and you can safely revert them at any time.' ); - await getVcsClient().ensureRepoExistsAsync(); + // BuildConfigure.ContextOptions.Vcs.client.getValueAsync() + + await vcsClient.ensureRepoExistsAsync(); const expoUpdatesIsInstalled = isExpoUpdatesInstalled(projectDir); @@ -64,6 +67,7 @@ export default class BuildConfigure extends EasCommand { const didCreateEasJson = await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false, + vcsClient, }); if (didCreateEasJson && isUsingEASUpdate(exp, projectId)) { if (exp.updates?.useClassicUpdates) { @@ -78,16 +82,22 @@ export default class BuildConfigure extends EasCommand { // configure expo-updates if (expoUpdatesIsInstalled) { if ([RequestedPlatform.Android, RequestedPlatform.All].includes(platform)) { - const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID); + const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient); if (workflow === Workflow.GENERIC) { await syncAndroidUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId); } } if ([RequestedPlatform.Ios, RequestedPlatform.All].includes(platform)) { - const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); if (workflow === Workflow.GENERIC) { - await syncIosUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId); + await syncIosUpdatesConfigurationAsync( + graphqlClient, + vcsClient, + projectDir, + exp, + projectId + ); } } } diff --git a/packages/eas-cli/src/commands/build/index.ts b/packages/eas-cli/src/commands/build/index.ts index 1f7d4e2782..981d728f35 100644 --- a/packages/eas-cli/src/commands/build/index.ts +++ b/packages/eas-cli/src/commands/build/index.ts @@ -106,6 +106,7 @@ export default class Build extends EasCommand { ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.ProjectDir, ...this.ContextOptions.Analytics, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -121,6 +122,7 @@ export default class Build extends EasCommand { getDynamicPrivateProjectConfigAsync, projectDir, analytics, + vcsClient, } = await this.getContextAsync(Build, { nonInteractive: flags.nonInteractive, }); @@ -141,6 +143,7 @@ export default class Build extends EasCommand { await runBuildAndSubmitAsync( graphqlClient, analytics, + vcsClient, projectDir, flagsWithPlatform, actor, diff --git a/packages/eas-cli/src/commands/build/inspect.ts b/packages/eas-cli/src/commands/build/inspect.ts index 07800683e3..f49a144288 100644 --- a/packages/eas-cli/src/commands/build/inspect.ts +++ b/packages/eas-cli/src/commands/build/inspect.ts @@ -11,7 +11,6 @@ import Log from '../../log'; import { ora } from '../../ora'; import { RequestedPlatform } from '../../platform'; import { getTmpDirectory } from '../../utils/paths'; -import { getVcsClient } from '../../vcs'; enum InspectStage { ARCHIVE = 'archive', @@ -67,6 +66,7 @@ export default class BuildInspect extends EasCommand { ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.ProjectDir, ...this.ContextOptions.Analytics, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -76,6 +76,7 @@ export default class BuildInspect extends EasCommand { getDynamicPrivateProjectConfigAsync, projectDir, analytics, + vcsClient, } = await this.getContextAsync(BuildInspect, { nonInteractive: false, }); @@ -92,15 +93,15 @@ export default class BuildInspect extends EasCommand { await this.prepareOutputDirAsync(outputDirectory, flags.force); if (flags.stage === InspectStage.ARCHIVE) { - const vcs = getVcsClient(); - await vcs.ensureRepoExistsAsync(); - await vcs.makeShallowCopyAsync(tmpWorkingdir); + await vcsClient.ensureRepoExistsAsync(); + await vcsClient.makeShallowCopyAsync(tmpWorkingdir); await this.copyToOutputDirAsync(tmpWorkingdir, outputDirectory); } else { try { await runBuildAndSubmitAsync( graphqlClient, analytics, + vcsClient, projectDir, { nonInteractive: false, diff --git a/packages/eas-cli/src/commands/build/internal.ts b/packages/eas-cli/src/commands/build/internal.ts index 20937eaada..e55417c28e 100644 --- a/packages/eas-cli/src/commands/build/internal.ts +++ b/packages/eas-cli/src/commands/build/internal.ts @@ -37,6 +37,7 @@ export default class BuildInternal extends EasCommand { ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.ProjectDir, ...this.ContextOptions.Analytics, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -49,6 +50,7 @@ export default class BuildInternal extends EasCommand { getDynamicPrivateProjectConfigAsync, projectDir, analytics, + vcsClient, } = await this.getContextAsync(BuildInternal, { nonInteractive: true, }); @@ -60,6 +62,7 @@ export default class BuildInternal extends EasCommand { await runBuildAndSubmitAsync( graphqlClient, analytics, + vcsClient, projectDir, { requestedPlatform: flags.platform as RequestedPlatform, diff --git a/packages/eas-cli/src/commands/build/list.ts b/packages/eas-cli/src/commands/build/list.ts index a929bca7bb..7b1c2a4e11 100644 --- a/packages/eas-cli/src/commands/build/list.ts +++ b/packages/eas-cli/src/commands/build/list.ts @@ -56,6 +56,7 @@ export default class BuildList extends EasCommand { static override contextDefinition = { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { diff --git a/packages/eas-cli/src/commands/build/resign.ts b/packages/eas-cli/src/commands/build/resign.ts index a006a06912..ce649bd0fb 100644 --- a/packages/eas-cli/src/commands/build/resign.ts +++ b/packages/eas-cli/src/commands/build/resign.ts @@ -88,6 +88,7 @@ export default class BuildResign extends EasCommand { ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.ProjectDir, ...this.ContextOptions.Analytics, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -104,6 +105,7 @@ export default class BuildResign extends EasCommand { getDynamicPrivateProjectConfigAsync, projectDir, analytics, + vcsClient, } = await this.getContextAsync(BuildResign, { nonInteractive: flags.nonInteractive, }); @@ -145,6 +147,7 @@ export default class BuildResign extends EasCommand { analytics, env: buildProfile.env, easJsonCliConfig, + vcsClient, }); if (buildProfile.credentialsSource !== CredentialsSource.LOCAL && !nonInteractive) { await credentialsCtx.appStore.ensureAuthenticatedAsync(); @@ -154,6 +157,7 @@ export default class BuildResign extends EasCommand { projectDir, nonInteractive, exp, + vcsClient, }, buildProfile ); @@ -162,6 +166,7 @@ export default class BuildResign extends EasCommand { exp, xcodeBuildContext, env: buildProfile.env, + vcsClient, }); const credentialsResult = await ensureIosCredentialsForBuildResignAsync( credentialsCtx, diff --git a/packages/eas-cli/src/commands/build/run.ts b/packages/eas-cli/src/commands/build/run.ts index 3da3f4fbdb..193272ae8f 100644 --- a/packages/eas-cli/src/commands/build/run.ts +++ b/packages/eas-cli/src/commands/build/run.ts @@ -73,6 +73,7 @@ export default class Run extends EasCommand { ...this.ContextOptions.LoggedIn, ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.ProjectDir, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { diff --git a/packages/eas-cli/src/commands/build/version/get.ts b/packages/eas-cli/src/commands/build/version/get.ts index 0604fc6045..050a477c72 100644 --- a/packages/eas-cli/src/commands/build/version/get.ts +++ b/packages/eas-cli/src/commands/build/version/get.ts @@ -38,6 +38,7 @@ export default class BuildVersionGetView extends EasCommand { ...this.ContextOptions.LoggedIn, ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.ProjectDir, + ...this.ContextOptions.Vcs, }; public async runAsync(): Promise { @@ -49,6 +50,7 @@ export default class BuildVersionGetView extends EasCommand { loggedIn: { graphqlClient }, getDynamicPrivateProjectConfigAsync, projectDir, + vcsClient, } = await this.getContextAsync(BuildVersionGetView, { nonInteractive: true, }); @@ -85,6 +87,7 @@ export default class BuildVersionGetView extends EasCommand { exp, buildProfile: profile, platform, + vcsClient, }); const remoteVersions = await AppVersionQuery.latestVersionAsync( graphqlClient, diff --git a/packages/eas-cli/src/commands/build/version/set.ts b/packages/eas-cli/src/commands/build/version/set.ts index 39e30b6307..f7bf613044 100644 --- a/packages/eas-cli/src/commands/build/version/set.ts +++ b/packages/eas-cli/src/commands/build/version/set.ts @@ -41,6 +41,7 @@ export default class BuildVersionSetView extends EasCommand { ...this.ContextOptions.LoggedIn, ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.ProjectDir, + ...this.ContextOptions.Vcs, }; public async runAsync(): Promise { @@ -49,6 +50,7 @@ export default class BuildVersionSetView extends EasCommand { loggedIn: { graphqlClient }, getDynamicPrivateProjectConfigAsync, projectDir, + vcsClient, } = await this.getContextAsync(BuildVersionSetView, { nonInteractive: false, }); @@ -74,6 +76,7 @@ export default class BuildVersionSetView extends EasCommand { exp, buildProfile: profile, platform, + vcsClient, }); const remoteVersions = await AppVersionQuery.latestVersionAsync( graphqlClient, diff --git a/packages/eas-cli/src/commands/build/version/sync.ts b/packages/eas-cli/src/commands/build/version/sync.ts index 179d4cc60e..5e423a41e0 100644 --- a/packages/eas-cli/src/commands/build/version/sync.ts +++ b/packages/eas-cli/src/commands/build/version/sync.ts @@ -28,6 +28,7 @@ import { } from '../../../project/remoteVersionSource'; import { resolveWorkflowAsync } from '../../../project/workflow'; import { getProfilesAsync } from '../../../utils/profiles'; +import { Client } from '../../../vcs/vcs'; interface SyncContext { projectDir: string; @@ -35,6 +36,7 @@ interface SyncContext { workflow: Workflow; profile: BuildProfile; buildVersion: string; + vcsClient: Client; } export default class BuildVersionSyncView extends EasCommand { @@ -58,6 +60,7 @@ export default class BuildVersionSyncView extends EasCommand { ...this.ContextOptions.LoggedIn, ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.ProjectDir, + ...this.ContextOptions.Vcs, }; public async runAsync(): Promise { @@ -66,6 +69,7 @@ export default class BuildVersionSyncView extends EasCommand { loggedIn: { graphqlClient }, getDynamicPrivateProjectConfigAsync, projectDir, + vcsClient, } = await this.getContextAsync(BuildVersionSyncView, { nonInteractive: true, }); @@ -97,6 +101,7 @@ export default class BuildVersionSyncView extends EasCommand { exp, buildProfile: profileInfo.profile, platform: profileInfo.platform, + vcsClient, }); const remoteVersions = await AppVersionQuery.latestVersionAsync( graphqlClient, @@ -104,7 +109,7 @@ export default class BuildVersionSyncView extends EasCommand { toAppPlatform(profileInfo.platform), applicationIdentifier ); - const workflow = await resolveWorkflowAsync(projectDir, profileInfo.platform); + const workflow = await resolveWorkflowAsync(projectDir, profileInfo.platform, vcsClient); if (!remoteVersions?.buildVersion) { Log.warn( `Skipping versions sync for ${platformDisplayName}. There are no versions configured on Expo servers, use "eas build:version:set" or run a build to initialize it.` @@ -128,6 +133,7 @@ export default class BuildVersionSyncView extends EasCommand { profile: profileInfo.profile as BuildProfile, workflow, buildVersion: remoteVersions.buildVersion, + vcsClient, }); } else { this.syncIosAsync({ @@ -136,6 +142,7 @@ export default class BuildVersionSyncView extends EasCommand { profile: profileInfo.profile as BuildProfile, workflow, buildVersion: remoteVersions.buildVersion, + vcsClient, }); } Log.withTick( @@ -152,9 +159,10 @@ export default class BuildVersionSyncView extends EasCommand { exp, profile, buildVersion, + vcsClient, }: SyncContext): Promise { const xcodeBuildContext = await resolveXcodeBuildContextAsync( - { exp, projectDir, nonInteractive: false }, + { exp, projectDir, nonInteractive: false, vcsClient }, profile ); const targets = await resolveTargetsAsync({ @@ -162,6 +170,7 @@ export default class BuildVersionSyncView extends EasCommand { exp, xcodeBuildContext, env: profile.env, + vcsClient, }); if (!isValidBuildNumber(buildVersion)) { diff --git a/packages/eas-cli/src/commands/build/view.ts b/packages/eas-cli/src/commands/build/view.ts index 8188da1299..05d7b2fe2a 100644 --- a/packages/eas-cli/src/commands/build/view.ts +++ b/packages/eas-cli/src/commands/build/view.ts @@ -20,6 +20,7 @@ export default class BuildView extends EasCommand { static override contextDefinition = { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { diff --git a/packages/eas-cli/src/commands/credentials.ts b/packages/eas-cli/src/commands/credentials.ts index 031b1a068d..56ac736e5f 100644 --- a/packages/eas-cli/src/commands/credentials.ts +++ b/packages/eas-cli/src/commands/credentials.ts @@ -15,6 +15,7 @@ export default class Credentials extends EasCommand { ...this.ContextOptions.OptionalProjectConfig, ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.Analytics, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -24,6 +25,7 @@ export default class Credentials extends EasCommand { privateProjectConfig, getDynamicPrivateProjectConfigAsync, analytics, + vcsClient, } = await this.getContextAsync(Credentials, { nonInteractive: false, }); @@ -31,6 +33,7 @@ export default class Credentials extends EasCommand { await new SelectPlatform( actor, graphqlClient, + vcsClient, analytics, privateProjectConfig ?? null, getDynamicPrivateProjectConfigAsync, diff --git a/packages/eas-cli/src/commands/diagnostics.ts b/packages/eas-cli/src/commands/diagnostics.ts index 097bb332c2..dfb943d9c7 100644 --- a/packages/eas-cli/src/commands/diagnostics.ts +++ b/packages/eas-cli/src/commands/diagnostics.ts @@ -5,16 +5,18 @@ import EasCommand from '../commandUtils/EasCommand'; import Log from '../log'; import { resolveWorkflowAsync } from '../project/workflow'; import { easCliVersion } from '../utils/easCli'; +import { Client } from '../vcs/vcs'; export default class Diagnostics extends EasCommand { static override description = 'display environment info'; static override contextDefinition = { ...this.ContextOptions.ProjectDir, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { - const { projectDir } = await this.getContextAsync(Diagnostics, { + const { projectDir, vcsClient } = await this.getContextAsync(Diagnostics, { nonInteractive: true, }); @@ -43,13 +45,13 @@ export default class Diagnostics extends EasCommand { ); Log.log(info.trimEnd()); - await this.printWorkflowAsync(projectDir); + await this.printWorkflowAsync(projectDir, vcsClient); Log.newLine(); } - private async printWorkflowAsync(projectDir: string): Promise { - const androidWorkflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID); - const iosWorkflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + private async printWorkflowAsync(projectDir: string, vcsClient: Client): Promise { + const androidWorkflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient); + const iosWorkflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); if (androidWorkflow === iosWorkflow) { Log.log(` Project workflow: ${androidWorkflow}`); diff --git a/packages/eas-cli/src/commands/metadata/pull.ts b/packages/eas-cli/src/commands/metadata/pull.ts index bac1bccbf4..37719ce0f2 100644 --- a/packages/eas-cli/src/commands/metadata/pull.ts +++ b/packages/eas-cli/src/commands/metadata/pull.ts @@ -27,6 +27,7 @@ export default class MetadataPull extends EasCommand { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.LoggedIn, ...this.ContextOptions.Analytics, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -37,12 +38,13 @@ export default class MetadataPull extends EasCommand { loggedIn: { actor, graphqlClient }, privateProjectConfig: { exp, projectId, projectDir }, analytics, + vcsClient, } = await this.getContextAsync(MetadataPull, { nonInteractive: false, }); // this command is interactive (all nonInteractive flags passed to utility functions are false) - await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false }); + await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false, vcsClient }); const submitProfiles = await getProfilesAsync({ type: 'submit', @@ -64,6 +66,7 @@ export default class MetadataPull extends EasCommand { graphqlClient, analytics, nonInteractive: false, + vcsClient, }); try { diff --git a/packages/eas-cli/src/commands/metadata/push.ts b/packages/eas-cli/src/commands/metadata/push.ts index 69aa0ef3a8..18306a9799 100644 --- a/packages/eas-cli/src/commands/metadata/push.ts +++ b/packages/eas-cli/src/commands/metadata/push.ts @@ -25,6 +25,7 @@ export default class MetadataPush extends EasCommand { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.LoggedIn, ...this.ContextOptions.Analytics, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -35,12 +36,13 @@ export default class MetadataPush extends EasCommand { loggedIn: { actor, graphqlClient }, privateProjectConfig: { exp, projectId, projectDir }, analytics, + vcsClient, } = await this.getContextAsync(MetadataPush, { nonInteractive: false, }); // this command is interactive (all nonInteractive flags passed to utility functions are false) - await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false }); + await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false, vcsClient }); const submitProfiles = await getProfilesAsync({ type: 'submit', @@ -62,6 +64,7 @@ export default class MetadataPush extends EasCommand { graphqlClient, analytics, nonInteractive: false, + vcsClient, }); try { diff --git a/packages/eas-cli/src/commands/submit.ts b/packages/eas-cli/src/commands/submit.ts index 5c0df81c67..07b3fbc46d 100644 --- a/packages/eas-cli/src/commands/submit.ts +++ b/packages/eas-cli/src/commands/submit.ts @@ -94,6 +94,7 @@ export default class Submit extends EasCommand { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.ProjectDir, ...this.ContextOptions.Analytics, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -102,6 +103,7 @@ export default class Submit extends EasCommand { loggedIn: { actor, graphqlClient }, privateProjectConfig: { exp, projectId, projectDir }, analytics, + vcsClient, } = await this.getContextAsync(Submit, { nonInteractive: false, }); @@ -135,6 +137,7 @@ export default class Submit extends EasCommand { analytics, exp, projectId, + vcsClient, }); if (submissionProfiles.length > 1) { diff --git a/packages/eas-cli/src/commands/update/__tests__/configure.test.ts b/packages/eas-cli/src/commands/update/__tests__/configure.test.ts index 784c7f12cc..a93a60f6a0 100644 --- a/packages/eas-cli/src/commands/update/__tests__/configure.test.ts +++ b/packages/eas-cli/src/commands/update/__tests__/configure.test.ts @@ -3,10 +3,12 @@ import { instance, mock } from 'ts-mockito'; import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient'; import { ensureEASUpdateIsConfiguredAsync } from '../../../update/configure'; +import { Client } from '../../../vcs/vcs'; describe(ensureEASUpdateIsConfiguredAsync, () => { it('errors with "useClassicUpdates" set and no app.json', async () => { const graphqlClient = instance(mock({})); + const vcsClient = instance(mock({})); const exp: ExpoConfig = { name: 'test', slug: 'test', @@ -19,6 +21,7 @@ describe(ensureEASUpdateIsConfiguredAsync, () => { projectId: 'test', projectDir: '/tmp/test', platform: null, + vcsClient, }); }).rejects.toThrow( `Your app config sets "updates.useClassicUpdates" but EAS Update does not support classic updates. Remove "useClassicUpdates" from your app config and run this command again.` diff --git a/packages/eas-cli/src/commands/update/configure.ts b/packages/eas-cli/src/commands/update/configure.ts index 68902ec3ea..5f153e432d 100644 --- a/packages/eas-cli/src/commands/update/configure.ts +++ b/packages/eas-cli/src/commands/update/configure.ts @@ -10,7 +10,6 @@ import { ensureEASUpdateIsConfiguredAsync, ensureEASUpdateIsConfiguredInEasJsonAsync, } from '../../update/configure'; -import { getVcsClient } from '../../vcs'; export default class UpdateConfigure extends EasCommand { static override description = 'configure the project to support EAS Update'; @@ -28,6 +27,7 @@ export default class UpdateConfigure extends EasCommand { static override contextDefinition = { ...this.ContextOptions.ProjectConfig, ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -36,6 +36,7 @@ export default class UpdateConfigure extends EasCommand { const { privateProjectConfig: { projectId, exp, projectDir }, loggedIn: { graphqlClient }, + vcsClient, } = await this.getContextAsync(UpdateConfigure, { nonInteractive: flags['non-interactive'], }); @@ -44,13 +45,14 @@ export default class UpdateConfigure extends EasCommand { '💡 The following process will configure your project to use EAS Update. These changes only apply to your local project files and you can safely revert them at any time.' ); - await getVcsClient().ensureRepoExistsAsync(); + await vcsClient.ensureRepoExistsAsync(); await ensureEASUpdateIsConfiguredAsync(graphqlClient, { exp, projectId, projectDir, platform, + vcsClient, }); await ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir); diff --git a/packages/eas-cli/src/commands/update/index.ts b/packages/eas-cli/src/commands/update/index.ts index f4b1f6cf52..780f24f141 100644 --- a/packages/eas-cli/src/commands/update/index.ts +++ b/packages/eas-cli/src/commands/update/index.ts @@ -52,7 +52,6 @@ import uniqBy from '../../utils/expodash/uniqBy'; import formatFields from '../../utils/formatFields'; import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json'; import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService'; -import { getVcsClient } from '../../vcs'; type RawUpdateFlags = { auto: boolean; @@ -155,6 +154,7 @@ export default class UpdatePublish extends EasCommand { static override contextDefinition = { ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -179,6 +179,7 @@ export default class UpdatePublish extends EasCommand { getDynamicPublicProjectConfigAsync, getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, + vcsClient, } = await this.getContextAsync(UpdatePublish, { nonInteractive, }); @@ -200,6 +201,7 @@ export default class UpdatePublish extends EasCommand { platform: getRequestedPlatform(platformFlag), projectDir, projectId, + vcsClient, }); const { exp } = await getDynamicPublicProjectConfigAsync(); @@ -208,6 +210,7 @@ export default class UpdatePublish extends EasCommand { const branchName = await getBranchNameForCommandAsync({ graphqlClient, + vcsClient, projectId, channelNameArg, branchNameArg, @@ -216,7 +219,7 @@ export default class UpdatePublish extends EasCommand { paginatedQueryOptions, }); - const updateMessage = await getUpdateMessageForCommandAsync({ + const updateMessage = await getUpdateMessageForCommandAsync(vcsClient, { updateMessageArg, autoFlag, nonInteractive, @@ -359,7 +362,12 @@ export default class UpdatePublish extends EasCommand { throw e; } - const runtimeVersions = await getRuntimeVersionObjectAsync(exp, realizedPlatforms, projectDir); + const runtimeVersions = await getRuntimeVersionObjectAsync( + exp, + realizedPlatforms, + projectDir, + vcsClient + ); const runtimeToPlatformMapping = getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions); @@ -378,8 +386,6 @@ export default class UpdatePublish extends EasCommand { ); } - const vcsClient = getVcsClient(); - const gitCommitHash = await vcsClient.getCommitHashAsync(); const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync(); diff --git a/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts b/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts index 7795afe7c3..9d4906d2b0 100644 --- a/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts +++ b/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts @@ -43,7 +43,6 @@ import uniqBy from '../../utils/expodash/uniqBy'; import formatFields from '../../utils/formatFields'; import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json'; import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService'; -import { getVcsClient } from '../../vcs'; type RawUpdateFlags = { auto: boolean; @@ -109,6 +108,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand { static override contextDefinition = { ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.LoggedIn, + ...this.ContextOptions.Vcs, }; async runAsync(): Promise { @@ -129,6 +129,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand { getDynamicPublicProjectConfigAsync, getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, + vcsClient, } = await this.getContextAsync(UpdateRollBackToEmbedded, { nonInteractive, }); @@ -150,6 +151,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand { platform: getRequestedPlatform(platformFlag), projectDir, projectId, + vcsClient, }); const { exp } = await getDynamicPublicProjectConfigAsync(); @@ -158,6 +160,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand { const branchName = await getBranchNameForCommandAsync({ graphqlClient, + vcsClient, projectId, channelNameArg, branchNameArg, @@ -166,7 +169,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand { paginatedQueryOptions, }); - const updateMessage = await getUpdateMessageForCommandAsync({ + const updateMessage = await getUpdateMessageForCommandAsync(vcsClient, { updateMessageArg, autoFlag, nonInteractive, @@ -190,12 +193,15 @@ export default class UpdateRollBackToEmbedded extends EasCommand { Log.withTick(`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`); - const vcsClient = getVcsClient(); - const gitCommitHash = await vcsClient.getCommitHashAsync(); const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync(); - const runtimeVersions = await getRuntimeVersionObjectAsync(exp, realizedPlatforms, projectDir); + const runtimeVersions = await getRuntimeVersionObjectAsync( + exp, + realizedPlatforms, + projectDir, + vcsClient + ); let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups']; const publishSpinner = ora('Publishing...').start(); diff --git a/packages/eas-cli/src/credentials/android/actions/BuildCredentialsUtils.ts b/packages/eas-cli/src/credentials/android/actions/BuildCredentialsUtils.ts index ff075565ae..71a8632c38 100644 --- a/packages/eas-cli/src/credentials/android/actions/BuildCredentialsUtils.ts +++ b/packages/eas-cli/src/credentials/android/actions/BuildCredentialsUtils.ts @@ -109,6 +109,7 @@ export async function getAppLookupParamsFromContextAsync( const androidApplicationIdentifier = await getApplicationIdAsync( ctx.projectDir, ctx.exp, + ctx.vcsClient, gradleContext ); if (!androidApplicationIdentifier) { diff --git a/packages/eas-cli/src/credentials/context.ts b/packages/eas-cli/src/credentials/context.ts index b0be625c3b..43a775b8c9 100644 --- a/packages/eas-cli/src/credentials/context.ts +++ b/packages/eas-cli/src/credentials/context.ts @@ -9,6 +9,7 @@ import Log from '../log'; import { getPrivateExpoConfig } from '../project/expoConfig'; import { confirmAsync } from '../prompts'; import { Actor } from '../user/User'; +import { Client } from '../vcs/vcs'; import * as AndroidGraphqlClient from './android/api/GraphqlClient'; import * as IosGraphqlClient from './ios/api/GraphqlClient'; import AppStoreApi from './ios/appstore/AppStoreApi'; @@ -28,6 +29,7 @@ export class CredentialsContext { public readonly user: Actor; public readonly graphqlClient: ExpoGraphqlClient; public readonly analytics: Analytics; + public readonly vcsClient: Client; public readonly easJsonCliConfig?: EasJson['cli']; private shouldAskAuthenticateAppStore: boolean = true; @@ -44,6 +46,7 @@ export class CredentialsContext { user: Actor; graphqlClient: ExpoGraphqlClient; analytics: Analytics; + vcsClient: Client; env?: Env; } ) { @@ -52,6 +55,7 @@ export class CredentialsContext { this.user = options.user; this.graphqlClient = options.graphqlClient; this.analytics = options.analytics; + this.vcsClient = options.vcsClient; this.nonInteractive = options.nonInteractive ?? false; this.projectInfo = options.projectInfo; } diff --git a/packages/eas-cli/src/credentials/credentialsJson/update.ts b/packages/eas-cli/src/credentials/credentialsJson/update.ts index 40f71d29a6..68c0b73779 100644 --- a/packages/eas-cli/src/credentials/credentialsJson/update.ts +++ b/packages/eas-cli/src/credentials/credentialsJson/update.ts @@ -6,8 +6,8 @@ import { AndroidAppBuildCredentialsFragment, IosDistributionType } from '../../g import Log from '../../log'; import { findApplicationTarget, findTargetByName } from '../../project/ios/target'; import zipObject from '../../utils/expodash/zipObject'; -import { getVcsClient } from '../../vcs'; import GitClient from '../../vcs/clients/git'; +import { Client } from '../../vcs/vcs'; import { CredentialsContext } from '../context'; import { App, Target, TargetCredentials } from '../ios/types'; import { readRawAsync } from './read'; @@ -38,7 +38,7 @@ export async function updateAndroidCredentialsAsync( rawCredentialsJson?.android?.keystore?.keystorePath ?? 'credentials/android/keystore.jks'; Log.log(`Writing Keystore to ${keystorePath}`); await updateFileAsync(ctx.projectDir, keystorePath, keystore.keystore); - const shouldWarnKeystore = await isFileUntrackedAsync(keystorePath); + const shouldWarnKeystore = await isFileUntrackedAsync(keystorePath, ctx.vcsClient); const androidCredentials: Partial = { keystore: { @@ -52,7 +52,7 @@ export async function updateAndroidCredentialsAsync( await fs.writeJson(getCredentialsJsonPath(ctx.projectDir), rawCredentialsJson, { spaces: 2, }); - const shouldWarnCredentialsJson = await isFileUntrackedAsync('credentials.json'); + const shouldWarnCredentialsJson = await isFileUntrackedAsync('credentials.json', ctx.vcsClient); const newFilePaths = []; if (shouldWarnKeystore) { @@ -137,14 +137,14 @@ export async function updateIosCredentialsAsync( const newFilePaths = []; for (const [, targetCredentials] of Object.entries(iosCredentials)) { - if (await isFileUntrackedAsync(targetCredentials.distributionCertificate.path)) { + if (await isFileUntrackedAsync(targetCredentials.distributionCertificate.path, ctx.vcsClient)) { newFilePaths.push(targetCredentials.distributionCertificate.path); } - if (await isFileUntrackedAsync(targetCredentials.provisioningProfilePath)) { + if (await isFileUntrackedAsync(targetCredentials.provisioningProfilePath, ctx.vcsClient)) { newFilePaths.push(targetCredentials.provisioningProfilePath); } } - if (await isFileUntrackedAsync('credentials.json')) { + if (await isFileUntrackedAsync('credentials.json', ctx.vcsClient)) { newFilePaths.push('credentials.json'); } displayUntrackedFilesWarning(newFilePaths); @@ -296,8 +296,7 @@ async function updateFileAsync( } } -async function isFileUntrackedAsync(path: string): Promise { - const vcsClient = getVcsClient(); +async function isFileUntrackedAsync(path: string, vcsClient: Client): Promise { if (vcsClient instanceof GitClient) { return await vcsClient.isFileUntrackedAsync(path); } diff --git a/packages/eas-cli/src/credentials/ios/actions/__tests__/SetUpAdhocProvisioningProfile-test.ts b/packages/eas-cli/src/credentials/ios/actions/__tests__/SetUpAdhocProvisioningProfile-test.ts index 51e2d5eca0..d81e5bef55 100644 --- a/packages/eas-cli/src/credentials/ios/actions/__tests__/SetUpAdhocProvisioningProfile-test.ts +++ b/packages/eas-cli/src/credentials/ios/actions/__tests__/SetUpAdhocProvisioningProfile-test.ts @@ -15,6 +15,7 @@ import { import Log from '../../../../log'; import { getApplePlatformFromTarget } from '../../../../project/ios/target'; import { Actor } from '../../../../user/User'; +import { Client } from '../../../../vcs/vcs'; import { CredentialsContext, CredentialsContextProjectInfo } from '../../../context'; import { ProvisioningProfile } from '../../appstore/Credentials.types'; import { ApplePlatform } from '../../appstore/constants'; @@ -118,6 +119,7 @@ function setUpTest(): { ctx: CredentialsContext; distCert: AppleDistributionCert graphqlClient: ExpoGraphqlClient; analytics: Analytics; env?: Env; + vcsClient: Client; } ) ); diff --git a/packages/eas-cli/src/credentials/manager/HelperActions.ts b/packages/eas-cli/src/credentials/manager/HelperActions.ts index 92defe0190..808ff6dd2d 100644 --- a/packages/eas-cli/src/credentials/manager/HelperActions.ts +++ b/packages/eas-cli/src/credentials/manager/HelperActions.ts @@ -4,12 +4,14 @@ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/creat import Log from '../../log'; import { pressAnyKeyToContinueAsync } from '../../prompts'; import { Actor } from '../../user/User'; +import { Client } from '../../vcs/vcs'; import { CredentialsContext, CredentialsContextProjectInfo } from '../context'; export interface Action { actor: Actor; graphqlClient: ExpoGraphqlClient; analytics: Analytics; + vcsClient: Client; projectInfo: CredentialsContextProjectInfo | null; getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn; runAsync(ctx: CredentialsContext): Promise; diff --git a/packages/eas-cli/src/credentials/manager/ManageAndroid.ts b/packages/eas-cli/src/credentials/manager/ManageAndroid.ts index 4115b52f8a..d283ba1c4e 100644 --- a/packages/eas-cli/src/credentials/manager/ManageAndroid.ts +++ b/packages/eas-cli/src/credentials/manager/ManageAndroid.ts @@ -65,6 +65,7 @@ export class ManageAndroid { analytics: this.callingAction.analytics, env: buildProfile?.env, nonInteractive: false, + vcsClient: this.callingAction.vcsClient, }); let gradleContext; @@ -153,7 +154,7 @@ export class ManageAndroid { buildProfile: BuildProfile ): Promise { assert(ctx.hasProjectContext, 'createProjectContextAsync: must have project context.'); - return await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile); + return await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile, ctx.vcsClient); } private async runProjectSpecificActionAsync( diff --git a/packages/eas-cli/src/credentials/manager/ManageIos.ts b/packages/eas-cli/src/credentials/manager/ManageIos.ts index 7667ab0b6e..cf084c55b9 100644 --- a/packages/eas-cli/src/credentials/manager/ManageIos.ts +++ b/packages/eas-cli/src/credentials/manager/ManageIos.ts @@ -78,6 +78,7 @@ export class ManageIos { analytics: this.callingAction.analytics, env: buildProfile?.env, nonInteractive: false, + vcsClient: this.callingAction.vcsClient, }); const buildCredentialsActions = getBuildCredentialsActions(ctx); const pushKeyActions = getPushKeyActions(ctx); @@ -194,6 +195,7 @@ export class ManageIos { projectDir: ctx.projectDir, nonInteractive: ctx.nonInteractive, exp: ctx.exp, + vcsClient: ctx.vcsClient, }, buildProfile ); @@ -202,6 +204,7 @@ export class ManageIos { projectDir: ctx.projectDir, xcodeBuildContext, env: buildProfile.env, + vcsClient: ctx.vcsClient, }); return { app, diff --git a/packages/eas-cli/src/credentials/manager/SelectPlatform.ts b/packages/eas-cli/src/credentials/manager/SelectPlatform.ts index c00a3d283f..33198f5096 100644 --- a/packages/eas-cli/src/credentials/manager/SelectPlatform.ts +++ b/packages/eas-cli/src/credentials/manager/SelectPlatform.ts @@ -3,6 +3,7 @@ import { DynamicConfigContextFn } from '../../commandUtils/context/DynamicProjec import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient'; import { selectPlatformAsync } from '../../platform'; import { Actor } from '../../user/User'; +import { Client } from '../../vcs/vcs'; import { CredentialsContextProjectInfo } from '../context'; import { ManageAndroid } from './ManageAndroid'; import { ManageIos } from './ManageIos'; @@ -11,6 +12,7 @@ export class SelectPlatform { constructor( public readonly actor: Actor, public readonly graphqlClient: ExpoGraphqlClient, + public readonly vcsClient: Client, public readonly analytics: Analytics, public readonly projectInfo: CredentialsContextProjectInfo | null, public readonly getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn, diff --git a/packages/eas-cli/src/credentials/manager/__tests__/ManageAndroid-test.ts b/packages/eas-cli/src/credentials/manager/__tests__/ManageAndroid-test.ts index f4ae06ed36..e47f41623f 100644 --- a/packages/eas-cli/src/credentials/manager/__tests__/ManageAndroid-test.ts +++ b/packages/eas-cli/src/credentials/manager/__tests__/ManageAndroid-test.ts @@ -7,6 +7,7 @@ import { learnMore } from '../../../log'; import { getProjectConfigDescription } from '../../../project/projectUtils'; import { pressAnyKeyToContinueAsync } from '../../../prompts'; import { Actor } from '../../../user/User'; +import { Client } from '../../../vcs/vcs'; import { getAppLookupParamsFromContextAsync } from '../../android/actions/BuildCredentialsUtils'; import { CredentialsContextProjectInfo } from '../../context'; import { AndroidPackageNotDefinedError } from '../../errors'; @@ -30,6 +31,7 @@ describe('runAsync', () => { actor: {} as Actor, graphqlClient: {} as ExpoGraphqlClient, analytics: {} as Analytics, + vcsClient: {} as Client, getDynamicPrivateProjectConfigAsync: jest .fn() .mockResolvedValue({ exp: {}, projectId: '' }), diff --git a/packages/eas-cli/src/metadata/auth.ts b/packages/eas-cli/src/metadata/auth.ts index b10d7be510..d41508be00 100644 --- a/packages/eas-cli/src/metadata/auth.ts +++ b/packages/eas-cli/src/metadata/auth.ts @@ -6,6 +6,7 @@ import assert from 'assert'; import { CredentialsContext } from '../credentials/context'; import { getRequestContext } from '../credentials/ios/appstore/authenticate'; import { getBundleIdentifierAsync } from '../project/ios/bundleIdentifier'; +import { Client } from '../vcs/vcs'; export type MetadataAppStoreAuthentication = { /** The root entity of the App store */ @@ -21,13 +22,14 @@ export type MetadataAppStoreAuthentication = { async function resolveAppStoreBundleIdentifierAsync( projectDir: string, profile: SubmitProfile, - exp: ExpoConfig + exp: ExpoConfig, + vcsClient: Client ): Promise { if ('bundleIdentifier' in profile) { - return profile.bundleIdentifier ?? (await getBundleIdentifierAsync(projectDir, exp)); + return profile.bundleIdentifier ?? (await getBundleIdentifierAsync(projectDir, exp, vcsClient)); } - return await getBundleIdentifierAsync(projectDir, exp); + return await getBundleIdentifierAsync(projectDir, exp, vcsClient); } /** @@ -45,7 +47,12 @@ export async function getAppStoreAuthAsync({ exp: ExpoConfig; credentialsCtx: CredentialsContext; }): Promise { - const bundleId = await resolveAppStoreBundleIdentifierAsync(projectDir, profile, exp); + const bundleId = await resolveAppStoreBundleIdentifierAsync( + projectDir, + profile, + exp, + credentialsCtx.vcsClient + ); const authCtx = await credentialsCtx.appStore.ensureAuthenticatedAsync(); assert(authCtx.authState, 'Failed to authenticate with App Store Connect'); diff --git a/packages/eas-cli/src/project/__tests__/workflow-test.ts b/packages/eas-cli/src/project/__tests__/workflow-test.ts index ec70813a60..afdf8b4c7a 100644 --- a/packages/eas-cli/src/project/__tests__/workflow-test.ts +++ b/packages/eas-cli/src/project/__tests__/workflow-test.ts @@ -1,11 +1,13 @@ import { Platform, Workflow } from '@expo/eas-build-job'; import { vol } from 'memfs'; +import { getVcsClient } from '../../vcs'; import { resolveWorkflowAsync, resolveWorkflowPerPlatformAsync } from '../workflow'; jest.mock('fs'); const projectDir = '/app'; +const vcsClient = getVcsClient(); describe(resolveWorkflowAsync, () => { beforeEach(() => { @@ -21,10 +23,12 @@ describe(resolveWorkflowAsync, () => { projectDir ); - await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID)).resolves.toBe( + await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient)).resolves.toBe( + Workflow.GENERIC + ); + await expect(resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient)).resolves.toBe( Workflow.GENERIC ); - await expect(resolveWorkflowAsync(projectDir, Platform.IOS)).resolves.toBe(Workflow.GENERIC); }); test('bare workflow for single platform', async () => { @@ -35,10 +39,12 @@ describe(resolveWorkflowAsync, () => { projectDir ); - await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID)).resolves.toBe( + await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient)).resolves.toBe( Workflow.MANAGED ); - await expect(resolveWorkflowAsync(projectDir, Platform.IOS)).resolves.toBe(Workflow.GENERIC); + await expect(resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient)).resolves.toBe( + Workflow.GENERIC + ); }); test('android/ios directories are ignored', async () => { @@ -51,10 +57,12 @@ describe(resolveWorkflowAsync, () => { projectDir ); - await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID)).resolves.toBe( + await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient)).resolves.toBe( + Workflow.MANAGED + ); + await expect(resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient)).resolves.toBe( Workflow.MANAGED ); - await expect(resolveWorkflowAsync(projectDir, Platform.IOS)).resolves.toBe(Workflow.MANAGED); }); }); @@ -72,7 +80,7 @@ describe(resolveWorkflowPerPlatformAsync, () => { projectDir ); - await expect(resolveWorkflowPerPlatformAsync(projectDir)).resolves.toEqual({ + await expect(resolveWorkflowPerPlatformAsync(projectDir, vcsClient)).resolves.toEqual({ android: Workflow.GENERIC, ios: Workflow.GENERIC, }); diff --git a/packages/eas-cli/src/project/android/__tests__/applicationId-test.ts b/packages/eas-cli/src/project/android/__tests__/applicationId-test.ts index de053e508d..8efa325bfb 100644 --- a/packages/eas-cli/src/project/android/__tests__/applicationId-test.ts +++ b/packages/eas-cli/src/project/android/__tests__/applicationId-test.ts @@ -7,6 +7,7 @@ import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/cr import { jester, jester as mockJester } from '../../../credentials/__tests__/fixtures-constants'; import { AppQuery } from '../../../graphql/queries/AppQuery'; import { promptAsync } from '../../../prompts'; +import { getVcsClient } from '../../../vcs'; import { ensureApplicationIdIsDefinedForManagedProjectAsync, getApplicationIdAsync, @@ -17,6 +18,8 @@ jest.mock('../../../prompts'); jest.mock('../../../graphql/queries/AppQuery'); jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) })); +const vcsClient = getVcsClient(); + beforeEach(async () => { vol.reset(); @@ -44,7 +47,9 @@ describe(getApplicationIdAsync, () => { '/app' ); - const applicationId = await getApplicationIdAsync('/app', {} as any, { moduleName: 'app' }); + const applicationId = await getApplicationIdAsync('/app', {} as any, vcsClient, { + moduleName: 'app', + }); expect(applicationId).toBe('com.expo.notdominik'); }); @@ -56,9 +61,9 @@ describe(getApplicationIdAsync, () => { }, '/app' ); - await expect(getApplicationIdAsync('/app', {} as any, undefined)).rejects.toThrowError( - /Failed to find 'build.gradle' / - ); + await expect( + getApplicationIdAsync('/app', {} as any, vcsClient, undefined) + ).rejects.toThrowError(/Failed to find 'build.gradle' /); }); it('throws an error if the project does not have applicationId defined in build.gradle', async () => { @@ -71,7 +76,7 @@ describe(getApplicationIdAsync, () => { ); await expect( - getApplicationIdAsync('/app', {} as any, { moduleName: 'app' }) + getApplicationIdAsync('/app', {} as any, vcsClient, { moduleName: 'app' }) ).rejects.toThrowError(/Could not read applicationId/); }); }); @@ -83,6 +88,7 @@ describe(getApplicationIdAsync, () => { { android: { package: 'com.expo.notdominik' }, } as any, + vcsClient, { moduleName: 'app' } ); expect(applicationId).toBe('com.expo.notdominik'); @@ -90,15 +96,20 @@ describe(getApplicationIdAsync, () => { it('throws an error if Android package is not defined in app config', async () => { await expect( - getApplicationIdAsync('/app', {} as any, { moduleName: 'app' }) + getApplicationIdAsync('/app', {} as any, vcsClient, { moduleName: 'app' }) ).rejects.toThrowError(/Specify "android.package"/); }); it('throws an error if Android package in app config is invalid', async () => { await expect( - getApplicationIdAsync('/app', { android: { package: '1com.expo.notdominik' } } as any, { - moduleName: 'app', - }) + getApplicationIdAsync( + '/app', + { android: { package: '1com.expo.notdominik' } } as any, + vcsClient, + { + moduleName: 'app', + } + ) ).rejects.toThrowError(/Specify "android.package"/); }); }); @@ -121,6 +132,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => { projectDir: '/app', projectId: '', exp: {} as any, + vcsClient, }) ).rejects.toThrowError(/we can't update this file programmatically/); }); @@ -150,6 +162,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => { projectDir: '/app', projectId: '', exp: {} as any, + vcsClient, }) ).resolves.toBe('com.expo.notdominik'); expect(promptAsync).toHaveBeenCalled(); @@ -182,6 +195,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => { projectDir: '/app', projectId: '', exp: {} as any, + vcsClient, }) ).resolves.toBe('com.expo.notdominik'); const appJson = JSON.parse(await fs.readFile('/app/app.json', 'utf-8')); diff --git a/packages/eas-cli/src/project/android/__tests__/gradle-test.ts b/packages/eas-cli/src/project/android/__tests__/gradle-test.ts index 35a1beea5c..3314f13fc9 100644 --- a/packages/eas-cli/src/project/android/__tests__/gradle-test.ts +++ b/packages/eas-cli/src/project/android/__tests__/gradle-test.ts @@ -4,12 +4,15 @@ import os from 'os'; import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants'; import { promptAsync } from '../../../prompts'; +import { getVcsClient } from '../../../vcs'; import { resolveGradleBuildContextAsync } from '../gradle'; jest.mock('fs'); jest.mock('../../../prompts'); jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) })); +const vcsClient = getVcsClient(); + beforeEach(async () => { vol.reset(); // do not remove the following line @@ -36,7 +39,7 @@ describe(resolveGradleBuildContextAsync, () => { '/app' ); - const gradleContext = await resolveGradleBuildContextAsync('/app', {} as any); + const gradleContext = await resolveGradleBuildContextAsync('/app', {} as any, vcsClient); expect(gradleContext).toEqual({ moduleName: 'app' }); }); it('resolves to flavor', async () => { @@ -59,9 +62,13 @@ describe(resolveGradleBuildContextAsync, () => { '/app' ); - const gradleContext = await resolveGradleBuildContextAsync('/app', { - gradleCommand: ':app:buildAbcRelease', - } as any); + const gradleContext = await resolveGradleBuildContextAsync( + '/app', + { + gradleCommand: ':app:buildAbcRelease', + } as any, + vcsClient + ); expect(gradleContext).toEqual({ moduleName: 'app', flavor: 'abc' }); }); @@ -73,9 +80,13 @@ describe(resolveGradleBuildContextAsync, () => { }, '/app' ); - const gradleContext = await resolveGradleBuildContextAsync('/app', { - gradleCommand: ':app:buildAbcRelease', - } as any); + const gradleContext = await resolveGradleBuildContextAsync( + '/app', + { + gradleCommand: ':app:buildAbcRelease', + } as any, + vcsClient + ); expect(gradleContext).toEqual(undefined); }); it('returns undefined if flavor does not exist', async () => { @@ -93,16 +104,20 @@ describe(resolveGradleBuildContextAsync, () => { '/app' ); - const gradleContext = await resolveGradleBuildContextAsync('/app', { - gradleCommand: ':app:buildAbcRelease', - } as any); + const gradleContext = await resolveGradleBuildContextAsync( + '/app', + { + gradleCommand: ':app:buildAbcRelease', + } as any, + vcsClient + ); expect(gradleContext).toEqual(undefined); }); }); describe('managed projects', () => { it('resolves to { moduleName: app } for managed projects', async () => { - const gradleContext = await resolveGradleBuildContextAsync('/app', {} as any); + const gradleContext = await resolveGradleBuildContextAsync('/app', {} as any, {} as any); expect(gradleContext).toEqual({ moduleName: 'app' }); }); }); diff --git a/packages/eas-cli/src/project/android/applicationId.ts b/packages/eas-cli/src/project/android/applicationId.ts index ea1962bef6..53f1f1f931 100644 --- a/packages/eas-cli/src/project/android/applicationId.ts +++ b/packages/eas-cli/src/project/android/applicationId.ts @@ -15,6 +15,7 @@ import { getProjectConfigDescription, } from '../../project/projectUtils'; import { promptAsync } from '../../prompts'; +import { Client } from '../../vcs/vcs'; import { resolveWorkflowAsync } from '../workflow'; import { GradleBuildContext } from './gradle'; import * as gradleUtils from './gradleUtils'; @@ -26,17 +27,19 @@ export async function ensureApplicationIdIsDefinedForManagedProjectAsync({ projectDir, projectId, exp, + vcsClient, }: { graphqlClient: ExpoGraphqlClient; projectDir: string; projectId: string; exp: ExpoConfig; + vcsClient: Client; }): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID); + const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient); assert(workflow === Workflow.MANAGED, 'This function should be called only for managed projects'); try { - return await getApplicationIdAsync(projectDir, exp, { + return await getApplicationIdAsync(projectDir, exp, vcsClient, { moduleName: gradleUtils.DEFAULT_MODULE_NAME, }); } catch { @@ -95,9 +98,10 @@ export async function getApplicationIdFromBareAsync( export async function getApplicationIdAsync( projectDir: string, exp: ExpoConfig, + vcsClient: Client, gradleContext?: GradleBuildContext ): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID); + const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient); if (workflow === Workflow.GENERIC) { warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject(projectDir, exp); diff --git a/packages/eas-cli/src/project/android/gradle.ts b/packages/eas-cli/src/project/android/gradle.ts index d9720afa63..b5349923e5 100644 --- a/packages/eas-cli/src/project/android/gradle.ts +++ b/packages/eas-cli/src/project/android/gradle.ts @@ -3,6 +3,7 @@ import { BuildProfile } from '@expo/eas-json'; import Log from '../../log'; import { resolveWorkflowAsync } from '../../project/workflow'; +import { Client } from '../../vcs/vcs'; import * as gradleUtils from './gradleUtils'; export interface GradleBuildContext { @@ -12,9 +13,10 @@ export interface GradleBuildContext { export async function resolveGradleBuildContextAsync( projectDir: string, - buildProfile: BuildProfile + buildProfile: BuildProfile, + vcsClient: Client ): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID); + const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient); if (workflow === Workflow.GENERIC) { try { if (buildProfile.gradleCommand) { diff --git a/packages/eas-cli/src/project/applicationIdentifier.ts b/packages/eas-cli/src/project/applicationIdentifier.ts index 1cf3f3379b..729058f4b4 100644 --- a/packages/eas-cli/src/project/applicationIdentifier.ts +++ b/packages/eas-cli/src/project/applicationIdentifier.ts @@ -3,6 +3,7 @@ import { Platform, Workflow } from '@expo/eas-build-job'; import { BuildProfile } from '@expo/eas-json'; import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient'; +import { Client } from '../vcs/vcs'; import { ensureApplicationIdIsDefinedForManagedProjectAsync, getApplicationIdAsync, @@ -23,6 +24,7 @@ export async function getApplicationIdentifierAsync({ exp, buildProfile, platform, + vcsClient, }: { graphqlClient: ExpoGraphqlClient; projectDir: string; @@ -30,10 +32,11 @@ export async function getApplicationIdentifierAsync({ exp: ExpoConfig; buildProfile: BuildProfile; platform: Platform; + vcsClient: Client; }): Promise { if (platform === Platform.ANDROID) { const profile = buildProfile as BuildProfile; - const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID); + const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient); if (workflow === Workflow.MANAGED) { return await ensureApplicationIdIsDefinedForManagedProjectAsync({ @@ -41,13 +44,14 @@ export async function getApplicationIdentifierAsync({ projectDir, projectId, exp, + vcsClient, }); } - const gradleContext = await resolveGradleBuildContextAsync(projectDir, profile); - return await getApplicationIdAsync(projectDir, exp, gradleContext); + const gradleContext = await resolveGradleBuildContextAsync(projectDir, profile, vcsClient); + return await getApplicationIdAsync(projectDir, exp, vcsClient, gradleContext); } else { - const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); const profile = buildProfile as BuildProfile; if (workflow === Workflow.MANAGED) { return await ensureBundleIdentifierIsDefinedForManagedProjectAsync({ @@ -55,11 +59,12 @@ export async function getApplicationIdentifierAsync({ projectDir, projectId, exp, + vcsClient, }); } const xcodeBuildContext = await resolveXcodeBuildContextAsync( - { exp, projectDir, nonInteractive: false }, + { exp, projectDir, nonInteractive: false, vcsClient }, profile ); const targets = await resolveTargetsAsync({ @@ -67,9 +72,10 @@ export async function getApplicationIdentifierAsync({ exp, xcodeBuildContext, env: profile.env, + vcsClient, }); const applicationTarget = findApplicationTarget(targets); - return await getBundleIdentifierAsync(projectDir, exp, { + return await getBundleIdentifierAsync(projectDir, exp, vcsClient, { targetName: applicationTarget.targetName, buildConfiguration: applicationTarget.buildConfiguration, }); diff --git a/packages/eas-cli/src/project/ios/__tests__/bundleIdentifier-test.ts b/packages/eas-cli/src/project/ios/__tests__/bundleIdentifier-test.ts index 5b0da292c1..df15d300ad 100644 --- a/packages/eas-cli/src/project/ios/__tests__/bundleIdentifier-test.ts +++ b/packages/eas-cli/src/project/ios/__tests__/bundleIdentifier-test.ts @@ -8,6 +8,7 @@ import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/cr import { jester, jester as mockJester } from '../../../credentials/__tests__/fixtures-constants'; import { AppQuery } from '../../../graphql/queries/AppQuery'; import { promptAsync } from '../../../prompts'; +import { getVcsClient } from '../../../vcs'; import { ensureBundleIdentifierIsDefinedForManagedProjectAsync, getBundleIdentifierAsync, @@ -19,6 +20,8 @@ jest.mock('../../../prompts'); jest.mock('../../../graphql/queries/AppQuery'); jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) })); +const vcsClient = getVcsClient(); + beforeEach(async () => { vol.reset(); @@ -44,7 +47,7 @@ describe(getBundleIdentifierAsync, () => { '/app' ); - const bundleIdentifier = await getBundleIdentifierAsync('/app', {} as any); + const bundleIdentifier = await getBundleIdentifierAsync('/app', {} as any, vcsClient); expect(bundleIdentifier).toBe('org.name.testproject'); }); @@ -59,7 +62,7 @@ describe(getBundleIdentifierAsync, () => { '/app' ); - await expect(getBundleIdentifierAsync('/app', {} as any)).rejects.toThrowError( + await expect(getBundleIdentifierAsync('/app', {} as any, vcsClient)).rejects.toThrowError( /Could not read bundle identifier/ ); }); @@ -67,21 +70,25 @@ describe(getBundleIdentifierAsync, () => { describe('managed projects', () => { it('reads bundleIdentifier from app config', async () => { - const applicationId = await getBundleIdentifierAsync('/app', { - ios: { bundleIdentifier: 'com.expo.notdominik' }, - } as any); + const applicationId = await getBundleIdentifierAsync( + '/app', + { + ios: { bundleIdentifier: 'com.expo.notdominik' }, + } as any, + vcsClient + ); expect(applicationId).toBe('com.expo.notdominik'); }); it('throws an error if bundleIdentifier is not defined in app config', async () => { - await expect(getBundleIdentifierAsync('/app', {} as any)).rejects.toThrowError( + await expect(getBundleIdentifierAsync('/app', {} as any, vcsClient)).rejects.toThrowError( /Specify "ios.bundleIdentifier"/ ); }); it('throws an error if bundleIdentifier in app config is invalid', async () => { await expect( - getBundleIdentifierAsync('/app', { ios: { bundleIdentifier: '' } } as any) + getBundleIdentifierAsync('/app', { ios: { bundleIdentifier: '' } } as any, vcsClient) ).rejects.toThrowError(/Specify "ios.bundleIdentifier"/); }); }); @@ -103,6 +110,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => { projectDir: '/app', projectId: '1234', exp: {} as any, + vcsClient, }) ).rejects.toThrowError(/we can't update this file programmatically/); }); @@ -132,6 +140,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => { projectDir: '/app', projectId: '1234', exp: {} as any, + vcsClient, }) ).resolves.toBe('com.expo.notdominik'); expect(promptAsync).toHaveBeenCalled(); @@ -162,6 +171,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => { projectDir: '/app', projectId: '1234', exp: {} as any, + vcsClient, }) ).resolves.toBe('com.expo.notdominik'); const appJson = JSON.parse(await fs.readFile('/app/app.json', 'utf-8')); diff --git a/packages/eas-cli/src/project/ios/bundleIdentifier.ts b/packages/eas-cli/src/project/ios/bundleIdentifier.ts index c921215530..825561d82c 100644 --- a/packages/eas-cli/src/project/ios/bundleIdentifier.ts +++ b/packages/eas-cli/src/project/ios/bundleIdentifier.ts @@ -9,6 +9,7 @@ import { readAppJson } from '../../build/utils/appJson'; import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient'; import Log, { learnMore } from '../../log'; import { promptAsync } from '../../prompts'; +import { Client } from '../../vcs/vcs'; import { getOwnerAccountForProjectIdAsync, getProjectConfigDescription } from '../projectUtils'; import { resolveWorkflowAsync } from '../workflow'; @@ -19,17 +20,19 @@ export async function ensureBundleIdentifierIsDefinedForManagedProjectAsync({ projectDir, projectId, exp, + vcsClient, }: { graphqlClient: ExpoGraphqlClient; projectDir: string; projectId: string; exp: ExpoConfig; + vcsClient: Client; }): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); assert(workflow === Workflow.MANAGED, 'This function should be called only for managed projects'); try { - return await getBundleIdentifierAsync(projectDir, exp); + return await getBundleIdentifierAsync(projectDir, exp, vcsClient); } catch { return await configureBundleIdentifierAsync({ graphqlClient, @@ -49,9 +52,10 @@ export class AmbiguousBundleIdentifierError extends Error { export async function getBundleIdentifierAsync( projectDir: string, exp: ExpoConfig, + vcsClient: Client, xcodeContext?: { targetName?: string; buildConfiguration?: string } ): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); if (workflow === Workflow.GENERIC) { warnIfBundleIdentifierDefinedInAppConfigForBareWorkflowProject(projectDir, exp); diff --git a/packages/eas-cli/src/project/ios/entitlements.ts b/packages/eas-cli/src/project/ios/entitlements.ts index f29ac6efaa..8f1f669d2f 100644 --- a/packages/eas-cli/src/project/ios/entitlements.ts +++ b/packages/eas-cli/src/project/ios/entitlements.ts @@ -3,6 +3,7 @@ import { JSONObject } from '@expo/json-file'; import { getPrebuildConfigAsync } from '@expo/prebuild-config'; import { readPlistAsync } from '../../utils/plist'; +import { Client } from '../../vcs/vcs'; import { hasIgnoredIosProjectAsync } from '../workflow'; interface Target { @@ -11,7 +12,8 @@ interface Target { } export async function getManagedApplicationTargetEntitlementsAsync( projectDir: string, - env: Record + env: Record, + vcsClient: Client ): Promise { const originalProcessEnv: NodeJS.ProcessEnv = process.env; @@ -26,7 +28,7 @@ export async function getManagedApplicationTargetEntitlementsAsync( projectRoot: projectDir, platforms: ['ios'], introspect: true, - ignoreExistingNativeFiles: await hasIgnoredIosProjectAsync(projectDir), + ignoreExistingNativeFiles: await hasIgnoredIosProjectAsync(projectDir, vcsClient), }); return expWithMods.ios?.entitlements || {}; } finally { diff --git a/packages/eas-cli/src/project/ios/scheme.ts b/packages/eas-cli/src/project/ios/scheme.ts index 0327c212e9..f5b6e708e0 100644 --- a/packages/eas-cli/src/project/ios/scheme.ts +++ b/packages/eas-cli/src/project/ios/scheme.ts @@ -7,6 +7,7 @@ import chalk from 'chalk'; import Log from '../../log'; import { promptAsync } from '../../prompts'; import sortBy from '../../utils/expodash/sortBy'; +import { Client } from '../../vcs/vcs'; import { resolveWorkflowAsync } from '../workflow'; export interface XcodeBuildContext { @@ -19,10 +20,11 @@ export async function resolveXcodeBuildContextAsync( exp, projectDir, nonInteractive, - }: { exp: ExpoConfig; projectDir: string; nonInteractive: boolean }, + vcsClient, + }: { exp: ExpoConfig; projectDir: string; nonInteractive: boolean; vcsClient: Client }, buildProfile: BuildProfile ): Promise { - const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient); if (workflow === Workflow.GENERIC) { const buildScheme = buildProfile.scheme ?? diff --git a/packages/eas-cli/src/project/ios/target.ts b/packages/eas-cli/src/project/ios/target.ts index a1230364c3..df2371536a 100644 --- a/packages/eas-cli/src/project/ios/target.ts +++ b/packages/eas-cli/src/project/ios/target.ts @@ -7,6 +7,7 @@ import type { XCBuildConfiguration } from 'xcode'; import { ApplePlatform } from '../../credentials/ios/appstore/constants'; import { Target } from '../../credentials/ios/types'; +import { Client } from '../../vcs/vcs'; import { resolveWorkflowAsync } from '../workflow'; import { getBundleIdentifierAsync } from './bundleIdentifier'; import { @@ -27,6 +28,7 @@ interface ResolveTargetOptions { exp: ExpoConfig; env?: Record; xcodeBuildContext: XcodeBuildContext; + vcsClient: Client; } const AppExtensionsConfigSchema = Joi.array().items( @@ -43,16 +45,23 @@ export async function resolveManagedProjectTargetsAsync({ projectDir, xcodeBuildContext, env, + vcsClient, }: ResolveTargetOptions): Promise { const { buildScheme, buildConfiguration } = xcodeBuildContext; const applicationTargetName = buildScheme; - const applicationTargetBundleIdentifier = await getBundleIdentifierAsync(projectDir, exp, { - targetName: applicationTargetName, - buildConfiguration, - }); + const applicationTargetBundleIdentifier = await getBundleIdentifierAsync( + projectDir, + exp, + vcsClient, + { + targetName: applicationTargetName, + buildConfiguration, + } + ); const applicationTargetEntitlements = await getManagedApplicationTargetEntitlementsAsync( projectDir, - env ?? {} + env ?? {}, + vcsClient ); const appExtensions: UserDefinedTarget[] = exp.extra?.eas?.build?.experimental?.ios?.appExtensions ?? []; @@ -89,6 +98,7 @@ export async function resolveBareProjectTargetsAsync({ exp, projectDir, xcodeBuildContext, + vcsClient, }: ResolveTargetOptions): Promise { const { buildScheme, buildConfiguration } = xcodeBuildContext; const result: Target[] = []; @@ -98,7 +108,7 @@ export async function resolveBareProjectTargetsAsync({ projectDir, buildScheme ); - const bundleIdentifier = await getBundleIdentifierAsync(projectDir, exp, { + const bundleIdentifier = await getBundleIdentifierAsync(projectDir, exp, vcsClient, { targetName: applicationTarget.name, buildConfiguration, }); @@ -125,6 +135,7 @@ export async function resolveBareProjectTargetsAsync({ target: applicationTarget, bundleIdentifier, pbxProject, + vcsClient, }); if (dependencies.length > 0) { result.push(...dependencies); @@ -134,7 +145,7 @@ export async function resolveBareProjectTargetsAsync({ } export async function resolveTargetsAsync(opts: ResolveTargetOptions): Promise { - const workflow = await resolveWorkflowAsync(opts.projectDir, Platform.IOS); + const workflow = await resolveWorkflowAsync(opts.projectDir, Platform.IOS, opts.vcsClient); if (workflow === Workflow.GENERIC) { return await resolveBareProjectTargetsAsync(opts); } else if (workflow === Workflow.MANAGED) { @@ -151,6 +162,7 @@ async function resolveBareProjectDependenciesAsync({ target, bundleIdentifier, pbxProject, + vcsClient, }: { exp: ExpoConfig; projectDir: string; @@ -158,6 +170,7 @@ async function resolveBareProjectDependenciesAsync({ target: IOSConfig.Target.Target; bundleIdentifier: string; pbxProject: XcodeProject; + vcsClient: Client; }): Promise { const result: Target[] = []; @@ -166,10 +179,15 @@ async function resolveBareProjectDependenciesAsync({ if (!dependency.signable) { continue; } - const dependencyBundleIdentifier = await getBundleIdentifierAsync(projectDir, exp, { - targetName: dependency.name, - buildConfiguration, - }); + const dependencyBundleIdentifier = await getBundleIdentifierAsync( + projectDir, + exp, + vcsClient, + { + targetName: dependency.name, + buildConfiguration, + } + ); const entitlements = await getNativeTargetEntitlementsAsync(projectDir, { targetName: target.name, buildConfiguration, @@ -193,6 +211,7 @@ async function resolveBareProjectDependenciesAsync({ target: dependency, bundleIdentifier: dependencyBundleIdentifier, pbxProject, + vcsClient, }); if (dependencyDependencies.length > 0) { result.push(...dependencyDependencies); diff --git a/packages/eas-cli/src/project/publish.ts b/packages/eas-cli/src/project/publish.ts index 4c1cda8933..d8029b1e84 100644 --- a/packages/eas-cli/src/project/publish.ts +++ b/packages/eas-cli/src/project/publish.ts @@ -33,7 +33,7 @@ import { import chunk from '../utils/expodash/chunk'; import { truthy } from '../utils/expodash/filter'; import uniqBy from '../utils/expodash/uniqBy'; -import { getVcsClient } from '../vcs'; +import { Client } from '../vcs/vcs'; import { resolveWorkflowAsync } from './workflow'; export type ExpoCLIExportPlatformFlag = Platform | 'all'; @@ -545,6 +545,7 @@ export async function getBranchNameForCommandAsync({ autoFlag, nonInteractive, paginatedQueryOptions, + vcsClient, }: { graphqlClient: ExpoGraphqlClient; projectId: string; @@ -553,6 +554,7 @@ export async function getBranchNameForCommandAsync({ autoFlag: boolean; nonInteractive: boolean; paginatedQueryOptions: PaginatedQueryOptions; + vcsClient: Client; }): Promise { if (channelNameArg && branchNameArg) { throw new Error( @@ -569,7 +571,7 @@ export async function getBranchNameForCommandAsync({ } if (autoFlag) { - return await getDefaultBranchNameAsync(); + return await getDefaultBranchNameAsync(vcsClient); } else if (nonInteractive) { throw new Error('Must supply --channel, --branch or --auto when in non-interactive mode.'); } else { @@ -593,7 +595,7 @@ export async function getBranchNameForCommandAsync({ type: 'text', name: 'name', message: 'No branches found. Provide a branch name:', - initial: await getDefaultBranchNameAsync(), + initial: await getDefaultBranchNameAsync(vcsClient), validate: value => (value ? true : 'Branch name may not be empty.'), }); branchName = name; @@ -604,20 +606,23 @@ export async function getBranchNameForCommandAsync({ } } -export async function getUpdateMessageForCommandAsync({ - updateMessageArg, - autoFlag, - nonInteractive, - jsonFlag, -}: { - updateMessageArg: string | undefined; - autoFlag: boolean; - nonInteractive: boolean; - jsonFlag: boolean; -}): Promise { +export async function getUpdateMessageForCommandAsync( + vcsClient: Client, + { + updateMessageArg, + autoFlag, + nonInteractive, + jsonFlag, + }: { + updateMessageArg: string | undefined; + autoFlag: boolean; + nonInteractive: boolean; + jsonFlag: boolean; + } +): Promise { let updateMessage = updateMessageArg; if (!updateMessageArg && autoFlag) { - updateMessage = (await getVcsClient().getLastCommitMessageAsync())?.trim(); + updateMessage = (await vcsClient.getLastCommitMessageAsync())?.trim(); } if (!updateMessage) { @@ -633,7 +638,7 @@ export async function getUpdateMessageForCommandAsync({ type: 'text', name: 'updateMessageLocal', message: `Provide an update message:`, - initial: (await getVcsClient().getLastCommitMessageAsync())?.trim(), + initial: (await vcsClient.getLastCommitMessageAsync())?.trim(), validate: (value: any) => (value ? true : validationMessage), }); updateMessage = updateMessageLocal; @@ -672,7 +677,8 @@ export function getRequestedPlatform( export async function getRuntimeVersionObjectAsync( exp: ExpoConfig, platforms: Platform[], - projectDir: string + projectDir: string, + vcsClient: Client ): Promise<{ platform: string; runtimeVersion: string }[]> { for (const platform of platforms) { if (platform === 'web') { @@ -681,7 +687,7 @@ export async function getRuntimeVersionObjectAsync( const isPolicy = typeof (exp[platform]?.runtimeVersion ?? exp.runtimeVersion) === 'object'; if (isPolicy) { const isManaged = - (await resolveWorkflowAsync(projectDir, platform as EASBuildJobPlatform)) === + (await resolveWorkflowAsync(projectDir, platform as EASBuildJobPlatform, vcsClient)) === Workflow.MANAGED; if (!isManaged) { throw new Error( diff --git a/packages/eas-cli/src/project/workflow.ts b/packages/eas-cli/src/project/workflow.ts index 17a307388c..01ea529fa0 100644 --- a/packages/eas-cli/src/project/workflow.ts +++ b/packages/eas-cli/src/project/workflow.ts @@ -3,11 +3,12 @@ import { Platform, Workflow } from '@expo/eas-build-job'; import fs from 'fs-extra'; import path from 'path'; -import { getVcsClient } from '../vcs'; +import { Client } from '../vcs/vcs'; export async function resolveWorkflowAsync( projectDir: string, - platform: Platform + platform: Platform, + vcsClient: Client ): Promise { let platformWorkflowMarkers: string[]; try { @@ -22,7 +23,6 @@ export async function resolveWorkflowAsync( return Workflow.MANAGED; } - const vcsClient = getVcsClient(); const vcsRootPath = path.normalize(await vcsClient.getRootPathAsync()); for (const marker of platformWorkflowMarkers) { if ( @@ -36,17 +36,20 @@ export async function resolveWorkflowAsync( } export async function resolveWorkflowPerPlatformAsync( - projectDir: string + projectDir: string, + vcsClient: Client ): Promise> { const [android, ios] = await Promise.all([ - resolveWorkflowAsync(projectDir, Platform.ANDROID), - resolveWorkflowAsync(projectDir, Platform.IOS), + resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient), + resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient), ]); return { android, ios }; } -export async function hasIgnoredIosProjectAsync(projectDir: string): Promise { - const vcsClient = getVcsClient(); +export async function hasIgnoredIosProjectAsync( + projectDir: string, + vcsClient: Client +): Promise { const vcsRootPath = path.normalize(await vcsClient.getRootPathAsync()); try { diff --git a/packages/eas-cli/src/submit/android/AndroidSubmitCommand.ts b/packages/eas-cli/src/submit/android/AndroidSubmitCommand.ts index 2f92e4ff39..dbd26bbe98 100644 --- a/packages/eas-cli/src/submit/android/AndroidSubmitCommand.ts +++ b/packages/eas-cli/src/submit/android/AndroidSubmitCommand.ts @@ -58,7 +58,9 @@ export default class AndroidSubmitCommand { private async maybeGetAndroidPackageFromCurrentProjectAsync(): Promise> { try { - return result(await getApplicationIdAsync(this.ctx.projectDir, this.ctx.exp)); + return result( + await getApplicationIdAsync(this.ctx.projectDir, this.ctx.exp, this.ctx.vcsClient) + ); } catch (error: any) { if (error instanceof AmbiguousApplicationIdError) { Log.warn( diff --git a/packages/eas-cli/src/submit/android/__tests__/AndroidSubmitCommand-test.ts b/packages/eas-cli/src/submit/android/__tests__/AndroidSubmitCommand-test.ts index dbb6ab18f4..5a0d972da6 100644 --- a/packages/eas-cli/src/submit/android/__tests__/AndroidSubmitCommand-test.ts +++ b/packages/eas-cli/src/submit/android/__tests__/AndroidSubmitCommand-test.ts @@ -20,6 +20,7 @@ import { import { SubmissionMutation } from '../../../graphql/mutations/SubmissionMutation'; import { createTestProject } from '../../../project/__tests__/project-utils'; import { getOwnerAccountForProjectIdAsync } from '../../../project/projectUtils'; +import { getVcsClient } from '../../../vcs'; import { createSubmissionContextAsync } from '../../context'; import { getRecentBuildsForSubmissionAsync } from '../../utils/builds'; import AndroidSubmitCommand from '../AndroidSubmitCommand'; @@ -35,6 +36,8 @@ jest.mock('../../../credentials/android/api/graphql/queries/AndroidAppCredential jest.mock('../../utils/builds'); jest.mock('../../../project/projectUtils'); +const vcsClient = getVcsClient(); + describe(AndroidSubmitCommand, () => { const testProject = createTestProject(testProjectId, mockJester.accounts[0].name, { android: { @@ -96,6 +99,7 @@ describe(AndroidSubmitCommand, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const command = new AndroidSubmitCommand(ctx); await expect(command.runAsync()).rejects.toThrowError( @@ -128,6 +132,7 @@ describe(AndroidSubmitCommand, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const command = new AndroidSubmitCommand(ctx); @@ -168,6 +173,7 @@ describe(AndroidSubmitCommand, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const command = new AndroidSubmitCommand(ctx); @@ -211,6 +217,7 @@ describe(AndroidSubmitCommand, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const command = new AndroidSubmitCommand(ctx); await command.runAsync(); diff --git a/packages/eas-cli/src/submit/android/__tests__/ServiceAccountSource-test.ts b/packages/eas-cli/src/submit/android/__tests__/ServiceAccountSource-test.ts index fb0b5637a5..5af20d5f42 100644 --- a/packages/eas-cli/src/submit/android/__tests__/ServiceAccountSource-test.ts +++ b/packages/eas-cli/src/submit/android/__tests__/ServiceAccountSource-test.ts @@ -15,6 +15,7 @@ import { SetUpGoogleServiceAccountKey } from '../../../credentials/android/actio import { createTestProject } from '../../../project/__tests__/project-utils'; import { getOwnerAccountForProjectIdAsync } from '../../../project/projectUtils'; import { promptAsync } from '../../../prompts'; +import { getVcsClient } from '../../../vcs'; import { createSubmissionContextAsync } from '../../context'; import { ServiceAccountSource, @@ -44,6 +45,8 @@ const mockDetectableServiceAccountJson = JSON.stringify({ client_email: 'beep-boop@iam.gserviceaccount.com', }); +const vcsClient = getVcsClient(); + beforeAll(() => { vol.fromJSON({ '/google-service-account.json': mockDetectableServiceAccountJson, @@ -158,6 +161,7 @@ describe(getServiceAccountKeyResultAsync, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const source: ServiceAccountSource = { sourceType: ServiceAccountSourceType.path, @@ -199,6 +203,7 @@ describe(getServiceAccountKeyResultAsync, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const source: ServiceAccountSource = { sourceType: ServiceAccountSourceType.prompt, @@ -236,6 +241,7 @@ describe(getServiceAccountKeyResultAsync, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const serviceAccountResult = await getServiceAccountKeyResultAsync(ctx, { sourceType: ServiceAccountSourceType.credentialsService, diff --git a/packages/eas-cli/src/submit/context.ts b/packages/eas-cli/src/submit/context.ts index 1154c08a03..928ee17913 100644 --- a/packages/eas-cli/src/submit/context.ts +++ b/packages/eas-cli/src/submit/context.ts @@ -12,6 +12,7 @@ import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGr import { CredentialsContext } from '../credentials/context'; import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils'; import { Actor } from '../user/User'; +import { Client } from '../vcs/vcs'; export interface SubmissionContext { accountName: string; @@ -28,6 +29,7 @@ export interface SubmissionContext { user: Actor; graphqlClient: ExpoGraphqlClient; analytics: Analytics; + vcsClient: Client; applicationIdentifierOverride?: string; } @@ -52,6 +54,7 @@ export async function createSubmissionContextAsync(params: { analytics: Analytics; exp: ExpoConfig; projectId: string; + vcsClient: Client; }): Promise> { const { applicationIdentifier, @@ -62,6 +65,7 @@ export async function createSubmissionContextAsync(params: { projectId, graphqlClient, analytics, + vcsClient, } = params; const { env, ...rest } = params; const projectName = exp.slug; @@ -76,6 +80,7 @@ export async function createSubmissionContextAsync(params: { analytics, projectInfo: { exp, projectId }, nonInteractive, + vcsClient, }); } diff --git a/packages/eas-cli/src/submit/ios/AppProduce.ts b/packages/eas-cli/src/submit/ios/AppProduce.ts index bb9d315bb7..9ae53c6e54 100644 --- a/packages/eas-cli/src/submit/ios/AppProduce.ts +++ b/packages/eas-cli/src/submit/ios/AppProduce.ts @@ -37,7 +37,7 @@ export async function ensureAppStoreConnectAppExistsAsync( bundleIdentifier: ctx.applicationIdentifierOverride ?? ctx.profile.bundleIdentifier ?? - (await getBundleIdentifierAsync(ctx.projectDir, exp)), + (await getBundleIdentifierAsync(ctx.projectDir, exp, ctx.vcsClient)), appName: appName ?? exp.name ?? (await promptForAppNameAsync()), language: sanitizeLanguage(language), }; diff --git a/packages/eas-cli/src/submit/ios/AscApiKeySource.ts b/packages/eas-cli/src/submit/ios/AscApiKeySource.ts index ae8a514ff7..49a646f3ca 100644 --- a/packages/eas-cli/src/submit/ios/AscApiKeySource.ts +++ b/packages/eas-cli/src/submit/ios/AscApiKeySource.ts @@ -78,7 +78,7 @@ async function maybeGetIosBundleIdentifierAsync( ctx: SubmissionContext ): Promise { try { - return await getBundleIdentifierAsync(ctx.projectDir, ctx.exp); + return await getBundleIdentifierAsync(ctx.projectDir, ctx.exp, ctx.vcsClient); } catch (error: any) { if (error instanceof AmbiguousBundleIdentifierError) { Log.warn( diff --git a/packages/eas-cli/src/submit/ios/__tests__/AscApiKeySource-test.ts b/packages/eas-cli/src/submit/ios/__tests__/AscApiKeySource-test.ts index 0f27fa97ed..392327eb8f 100644 --- a/packages/eas-cli/src/submit/ios/__tests__/AscApiKeySource-test.ts +++ b/packages/eas-cli/src/submit/ios/__tests__/AscApiKeySource-test.ts @@ -17,6 +17,7 @@ import { AppQuery } from '../../../graphql/queries/AppQuery'; import { createTestProject } from '../../../project/__tests__/project-utils'; import { getBundleIdentifierAsync } from '../../../project/ios/bundleIdentifier'; import { promptAsync } from '../../../prompts'; +import { getVcsClient } from '../../../vcs'; import { SubmissionContext, createSubmissionContextAsync } from '../../context'; import { AscApiKeySource, @@ -41,6 +42,8 @@ const testProject = createTestProject(testProjectId, mockJester.accounts[0].name }); const projectId = uuidv4(); +const vcsClient = getVcsClient(); + async function getIosSubmissionContextAsync(): Promise> { const graphqlClient = instance(mock()); const analytics = instance(mock()); @@ -59,6 +62,7 @@ async function getIosSubmissionContextAsync(): Promise { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const source: AscApiKeySource = { sourceType: AscApiKeySourceType.credentialsService, diff --git a/packages/eas-cli/src/submit/ios/__tests__/IosSubmitCommand-test.ts b/packages/eas-cli/src/submit/ios/__tests__/IosSubmitCommand-test.ts index d65eb60e0f..8f15f4a61b 100644 --- a/packages/eas-cli/src/submit/ios/__tests__/IosSubmitCommand-test.ts +++ b/packages/eas-cli/src/submit/ios/__tests__/IosSubmitCommand-test.ts @@ -13,6 +13,7 @@ import { SubmissionArchiveSourceType } from '../../../graphql/generated'; import { SubmissionMutation } from '../../../graphql/mutations/SubmissionMutation'; import { createTestProject } from '../../../project/__tests__/project-utils'; import { getOwnerAccountForProjectIdAsync } from '../../../project/projectUtils'; +import { getVcsClient } from '../../../vcs'; import { createSubmissionContextAsync } from '../../context'; import IosSubmitCommand from '../IosSubmitCommand'; @@ -31,6 +32,8 @@ jest.mock('../../../user/actions', () => ({ })); jest.mock('../../../project/projectUtils'); +const vcsClient = getVcsClient(); + describe(IosSubmitCommand, () => { const testProject = createTestProject(testProjectId, mockJester.accounts[0].name, {}); @@ -74,6 +77,7 @@ describe(IosSubmitCommand, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const command = new IosSubmitCommand(ctx); await expect(command.runAsync()).rejects.toThrowError(); @@ -105,6 +109,7 @@ describe(IosSubmitCommand, () => { analytics, exp: testProject.appJSON.expo, projectId, + vcsClient, }); const command = new IosSubmitCommand(ctx); await command.runAsync(); diff --git a/packages/eas-cli/src/update/configure.ts b/packages/eas-cli/src/update/configure.ts index 0b54aaaf81..5ae5e5bfd2 100644 --- a/packages/eas-cli/src/update/configure.ts +++ b/packages/eas-cli/src/update/configure.ts @@ -18,6 +18,7 @@ import { } from '../project/projectUtils'; import { resolveWorkflowPerPlatformAsync } from '../project/workflow'; import { confirmAsync } from '../prompts'; +import { Client } from '../vcs/vcs'; import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from './android/UpdatesModule'; import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from './ios/UpdatesModule'; @@ -260,6 +261,7 @@ function warnEASUpdatesManualConfig({ */ async function ensureEASUpdateIsConfiguredNativelyAsync( graphqlClient: ExpoGraphqlClient, + vcsClient: Client, { exp, projectId, @@ -280,7 +282,7 @@ async function ensureEASUpdateIsConfiguredNativelyAsync( } if (['all', 'ios'].includes(platform) && workflows.ios === Workflow.GENERIC) { - await syncIosUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId); + await syncIosUpdatesConfigurationAsync(graphqlClient, vcsClient, projectDir, exp, projectId); Log.withTick(`Configured ${chalk.bold('Expo.plist')} for EAS Update`); } } @@ -360,11 +362,13 @@ export async function ensureEASUpdateIsConfiguredAsync( exp: expMaybeWithoutUpdates, projectId, projectDir, + vcsClient, platform, }: { exp: ExpoConfig; projectId: string; projectDir: string; + vcsClient: Client; platform: RequestedPlatform | null; } ): Promise { @@ -396,7 +400,7 @@ export async function ensureEASUpdateIsConfiguredAsync( return; } - const workflows = await resolveWorkflowPerPlatformAsync(projectDir); + const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient); const { projectChanged, exp: expWithUpdates } = await ensureEASUpdatesIsConfiguredInExpoConfigAsync({ exp: expMaybeWithoutUpdates, @@ -407,7 +411,7 @@ export async function ensureEASUpdateIsConfiguredAsync( }); if (projectChanged || !hasExpoUpdates) { - await ensureEASUpdateIsConfiguredNativelyAsync(graphqlClient, { + await ensureEASUpdateIsConfiguredNativelyAsync(graphqlClient, vcsClient, { exp: expWithUpdates, projectDir, projectId, diff --git a/packages/eas-cli/src/update/ios/UpdatesModule.ts b/packages/eas-cli/src/update/ios/UpdatesModule.ts index c95126a27e..0a79d718fb 100644 --- a/packages/eas-cli/src/update/ios/UpdatesModule.ts +++ b/packages/eas-cli/src/update/ios/UpdatesModule.ts @@ -5,11 +5,12 @@ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/creat import { RequestedPlatform } from '../../platform'; import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils'; import { readPlistAsync, writePlistAsync } from '../../utils/plist'; -import { getVcsClient } from '../../vcs'; +import { Client } from '../../vcs/vcs'; import { ensureValidVersions } from '../utils'; export async function syncUpdatesConfigurationAsync( graphqlClient: ExpoGraphqlClient, + vcsClient: Client, projectDir: string, exp: ExpoConfig, projectId: string @@ -23,7 +24,7 @@ export async function syncUpdatesConfigurationAsync( expoPlist, accountName ); - await writeExpoPlistAsync(projectDir, updatedExpoPlist); + await writeExpoPlistAsync(vcsClient, projectDir, updatedExpoPlist); } async function readExpoPlistAsync(projectDir: string): Promise { @@ -32,12 +33,13 @@ async function readExpoPlistAsync(projectDir: string): Promise { const expoPlistPath = IOSConfig.Paths.getExpoPlistPath(projectDir); await writePlistAsync(expoPlistPath, expoPlist); - await getVcsClient().trackFileAsync(expoPlistPath); + await vcsClient.trackFileAsync(expoPlistPath); } export async function readReleaseChannelSafelyAsync(projectDir: string): Promise {