Skip to content

Commit 8a66d38

Browse files
authored
[build-tools] Let updateEnv override existing values (#3901)
## Summary - Change `BuildContext.updateEnv` so incoming values override existing env values. - Add a regression test covering overrides and clearing via undefined. ## Why This makes `updateEnv` behave like an update operation and lets callers intentionally refresh or clear existing environment entries. No idea how it existed so long like this. I guess the values we update with don't overlap with the values we already have that often. ## Test Plan CI should pass.
1 parent 6697add commit 8a66d38

2 files changed

Lines changed: 122 additions & 1 deletion

File tree

packages/build-tools/src/__tests__/context.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ jest.mock('fs-extra');
1919
jest.mock('../datadog', () => ({
2020
Datadog: {
2121
distribution: jest.fn(),
22+
log: jest.fn(),
2223
},
2324
}));
2425

2526
const datadogDistributionMock = jest.mocked(Datadog.distribution);
27+
const datadogLogMock = jest.mocked(Datadog.log);
2628

2729
describe('BuildContext', () => {
2830
beforeEach(() => {
@@ -139,6 +141,77 @@ describe('BuildContext', () => {
139141
});
140142
});
141143

144+
it('overwrites existing environment variables when updating env', async () => {
145+
await vol.promises.mkdir('/workingdir/eas-environment-secrets/', { recursive: true });
146+
147+
const ctx = new BuildContext(
148+
{
149+
triggeredBy: BuildTrigger.GIT_BASED_INTEGRATION,
150+
secrets: {},
151+
} as Job,
152+
{
153+
env: {
154+
__API_SERVER_URL: 'http://api.expo.test',
155+
EXISTING_ENV: 'old-value',
156+
REMOVED_ENV: 'old-value',
157+
},
158+
workingdir: '/workingdir',
159+
logger: createMockLogger(),
160+
logBuffer: { getLogs: () => [], getPhaseLogs: () => [] },
161+
uploadArtifact: jest.fn(),
162+
}
163+
);
164+
165+
ctx.updateEnv({
166+
EXISTING_ENV: 'new-value',
167+
REMOVED_ENV: undefined,
168+
NEW_ENV: 'new-env-value',
169+
});
170+
171+
expect(ctx.env.EXISTING_ENV).toBe('new-value');
172+
expect(ctx.env.REMOVED_ENV).toBeUndefined();
173+
expect(ctx.env.NEW_ENV).toBe('new-env-value');
174+
const logMessage = datadogLogMock.mock.calls[0][0];
175+
expect(logMessage).toContain('BuildContext.updateEnv merge order produced different env');
176+
expect(logMessage).toContain('different_env_names=');
177+
expect(logMessage).toContain('EXISTING_ENV');
178+
expect(logMessage).toContain('REMOVED_ENV');
179+
expect(JSON.stringify(datadogLogMock.mock.calls)).not.toContain('old-value');
180+
expect(JSON.stringify(datadogLogMock.mock.calls)).not.toContain('new-value');
181+
expect(JSON.stringify(datadogLogMock.mock.calls)).not.toContain('new-env-value');
182+
});
183+
184+
it('logs when updateEnv merge order produces the same env without values', async () => {
185+
await vol.promises.mkdir('/workingdir/eas-environment-secrets/', { recursive: true });
186+
187+
const ctx = new BuildContext(
188+
{
189+
triggeredBy: BuildTrigger.GIT_BASED_INTEGRATION,
190+
secrets: {},
191+
} as Job,
192+
{
193+
env: {
194+
__API_SERVER_URL: 'http://api.expo.test',
195+
EXISTING_ENV: 'same-value',
196+
},
197+
workingdir: '/workingdir',
198+
logger: createMockLogger(),
199+
logBuffer: { getLogs: () => [], getPhaseLogs: () => [] },
200+
uploadArtifact: jest.fn(),
201+
}
202+
);
203+
204+
ctx.updateEnv({
205+
EXISTING_ENV: 'same-value',
206+
UNSET_ENV: undefined,
207+
});
208+
209+
expect(datadogLogMock).toHaveBeenCalledWith(
210+
'BuildContext.updateEnv merge order produced same env'
211+
);
212+
expect(JSON.stringify(datadogLogMock.mock.calls)).not.toContain('same-value');
213+
});
214+
142215
it('emits build phase duration metrics for successful build phases', async () => {
143216
const ctx = createTestBuildContext({ platform: Platform.IOS });
144217

packages/build-tools/src/context.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,41 @@ export interface BuildContextOptions {
7272

7373
export class SkipNativeBuildError extends Error {}
7474

75+
function logEnvComparison({
76+
oldMergeOrderEnv,
77+
newMergeOrderEnv,
78+
}: {
79+
oldMergeOrderEnv: Env;
80+
newMergeOrderEnv: Env;
81+
}): void {
82+
const envNames = new Set([...Object.keys(oldMergeOrderEnv), ...Object.keys(newMergeOrderEnv)]);
83+
const sameEnvNames: string[] = [];
84+
const differentEnvNames: string[] = [];
85+
86+
for (const key of envNames) {
87+
const hasOldValue = key in oldMergeOrderEnv;
88+
const hasNewValue = key in newMergeOrderEnv;
89+
if (hasOldValue === hasNewValue && oldMergeOrderEnv[key] === newMergeOrderEnv[key]) {
90+
sameEnvNames.push(key);
91+
} else {
92+
differentEnvNames.push(key);
93+
}
94+
}
95+
96+
if (differentEnvNames.length === 0) {
97+
Datadog.log('BuildContext.updateEnv merge order produced same env');
98+
return;
99+
}
100+
101+
Datadog.log(
102+
`BuildContext.updateEnv merge order produced different env: compared_env_count=${
103+
envNames.size
104+
} different_env_count=${differentEnvNames.length} different_env_names=${differentEnvNames.join(
105+
','
106+
)} same_env_count=${sameEnvNames.length} same_env_names=${sameEnvNames.join(',')}`
107+
);
108+
}
109+
75110
export class BuildContext<TJob extends Job = Job> {
76111
public readonly workingdir: string;
77112
public logger: bunyan;
@@ -247,11 +282,24 @@ export class BuildContext<TJob extends Job = Job> {
247282
'Updating environment variables is only allowed when build was triggered by a git-based integration.'
248283
);
249284
}
250-
this._env = {
285+
286+
const oldMergeOrderEnv: Env = {
251287
...env,
252288
...this._env,
253289
__EAS_BUILD_ENVS_DIR: this.buildEnvsDirectory,
254290
};
291+
const newMergeOrderEnv: Env = {
292+
...this._env,
293+
...env,
294+
__EAS_BUILD_ENVS_DIR: this.buildEnvsDirectory,
295+
};
296+
297+
logEnvComparison({
298+
oldMergeOrderEnv,
299+
newMergeOrderEnv,
300+
});
301+
302+
this._env = newMergeOrderEnv;
255303
this._env.PATH = this._env.PATH
256304
? [this.buildExecutablesDirectory, this._env.PATH].join(':')
257305
: this.buildExecutablesDirectory;

0 commit comments

Comments
 (0)