Skip to content

Commit

Permalink
[build-tools] use frozen lockfile for node modules install for SDK 52…
Browse files Browse the repository at this point in the history
…+ and RN 0.76+
  • Loading branch information
szdziedzic committed Nov 12, 2024
1 parent e83cece commit 96eff2e
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 16 deletions.
55 changes: 46 additions & 9 deletions packages/build-tools/src/common/installDependencies.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,61 @@
import path from 'path';

import semver from 'semver';
import { Job } from '@expo/eas-build-job';
import spawn, { SpawnPromise, SpawnResult, SpawnOptions } from '@expo/turtle-spawn';

import { BuildContext } from '../context';
import { PackageManager, findPackagerRootDir } from '../utils/packageManager';
import { isUsingYarn2 } from '../utils/project';
import { isUsingModernYarnVersion } from '../utils/project';

export async function installDependenciesAsync<TJob extends Job>(
ctx: BuildContext<TJob>,
{ logger, infoCallbackFn, cwd }: SpawnOptions
{
logger,
infoCallbackFn,
cwd,
sdkVersionFromPackageJson,
reactNativeVersionFromPackageJson,
withoutFrozenLockfile,
}: {
logger?: SpawnOptions['logger'];
infoCallbackFn?: SpawnOptions['infoCallbackFn'];
cwd?: SpawnOptions['cwd'];
sdkVersionFromPackageJson?: string;
reactNativeVersionFromPackageJson?: string;
withoutFrozenLockfile?: boolean;
}
): Promise<{ spawnPromise: SpawnPromise<SpawnResult> }> {
let args = ['install'];
if (ctx.packageManager === PackageManager.PNPM) {
args = ['install', '--no-frozen-lockfile'];
} else if (ctx.packageManager === PackageManager.YARN) {
const isYarn2 = await isUsingYarn2(ctx.getReactNativeProjectDirectory());
if (isYarn2) {
args = ['install', '--no-immutable', '--inline-builds'];
const shouldUseFrozenLockfile = Boolean(
!withoutFrozenLockfile &&
!ctx.env.EAS_NO_FROZEN_LOCKFILE &&
((!!sdkVersionFromPackageJson && semver.satisfies(sdkVersionFromPackageJson, '>=52')) ||
(!!reactNativeVersionFromPackageJson &&
semver.satisfies(reactNativeVersionFromPackageJson, '>=0.76')))
);

let args: string[];
switch (ctx.packageManager) {
case PackageManager.NPM: {
args = [shouldUseFrozenLockfile ? 'ci' : 'install'];
break;
}
case PackageManager.PNPM: {
args = ['install', shouldUseFrozenLockfile ? '--frozen-lockfile' : '--no-frozen-lockfile'];
break;
}
case PackageManager.YARN: {
const isYarn2 = await isUsingModernYarnVersion(ctx.getReactNativeProjectDirectory());
args = isYarn2
? ['install', shouldUseFrozenLockfile ? '--immutable' : '--no-immutable', '--inline-builds']
: ['install', ...(shouldUseFrozenLockfile ? ['--frozen-lockfile'] : [])];
break;
}
case PackageManager.BUN:
args = ['install', ...(shouldUseFrozenLockfile ? ['--frozen-lockfile'] : [])];
break;
default:
throw new Error(`Unsupported package manager: ${ctx.packageManager}`);
}
logger?.info(`Running "${ctx.packageManager} ${args.join(' ')}" in ${cwd} directory`);
return {
Expand Down
1 change: 1 addition & 0 deletions packages/build-tools/src/common/prebuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export async function prebuildAsync<TJob extends BuildJob>(
await installDependenciesAsync(ctx, {
logger,
cwd: resolvePackagerDir(ctx),
withoutFrozenLockfile: true, // prebuild should can modify package.json in some cases
})
).spawnPromise;
await installDependenciesSpawnPromise;
Expand Down
22 changes: 20 additions & 2 deletions packages/build-tools/src/common/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BuildTrigger } from '@expo/eas-build-job/dist/common';
import nullthrows from 'nullthrows';
import { ExpoConfig } from '@expo/config';
import { UserFacingError } from '@expo/eas-build-job/dist/errors';
import semver from 'semver';

import { BuildContext } from '../context';
import { deleteXcodeEnvLocalIfExistsAsync } from '../ios/xcodeEnv';
Expand Down Expand Up @@ -55,8 +56,16 @@ export async function setupAsync<TJob extends BuildJob>(ctx: BuildContext<TJob>)
return packageJson;
});

const sdkVersionFromPackageJson = semver.coerce(packageJson?.dependencies?.expo)?.version;
const reactNativeVersionFromPackageJson = semver.coerce(
packageJson?.dependencies?.['react-native']
)?.version;

await ctx.runBuildPhase(BuildPhase.INSTALL_DEPENDENCIES, async () => {
await runInstallDependenciesAsync(ctx);
await runInstallDependenciesAsync(ctx, {
sdkVersionFromPackageJson,
reactNativeVersionFromPackageJson,
});
});

await ctx.runBuildPhase(BuildPhase.READ_APP_CONFIG, async () => {
Expand Down Expand Up @@ -145,7 +154,14 @@ async function runExpoDoctor<TJob extends Job>(ctx: BuildContext<TJob>): Promise
}

async function runInstallDependenciesAsync<TJob extends Job>(
ctx: BuildContext<TJob>
ctx: BuildContext<TJob>,
{
sdkVersionFromPackageJson,
reactNativeVersionFromPackageJson,
}: {
sdkVersionFromPackageJson?: string;
reactNativeVersionFromPackageJson?: string;
}
): Promise<void> {
let warnTimeout: NodeJS.Timeout | undefined;
let killTimeout: NodeJS.Timeout | undefined;
Expand All @@ -163,6 +179,8 @@ async function runInstallDependenciesAsync<TJob extends Job>(
}
},
cwd: resolvePackagerDir(ctx),
sdkVersionFromPackageJson,
reactNativeVersionFromPackageJson,
})
).spawnPromise;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
PackageManager,
resolvePackageManager,
} from '../../utils/packageManager';
import { isUsingYarn2 } from '../../utils/project';
import { isUsingModernYarnVersion } from '../../utils/project';

export function createInstallNodeModulesBuildFunction(): BuildFunction {
return new BuildFunction({
Expand Down Expand Up @@ -44,7 +44,7 @@ export async function installNodeModules(
if (packageManager === PackageManager.PNPM) {
args = ['install', '--no-frozen-lockfile'];
} else if (packageManager === PackageManager.YARN) {
const isYarn2 = await isUsingYarn2(stepCtx.workingDirectory);
const isYarn2 = await isUsingModernYarnVersion(stepCtx.workingDirectory);
if (isYarn2) {
args = ['install', '--no-immutable', '--inline-builds'];
}
Expand Down
4 changes: 2 additions & 2 deletions packages/build-tools/src/utils/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import spawn from '@expo/turtle-spawn';
import { BuildContext } from '../context';

import { PackageManager } from './packageManager';
import { isUsingYarn2, readPackageJson } from './project';
import { isUsingModernYarnVersion, readPackageJson } from './project';

export enum Hook {
PRE_INSTALL = 'eas-build-pre-install',
Expand All @@ -31,7 +31,7 @@ export async function runHookIfPresent<TJob extends BuildJob>(
// when using yarn 2, it's not possible to run any scripts before running 'yarn install'
// use 'npm' in that case
const packageManager =
(await isUsingYarn2(projectDir)) && hook === Hook.PRE_INSTALL
(await isUsingModernYarnVersion(projectDir)) && hook === Hook.PRE_INSTALL
? PackageManager.NPM
: ctx.packageManager;
await spawn(packageManager, ['run', hook], {
Expand Down
2 changes: 1 addition & 1 deletion packages/build-tools/src/utils/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { findPackagerRootDir, PackageManager } from '../utils/packageManager';
/**
* check if .yarnrc.yml exists in the project dir or in the workspace root dir
*/
export async function isUsingYarn2(projectDir: string): Promise<boolean> {
export async function isUsingModernYarnVersion(projectDir: string): Promise<boolean> {
const yarnrcPath = path.join(projectDir, '.yarnrc.yml');
const yarnrcRootPath = path.join(findPackagerRootDir(projectDir), '.yarnrc.yml');
return (await fs.pathExists(yarnrcPath)) || (await fs.pathExists(yarnrcRootPath));
Expand Down

0 comments on commit 96eff2e

Please sign in to comment.