From 2bc4365557b86d926ac3fb048ddc48fd55514322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Tue, 24 Jun 2025 10:12:42 +0200 Subject: [PATCH 1/2] Delete .expo directory --- packages/build-tools/src/common/setup.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/build-tools/src/common/setup.ts b/packages/build-tools/src/common/setup.ts index 7f9ee5f4a..6d34aa524 100644 --- a/packages/build-tools/src/common/setup.ts +++ b/packages/build-tools/src/common/setup.ts @@ -38,6 +38,18 @@ export async function setupAsync(ctx: BuildContext) if (ctx.job.platform === Platform.IOS && ctx.env.EAS_BUILD_RUNNER === 'eas-build') { await deleteXcodeEnvLocalIfExistsAsync(ctx as BuildContext); } + + // Delete .expo directory if it exists. + const expoDir = path.join(ctx.getReactNativeProjectDirectory(), '.expo'); + try { + if (await fs.pathExists(expoDir)) { + await fs.remove(expoDir); + ctx.logger.info('Deleted .expo directory.'); + } + } catch (err) { + ctx.logger.warn({ err }, 'Failed to delete .expo directory.'); + } + if (ctx.job.triggeredBy === BuildTrigger.GIT_BASED_INTEGRATION) { // We need to setup envs from eas.json before // eas-build-pre-install hook is called. From 26eba9762c7dffb077248e48736bb317cbf1d6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Tue, 24 Jun 2025 10:27:31 +0200 Subject: [PATCH 2/2] Add tests --- .../src/common/__tests__/setup.test.ts | 100 ++++++++++++++++++ packages/build-tools/src/common/setup.ts | 56 +++++----- 2 files changed, 131 insertions(+), 25 deletions(-) create mode 100644 packages/build-tools/src/common/__tests__/setup.test.ts diff --git a/packages/build-tools/src/common/__tests__/setup.test.ts b/packages/build-tools/src/common/__tests__/setup.test.ts new file mode 100644 index 000000000..2ce2f3879 --- /dev/null +++ b/packages/build-tools/src/common/__tests__/setup.test.ts @@ -0,0 +1,100 @@ +import { randomUUID } from 'crypto'; +import path from 'path'; + +import { vol } from 'memfs'; +import fs from 'fs-extra'; +import { + Platform, + BuildMode, + BuildTrigger, + Workflow, + ArchiveSourceType, + BuildJob, +} from '@expo/eas-build-job'; + +import { BuildContext } from '../..'; +import { prepareProjectAsync } from '../setup'; +import { createMockLogger } from '../../__tests__/utils/logger'; + +jest.mock('../projectSources'); + +describe('setup', () => { + beforeEach(() => { + vol.reset(); + }); + + it('should delete .expo directory if it exists', async () => { + // Arrange + const projectRoot = '/app'; + await vol.promises.mkdir(path.join(projectRoot, '.expo'), { recursive: true }); + await vol.promises.writeFile(path.join(projectRoot, '.expo', 'test.txt'), 'test'); + + const ctx = new BuildContext( + { + triggeredBy: BuildTrigger.EAS_CLI, + type: Workflow.MANAGED, + mode: BuildMode.BUILD, + initiatingUserId: randomUUID(), + appId: randomUUID(), + projectArchive: { + type: ArchiveSourceType.PATH, + path: projectRoot, + }, + platform: Platform.IOS, + secrets: { + robotAccessToken: randomUUID(), + environmentSecrets: [], + }, + } as BuildJob, + { + env: {}, + workingdir: '/workingdir', + logger: createMockLogger(), + logBuffer: { getLogs: () => [], getPhaseLogs: () => [] }, + uploadArtifact: jest.fn(), + } + ); + jest.spyOn(ctx, 'getReactNativeProjectDirectory').mockReturnValue(projectRoot); + + // Act + await prepareProjectAsync(ctx); + + // Assert + expect(await fs.pathExists(path.join(projectRoot, '.expo'))).toBe(false); + }); + + it('should not fail if .expo directory does not exist', async () => { + const projectRoot = '/app'; + + const ctx = new BuildContext( + { + triggeredBy: BuildTrigger.EAS_CLI, + type: Workflow.MANAGED, + mode: BuildMode.BUILD, + initiatingUserId: randomUUID(), + appId: randomUUID(), + projectArchive: { + type: ArchiveSourceType.PATH, + path: projectRoot, + }, + platform: Platform.IOS, + secrets: { + robotAccessToken: randomUUID(), + environmentSecrets: [], + }, + } as BuildJob, + { + env: {}, + workingdir: '/workingdir', + logger: createMockLogger(), + logBuffer: { getLogs: () => [], getPhaseLogs: () => [] }, + uploadArtifact: jest.fn(), + } + ); + jest.spyOn(ctx, 'getReactNativeProjectDirectory').mockReturnValue(projectRoot); + + await prepareProjectAsync(ctx); + + expect(await fs.pathExists(path.join(projectRoot, '.expo'))).toBe(false); + }); +}); diff --git a/packages/build-tools/src/common/setup.ts b/packages/build-tools/src/common/setup.ts index 6d34aa524..453a058b0 100644 --- a/packages/build-tools/src/common/setup.ts +++ b/packages/build-tools/src/common/setup.ts @@ -33,31 +33,7 @@ class InstallDependenciesTimeoutError extends Error {} export async function setupAsync(ctx: BuildContext): Promise { await ctx.runBuildPhase(BuildPhase.PREPARE_PROJECT, async () => { - await prepareProjectSourcesAsync(ctx); - await setUpNpmrcAsync(ctx, ctx.logger); - if (ctx.job.platform === Platform.IOS && ctx.env.EAS_BUILD_RUNNER === 'eas-build') { - await deleteXcodeEnvLocalIfExistsAsync(ctx as BuildContext); - } - - // Delete .expo directory if it exists. - const expoDir = path.join(ctx.getReactNativeProjectDirectory(), '.expo'); - try { - if (await fs.pathExists(expoDir)) { - await fs.remove(expoDir); - ctx.logger.info('Deleted .expo directory.'); - } - } catch (err) { - ctx.logger.warn({ err }, 'Failed to delete .expo directory.'); - } - - if (ctx.job.triggeredBy === BuildTrigger.GIT_BASED_INTEGRATION) { - // We need to setup envs from eas.json before - // eas-build-pre-install hook is called. - const env = await resolveEnvFromBuildProfileAsync(ctx, { - cwd: ctx.getReactNativeProjectDirectory(), - }); - ctx.updateEnv(env); - } + await prepareProjectAsync(ctx); }); await ctx.runBuildPhase(BuildPhase.PRE_INSTALL_HOOK, async () => { @@ -142,6 +118,36 @@ export async function setupAsync(ctx: BuildContext) } } +export async function prepareProjectAsync( + ctx: BuildContext +): Promise { + await prepareProjectSourcesAsync(ctx); + await setUpNpmrcAsync(ctx, ctx.logger); + if (ctx.job.platform === Platform.IOS && ctx.env.EAS_BUILD_RUNNER === 'eas-build') { + await deleteXcodeEnvLocalIfExistsAsync(ctx as BuildContext); + } + + // Delete .expo directory if it exists. + const expoDir = path.join(ctx.getReactNativeProjectDirectory(), '.expo'); + try { + if (await fs.pathExists(expoDir)) { + await fs.remove(expoDir); + ctx.logger.info('Deleted .expo directory.'); + } + } catch (err) { + ctx.logger.warn({ err }, 'Failed to delete .expo directory.'); + } + + if (ctx.job.triggeredBy === BuildTrigger.GIT_BASED_INTEGRATION) { + // We need to setup envs from eas.json before + // eas-build-pre-install hook is called. + const env = await resolveEnvFromBuildProfileAsync(ctx, { + cwd: ctx.getReactNativeProjectDirectory(), + }); + ctx.updateEnv(env); + } +} + async function runExpoDoctor(ctx: BuildContext): Promise { ctx.logger.info('Running "expo doctor"'); let timeout: NodeJS.Timeout | undefined;