From 672b958b35f8ccd5e00f6646ee3559cdb53cf6ac Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 21 Aug 2026 12:08:34 -0400 Subject: [PATCH 1/5] Use dev mode in sim --- packages/eas-cli/src/simulator/env.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/eas-cli/src/simulator/env.ts b/packages/eas-cli/src/simulator/env.ts index 9c69740df6..2ccc425f04 100644 --- a/packages/eas-cli/src/simulator/env.ts +++ b/packages/eas-cli/src/simulator/env.ts @@ -4,6 +4,7 @@ import * as fs from 'fs-extra'; import path from 'path'; import Log from '../log'; +import { getEnvWithoutInheritedDotenvValues } from '../utils/originalEnv'; export const SIMULATOR_DOTENV_FILE_NAME = '.env.eas-simulator'; export const EAS_SIMULATOR_SESSION_ID = 'EAS_SIMULATOR_SESSION_ID'; @@ -16,9 +17,14 @@ export function getSimulatorEnvFilePath(projectDir: string): string { export async function loadSimulatorEnvAsync(projectDir: string): Promise { const simulatorDotenvFilePath = getSimulatorEnvFilePath(projectDir); + const mode = 'development'; - loadProjectEnv(projectDir, { silent: true }); + process.env = getEnvWithoutInheritedDotenvValues(process.env); + process.env.NODE_ENV = mode; + delete process.env.__EXPO_CONFIG_MODE; + loadProjectEnv(projectDir, { mode, silent: true }); loadEnvFiles([simulatorDotenvFilePath], { force: true }); + delete process.env.__EXPO_CONFIG_MODE; } export async function writeSimulatorEnvAsync( From 8ed58fe2e06b195a0644a9b2a6bafee1bac92557 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 21 Aug 2026 12:08:39 -0400 Subject: [PATCH 2/5] Update tests --- .../commands/simulator/__tests__/exec.test.ts | 17 +++++- .../src/simulator/__tests__/env.test.ts | 54 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/packages/eas-cli/src/commands/simulator/__tests__/exec.test.ts b/packages/eas-cli/src/commands/simulator/__tests__/exec.test.ts index a6959f326e..2b461e7225 100644 --- a/packages/eas-cli/src/commands/simulator/__tests__/exec.test.ts +++ b/packages/eas-cli/src/commands/simulator/__tests__/exec.test.ts @@ -5,6 +5,7 @@ import { Config } from '@oclif/core'; import SimulatorExec from '../exec'; jest.mock('@expo/env', () => ({ + LOADED_ENV_NAME: '__EXPO_ENV_LOADED', loadEnvFiles: jest.fn(), loadProjectEnv: jest.fn(), })); @@ -20,14 +21,20 @@ function getMockOclifConfig(): Config { } describe(SimulatorExec, () => { + const originalEnv = process.env; const mockConfig = getMockOclifConfig(); const projectDir = '/test/project'; beforeEach(() => { jest.clearAllMocks(); + process.env = { ...originalEnv }; jest.mocked(spawnAsync).mockResolvedValue({} as never); }); + afterEach(() => { + process.env = originalEnv; + }); + function createCommand(argv: string[]): { command: SimulatorExec; getContextAsync: jest.SpyInstance; @@ -48,7 +55,10 @@ describe(SimulatorExec, () => { expect(getContextAsync).toHaveBeenCalledWith(SimulatorExec, { nonInteractive: true, }); - expect(loadProjectEnv).toHaveBeenCalledWith(projectDir, { silent: true }); + expect(loadProjectEnv).toHaveBeenCalledWith(projectDir, { + mode: 'development', + silent: true, + }); expect(loadEnvFiles).toHaveBeenCalledWith([`${projectDir}/.env.eas-simulator`], { force: true, }); @@ -123,7 +133,10 @@ describe(SimulatorExec, () => { const { command } = createCommand(['agent-device', 'touch', '@e2']); await command.runAsync(); - expect(loadProjectEnv).toHaveBeenCalledWith(projectDir, { silent: true }); + expect(loadProjectEnv).toHaveBeenCalledWith(projectDir, { + mode: 'development', + silent: true, + }); expect(loadEnvFiles).toHaveBeenCalledWith([`${projectDir}/.env.eas-simulator`], { force: true, }); diff --git a/packages/eas-cli/src/simulator/__tests__/env.test.ts b/packages/eas-cli/src/simulator/__tests__/env.test.ts index cce1ab2868..cea60d6aaf 100644 --- a/packages/eas-cli/src/simulator/__tests__/env.test.ts +++ b/packages/eas-cli/src/simulator/__tests__/env.test.ts @@ -1,3 +1,4 @@ +import { loadEnvFiles, loadProjectEnv } from '@expo/env'; import * as fs from 'fs-extra'; import { parse as parseDotenv } from 'dotenv'; @@ -5,12 +6,65 @@ import { EAS_SIMULATOR_SESSION_ID, SIMULATOR_DOTENV_FILE_HEADER, getSimulatorEnvFilePath, + loadSimulatorEnvAsync, resetSimulatorEnvAsync, writeSimulatorEnvAsync, } from '../env'; +jest.mock('@expo/env', () => ({ + LOADED_ENV_NAME: '__EXPO_ENV_LOADED', + loadEnvFiles: jest.fn(), + loadProjectEnv: jest.fn(), +})); jest.mock('fs-extra'); +describe(loadSimulatorEnvAsync, () => { + const projectDir = '/test/project'; + const originalEnv = process.env; + + beforeEach(() => { + jest.clearAllMocks(); + process.env = { + ...originalEnv, + DOTENV_VALUE: 'from-parent', + KEEP_VALUE: 'from-shell', + NODE_ENV: 'staging', + __EXPO_ENV_LOADED: '["DOTENV_VALUE"]', + __EXPO_CONFIG_MODE: 'production', + }; + }); + + afterEach(() => { + process.env = originalEnv; + }); + + it('loads simulator env in development mode without inherited dotenv values', async () => { + jest.mocked(loadProjectEnv).mockImplementation(() => { + expect(process.env.DOTENV_VALUE).toBeUndefined(); + expect(process.env.KEEP_VALUE).toBe('from-shell'); + expect(process.env.NODE_ENV).toBe('development'); + expect(process.env.__EXPO_ENV_LOADED).toBeUndefined(); + expect(process.env.__EXPO_CONFIG_MODE).toBeUndefined(); + return {} as never; + }); + jest.mocked(loadEnvFiles).mockImplementation(() => { + process.env.__EXPO_CONFIG_MODE = 'from-simulator-env'; + return {} as never; + }); + + await loadSimulatorEnvAsync(projectDir); + + expect(loadProjectEnv).toHaveBeenCalledWith(projectDir, { + mode: 'development', + silent: true, + }); + expect(loadEnvFiles).toHaveBeenCalledWith([`${projectDir}/.env.eas-simulator`], { + force: true, + }); + expect(process.env.__EXPO_CONFIG_MODE).toBeUndefined(); + }); +}); + describe(resetSimulatorEnvAsync, () => { const projectDir = '/test/project'; const simulatorDotenvPath = getSimulatorEnvFilePath(projectDir); From 1bb79363a7c23c0023b62bd64d7f081339da787d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 21 Aug 2026 12:10:59 -0400 Subject: [PATCH 3/5] Add changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79a5322178..d3ea4d5de6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ This is the log of notable changes to EAS CLI and related packages. ### ๐Ÿ› Bug fixes +- [eas-cli] Use development mode when EAS Simulator loads env files. ([#4245](https://github.com/expo/eas-cli/pull/4245) by [@ramonclaudio](https://github.com/ramonclaudio)) + ### ๐Ÿงน Chores ## [22.2.0](https://github.com/expo/eas-cli/releases/tag/v22.2.0) - 2026-08-20 From 4af7d467f80af76e9ac03d3aff1cd1ab985cbcc4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 23 Aug 2026 23:46:04 -0400 Subject: [PATCH 4/5] Fix sim --- packages/eas-cli/src/simulator/env.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/eas-cli/src/simulator/env.ts b/packages/eas-cli/src/simulator/env.ts index 2ccc425f04..2dbfe9c8a0 100644 --- a/packages/eas-cli/src/simulator/env.ts +++ b/packages/eas-cli/src/simulator/env.ts @@ -1,4 +1,4 @@ -import { loadEnvFiles, loadProjectEnv } from '@expo/env'; +import { LOADED_ENV_NAME, loadEnvFiles, loadProjectEnv } from '@expo/env'; import { parse as parseDotenv } from 'dotenv'; import * as fs from 'fs-extra'; import path from 'path'; @@ -22,8 +22,11 @@ export async function loadSimulatorEnvAsync(projectDir: string): Promise { process.env = getEnvWithoutInheritedDotenvValues(process.env); process.env.NODE_ENV = mode; delete process.env.__EXPO_CONFIG_MODE; - loadProjectEnv(projectDir, { mode, silent: true }); loadEnvFiles([simulatorDotenvFilePath], { force: true }); + delete process.env[LOADED_ENV_NAME]; + delete process.env.__EXPO_CONFIG_MODE; + loadProjectEnv(projectDir, { force: true, mode, silent: true }); + delete process.env[LOADED_ENV_NAME]; delete process.env.__EXPO_CONFIG_MODE; } From 49ecbf679fa9589af53512924122cd98b1e92bdb Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 23 Aug 2026 23:46:15 -0400 Subject: [PATCH 5/5] Update tests --- .../commands/simulator/__tests__/exec.test.ts | 17 +---- .../__tests__/env.integration.test.ts | 63 +++++++++++++++++++ .../src/simulator/__tests__/env.test.ts | 29 ++++++--- 3 files changed, 86 insertions(+), 23 deletions(-) create mode 100644 packages/eas-cli/src/simulator/__tests__/env.integration.test.ts diff --git a/packages/eas-cli/src/commands/simulator/__tests__/exec.test.ts b/packages/eas-cli/src/commands/simulator/__tests__/exec.test.ts index 2b461e7225..a10e687191 100644 --- a/packages/eas-cli/src/commands/simulator/__tests__/exec.test.ts +++ b/packages/eas-cli/src/commands/simulator/__tests__/exec.test.ts @@ -56,6 +56,7 @@ describe(SimulatorExec, () => { nonInteractive: true, }); expect(loadProjectEnv).toHaveBeenCalledWith(projectDir, { + force: true, mode: 'development', silent: true, }); @@ -128,20 +129,4 @@ describe(SimulatorExec, () => { expect(loadEnvFiles).not.toHaveBeenCalled(); expect(spawnAsync).not.toHaveBeenCalled(); }); - - it('loads simulator-specific env after regular env files', async () => { - const { command } = createCommand(['agent-device', 'touch', '@e2']); - await command.runAsync(); - - expect(loadProjectEnv).toHaveBeenCalledWith(projectDir, { - mode: 'development', - silent: true, - }); - expect(loadEnvFiles).toHaveBeenCalledWith([`${projectDir}/.env.eas-simulator`], { - force: true, - }); - expect(jest.mocked(loadProjectEnv).mock.invocationCallOrder[0]).toBeLessThan( - jest.mocked(loadEnvFiles).mock.invocationCallOrder[0] - ); - }); }); diff --git a/packages/eas-cli/src/simulator/__tests__/env.integration.test.ts b/packages/eas-cli/src/simulator/__tests__/env.integration.test.ts new file mode 100644 index 0000000000..deb36cdaa3 --- /dev/null +++ b/packages/eas-cli/src/simulator/__tests__/env.integration.test.ts @@ -0,0 +1,63 @@ +import { LOADED_ENV_NAME } from '@expo/env'; +import * as fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; + +import { loadSimulatorEnvAsync } from '../env'; + +describe(loadSimulatorEnvAsync, () => { + const originalEnv = process.env; + let projectDir: string; + + beforeEach(async () => { + projectDir = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-simulator-env-')); + process.env = { + ...originalEnv, + PARENT_DOTENV_VALUE: 'from-parent', + SHARED_WITH_SHELL: 'from-shell', + NODE_ENV: 'production', + [LOADED_ENV_NAME]: '["PARENT_DOTENV_VALUE"]', + __EXPO_CONFIG_MODE: 'production', + }; + delete process.env.EXPO_NO_DOTENV; + }); + + afterEach(async () => { + process.env = originalEnv; + await fs.rm(projectDir, { force: true, recursive: true }); + }); + + it('keeps shell values and gives simulator values priority over project dotenv values', async () => { + await fs.writeFile( + path.join(projectDir, '.env.development'), + [ + 'PROJECT_VALUE=from-project', + 'SHARED_WITH_SIMULATOR=from-project', + 'SHARED_WITH_SHELL=from-project', + '__EXPO_CONFIG_MODE=from-project', + ].join('\n') + ); + await fs.writeFile( + path.join(projectDir, '.env.eas-simulator'), + [ + 'SIMULATOR_VALUE=from-simulator', + 'SHARED_WITH_SIMULATOR=from-simulator', + 'SHARED_WITH_SHELL=from-simulator', + '__EXPO_CONFIG_MODE=from-simulator', + ].join('\n') + ); + + await loadSimulatorEnvAsync(projectDir); + + expect(process.env).toMatchObject({ + NODE_ENV: 'development', + PROJECT_VALUE: 'from-project', + SHARED_WITH_SHELL: 'from-shell', + SHARED_WITH_SIMULATOR: 'from-simulator', + SIMULATOR_VALUE: 'from-simulator', + }); + expect(process.env.PARENT_DOTENV_VALUE).toBeUndefined(); + expect(process.env[LOADED_ENV_NAME]).toBeUndefined(); + expect(process.env.__EXPO_CONFIG_MODE).toBeUndefined(); + }); +}); diff --git a/packages/eas-cli/src/simulator/__tests__/env.test.ts b/packages/eas-cli/src/simulator/__tests__/env.test.ts index cea60d6aaf..e709923f4a 100644 --- a/packages/eas-cli/src/simulator/__tests__/env.test.ts +++ b/packages/eas-cli/src/simulator/__tests__/env.test.ts @@ -38,29 +38,44 @@ describe(loadSimulatorEnvAsync, () => { process.env = originalEnv; }); - it('loads simulator env in development mode without inherited dotenv values', async () => { - jest.mocked(loadProjectEnv).mockImplementation(() => { + it('loads simulator env before project env files in development mode', async () => { + jest.mocked(loadEnvFiles).mockImplementation(() => { expect(process.env.DOTENV_VALUE).toBeUndefined(); expect(process.env.KEEP_VALUE).toBe('from-shell'); expect(process.env.NODE_ENV).toBe('development'); expect(process.env.__EXPO_ENV_LOADED).toBeUndefined(); expect(process.env.__EXPO_CONFIG_MODE).toBeUndefined(); + process.env.SIMULATOR_VALUE = 'from-simulator'; + process.env.__EXPO_ENV_LOADED = '["SIMULATOR_VALUE"]'; + process.env.__EXPO_CONFIG_MODE = 'from-simulator-env'; return {} as never; }); - jest.mocked(loadEnvFiles).mockImplementation(() => { - process.env.__EXPO_CONFIG_MODE = 'from-simulator-env'; + jest.mocked(loadProjectEnv).mockImplementation(() => { + expect(process.env.SIMULATOR_VALUE).toBe('from-simulator'); + expect(process.env.__EXPO_ENV_LOADED).toBeUndefined(); + expect(process.env.__EXPO_CONFIG_MODE).toBeUndefined(); + process.env.PROJECT_VALUE = 'from-project'; + process.env.__EXPO_ENV_LOADED = '["PROJECT_VALUE"]'; + process.env.__EXPO_CONFIG_MODE = 'from-project-env'; return {} as never; }); await loadSimulatorEnvAsync(projectDir); + expect(loadEnvFiles).toHaveBeenCalledWith([`${projectDir}/.env.eas-simulator`], { + force: true, + }); expect(loadProjectEnv).toHaveBeenCalledWith(projectDir, { + force: true, mode: 'development', silent: true, }); - expect(loadEnvFiles).toHaveBeenCalledWith([`${projectDir}/.env.eas-simulator`], { - force: true, - }); + expect(jest.mocked(loadEnvFiles).mock.invocationCallOrder[0]).toBeLessThan( + jest.mocked(loadProjectEnv).mock.invocationCallOrder[0] + ); + expect(process.env.SIMULATOR_VALUE).toBe('from-simulator'); + expect(process.env.PROJECT_VALUE).toBe('from-project'); + expect(process.env.__EXPO_ENV_LOADED).toBeUndefined(); expect(process.env.__EXPO_CONFIG_MODE).toBeUndefined(); }); });