Skip to content

Commit

Permalink
fix(ng-dev): support releasing with Yarn 2+ (#2126)
Browse files Browse the repository at this point in the history
The version of yarn is now checked prior to use and command line arguments
are adjusted based on the version used. This is required for version 2 or
higher which has different behavior than the now legacy 1.x versions.

PR Close #2126
  • Loading branch information
clydin committed Jun 20, 2024
1 parent e4f04a3 commit c6406e1
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 24 deletions.
24 changes: 9 additions & 15 deletions ng-dev/release/publish/external-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export abstract class ExternalCommands {
yarnCommand.binary,
[
...yarnCommand.args,
'--silent',
'ng-dev',
'release',
'set-dist-tag',
Expand Down Expand Up @@ -97,15 +96,7 @@ export abstract class ExternalCommands {
// TODO: detect yarn berry and handle flag differences properly.
await ChildProcess.spawn(
yarnCommand.binary,
[
...yarnCommand.args,
'--silent',
'ng-dev',
'release',
'npm-dist-tag',
'delete',
npmDistTag,
],
[...yarnCommand.args, 'ng-dev', 'release', 'npm-dist-tag', 'delete', npmDistTag],
{cwd: projectDir},
);
Log.info(green(` ✓ Deleted "${npmDistTag}" NPM dist tag for all packages.`));
Expand Down Expand Up @@ -135,7 +126,7 @@ export abstract class ExternalCommands {
// TODO: detect yarn berry and handle flag differences properly.
const {stdout} = await ChildProcess.spawn(
yarnCommand.binary,
[...yarnCommand.args, '--silent', 'ng-dev', 'release', 'build', '--json'],
[...yarnCommand.args, 'ng-dev', 'release', 'build', '--json'],
{
cwd: projectDir,
mode: 'silent',
Expand Down Expand Up @@ -172,7 +163,7 @@ export abstract class ExternalCommands {
// TODO: detect yarn berry and handle flag differences properly.
const {stdout} = await ChildProcess.spawn(
yarnCommand.binary,
[...yarnCommand.args, '--silent', 'ng-dev', 'release', 'info', '--json'],
[...yarnCommand.args, 'ng-dev', 'release', 'info', '--json'],
{
cwd: projectDir,
mode: 'silent',
Expand Down Expand Up @@ -218,7 +209,7 @@ export abstract class ExternalCommands {
// TODO: detect yarn berry and handle flag differences properly.
await ChildProcess.spawn(
yarnCommand.binary,
[...yarnCommand.args, '--silent', 'ng-dev', 'release', 'precheck'],
[...yarnCommand.args, 'ng-dev', 'release', 'precheck'],
{
cwd: projectDir,
// Note: We pass the precheck information to the command through `stdin`
Expand Down Expand Up @@ -251,8 +242,11 @@ export abstract class ExternalCommands {
// TODO: Consider using an Ora spinner instead to ensure minimal console output.
await ChildProcess.spawn(
yarnCommand.binary,
// TODO: detect yarn berry and handle flag differences properly.
[...yarnCommand.args, 'install', '--frozen-lockfile', '--non-interactive'],
[
...yarnCommand.args,
'install',
...(yarnCommand.legacy ? ['--frozen-lockfile', '--non-interactive'] : ['--immutable']),
],
{cwd: projectDir},
);
Log.info(green(' ✓ Installed project dependencies.'));
Expand Down
34 changes: 29 additions & 5 deletions ng-dev/utils/resolve-yarn-bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type ConfigWithParser = {fileName: string; parse: (c: string) => YarnConf
export interface YarnCommandInfo {
binary: string;
args: string[];
legacy?: boolean;
}

/** List of Yarn configuration files and their parsing mechanisms. */
Expand All @@ -47,17 +48,29 @@ export const yarnConfigFiles: ConfigWithParser[] = [
* become unavailable.
*/
export async function resolveYarnScriptForProject(projectDir: string): Promise<YarnCommandInfo> {
let info: YarnCommandInfo | undefined;

const yarnPathFromConfig = await getYarnPathFromConfigurationIfPresent(projectDir);
if (yarnPathFromConfig !== null) {
return {binary: 'node', args: [yarnPathFromConfig]};
info = {binary: 'node', args: [yarnPathFromConfig]};
}

if (!info) {
const yarnPathFromNpmBin = await getYarnPathFromNpmGlobalBinaries();
if (yarnPathFromNpmBin !== null) {
info = {binary: yarnPathFromNpmBin, args: []};
}
}

const yarnPathFromNpmBin = await getYarnPathFromNpmGlobalBinaries();
if (yarnPathFromNpmBin !== null) {
return {binary: yarnPathFromNpmBin, args: []};
info ??= {binary: 'yarn', args: []};

const yarnVersion = await getYarnVersion(info);
if (yarnVersion && Number(yarnVersion.split('.')[0]) < 2) {
info.args.push('--silent');
info.legacy = true;
}

return {binary: 'yarn', args: []};
return info;
}

/** Gets the path to the Yarn binary from the NPM global binary directory. */
Expand Down Expand Up @@ -99,6 +112,17 @@ async function getYarnPathFromConfigurationIfPresent(projectDir: string): Promis
return path.resolve(projectDir, yarnPath);
}

async function getYarnVersion(info: YarnCommandInfo): Promise<string | null> {
try {
return (
await ChildProcess.spawn(info.binary, [...info.args, '--version'], {mode: 'silent'})
).stdout.trim();
} catch (e) {
Log.debug('Could not determine Yarn version. Error:', e);
return null;
}
}

/** Finds and parses the Yarn configuration file for the given project. */
async function findAndParseYarnConfiguration(
projectDir: string,
Expand Down
11 changes: 7 additions & 4 deletions ng-dev/utils/test/resolve-yarn-bin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,12 @@ describe('resolve yarn bin', () => {
});

it('should fallback to just `yarn` and leave resolution to system', async () => {
expect(await resolveYarnScriptForProject(testTmpDir)).toEqual({
binary: 'yarn',
args: [],
});
// Can contain legacy property on CI depending on the CI global setup
expect(await resolveYarnScriptForProject(testTmpDir)).toEqual(
jasmine.objectContaining({
binary: 'yarn',
args: jasmine.arrayContaining([]),
}),
);
});
});

0 comments on commit c6406e1

Please sign in to comment.