Skip to content

Commit

Permalink
feat(npm): use Yarn 3 mode to skip install or build (#11012)
Browse files Browse the repository at this point in the history
  • Loading branch information
ylemkimon committed Aug 29, 2021
1 parent 5d9eeb0 commit 62a540c
Show file tree
Hide file tree
Showing 11 changed files with 481 additions and 72 deletions.
135 changes: 135 additions & 0 deletions lib/manager/npm/extract/__snapshots__/index.spec.ts.snap
Expand Up @@ -1339,3 +1339,138 @@ Object {
"yarnWorkspacesPackages": Array [],
}
`;

exports[`manager/npm/extract/index .extractPackageFile() sets skipInstalls false if Yarn zero-install is used 1`] = `
Object {
"constraints": Object {},
"deps": Array [
Object {
"currentValue": "6.5.0",
"datasource": "npm",
"depName": "autoprefixer",
"depType": "dependencies",
"prettyDepType": "dependency",
},
Object {
"currentValue": "~1.6.0",
"datasource": "npm",
"depName": "bower",
"depType": "dependencies",
"prettyDepType": "dependency",
},
Object {
"currentValue": "13.1.0",
"datasource": "npm",
"depName": "browserify",
"depType": "dependencies",
"prettyDepType": "dependency",
},
Object {
"currentValue": "0.9.2",
"datasource": "npm",
"depName": "browserify-css",
"depType": "dependencies",
"prettyDepType": "dependency",
},
Object {
"currentValue": "=0.22.0",
"datasource": "npm",
"depName": "cheerio",
"depType": "dependencies",
"prettyDepType": "dependency",
},
Object {
"currentValue": "1.21.0",
"datasource": "npm",
"depName": "config",
"depType": "dependencies",
"prettyDepType": "dependency",
},
Object {
"depName": "enabled",
"depType": "devDependencies",
"prettyDepType": "devDependency",
"skipReason": "invalid-value",
},
Object {
"currentValue": "^1.5.8",
"datasource": "npm",
"depName": "angular",
"depType": "devDependencies",
"prettyDepType": "devDependency",
},
Object {
"currentValue": "1.5.8",
"datasource": "npm",
"depName": "angular-touch",
"depType": "devDependencies",
"prettyDepType": "devDependency",
},
Object {
"currentValue": "1.5.8",
"datasource": "npm",
"depName": "angular-sanitize",
"depType": "devDependencies",
"prettyDepType": "devDependency",
},
Object {
"currentValue": "4.0.0-beta.1",
"datasource": "npm",
"depName": "@angular/core",
"depType": "devDependencies",
"prettyDepType": "devDependency",
},
Object {
"currentValue": "1.21.0",
"datasource": "npm",
"depName": "config",
"depType": "resolutions",
"prettyDepType": "resolutions",
},
Object {
"currentValue": "8.0.0",
"datasource": "npm",
"depName": "@angular/cli",
"depType": "resolutions",
"managerData": Object {
"key": "**/@angular/cli",
},
"prettyDepType": "resolutions",
},
Object {
"currentValue": "1.33.0",
"datasource": "npm",
"depName": "angular",
"depType": "resolutions",
"managerData": Object {
"key": "**/angular",
},
"prettyDepType": "resolutions",
},
Object {
"currentValue": "1.0.0",
"datasource": "npm",
"depName": "glob",
"depType": "resolutions",
"managerData": Object {
"key": "config/glob",
},
"prettyDepType": "resolutions",
},
],
"lernaClient": undefined,
"lernaPackages": undefined,
"managerData": Object {
"lernaJsonFile": undefined,
},
"npmLock": undefined,
"npmrc": undefined,
"packageFileVersion": "1.0.0",
"packageJsonName": "renovate",
"packageJsonType": "app",
"pnpmShrinkwrap": undefined,
"skipInstalls": false,
"yarnLock": "yarn.lock",
"yarnWorkspacesPackages": undefined,
}
`;
34 changes: 34 additions & 0 deletions lib/manager/npm/extract/__snapshots__/locked-versions.spec.ts.snap
Expand Up @@ -195,3 +195,37 @@ Array [
},
]
`;

exports[`manager/npm/extract/locked-versions .getLockedVersions() uses yarn.lock with yarn v3.0.0 1`] = `
Array [
Object {
"constraints": Object {
"yarn": "^3.0.0",
},
"deps": Array [
Object {
"currentValue": "1.0.0",
"depName": "a",
"lockedVersion": "1.0.0",
},
Object {
"currentValue": "2.0.0",
"depName": "b",
"lockedVersion": "2.0.0",
},
Object {
"currentValue": "^3.0.0",
"depName": "yarn",
"depType": "engines",
"lockedVersion": undefined,
"lookupName": "@yarnpkg/cli",
},
],
"lockFiles": Array [
"yarn.lock",
],
"npmLock": "package-lock.json",
"yarnLock": "yarn.lock",
},
]
`;
20 changes: 20 additions & 0 deletions lib/manager/npm/extract/index.spec.ts
Expand Up @@ -22,6 +22,7 @@ describe('manager/npm/extract/index', () => {
describe('.extractPackageFile()', () => {
beforeEach(() => {
fs.readLocalFile = jest.fn(() => null);
fs.localPathExists = jest.fn(() => false);
});
it('returns null if cannot parse', async () => {
const res = await npmExtract.extractPackageFile(
Expand Down Expand Up @@ -365,6 +366,25 @@ describe('manager/npm/extract/index', () => {
// FIXME: explicit assert condition
expect(res).toMatchSnapshot();
});

it('sets skipInstalls false if Yarn zero-install is used', async () => {
fs.readLocalFile = jest.fn((fileName) => {
if (fileName === 'yarn.lock') {
return '# yarn.lock';
}
if (fileName === '.yarnrc.yml') {
return 'pnpEnableInlining: false';
}
return null;
});
fs.localPathExists = jest.fn(() => true);
const res = await npmExtract.extractPackageFile(
input01Content,
'package.json',
defaultConfig
);
expect(res).toMatchSnapshot();
});
});
describe('.postExtract()', () => {
it('runs', async () => {
Expand Down
8 changes: 7 additions & 1 deletion lib/manager/npm/extract/index.ts
Expand Up @@ -19,6 +19,7 @@ import { getLockedVersions } from './locked-versions';
import { detectMonorepos } from './monorepo';
import { mightBeABrowserLibrary } from './type';
import type { NpmPackage, NpmPackageDependency } from './types';
import { isZeroInstall } from './yarn';

function parseDepName(depType: string, key: string): string {
if (depType !== 'resolutions') {
Expand Down Expand Up @@ -117,6 +118,10 @@ export async function extractPackageFile(
}
}
}

const yarnrcYmlFileName = getSiblingFileName(fileName, '.yarnrc.yml');
const yarnZeroInstall = await isZeroInstall(yarnrcYmlFileName);

let lernaJsonFile: string;
let lernaPackages: string[];
let lernaClient: 'yarn' | 'npm';
Expand Down Expand Up @@ -342,11 +347,12 @@ export async function extractPackageFile(
}
let skipInstalls = config.skipInstalls;
if (skipInstalls === null) {
if (hasFancyRefs && lockFiles.npmLock) {
if ((hasFancyRefs && lockFiles.npmLock) || yarnZeroInstall) {
// https://github.com/npm/cli/issues/1432
// Explanation:
// - npm install --package-lock-only is buggy for transitive deps in file: and npm: references
// - So we set skipInstalls to false if file: or npm: refs are found *and* the user hasn't explicitly set the value already
// - Also, do not skip install if Yarn zero-install is used
logger.debug('Automatically setting skipInstalls to false');
skipInstalls = false;
} else {
Expand Down
80 changes: 41 additions & 39 deletions lib/manager/npm/extract/locked-versions.spec.ts
Expand Up @@ -10,45 +10,47 @@ jest.mock('./yarn');

describe('manager/npm/extract/locked-versions', () => {
describe('.getLockedVersions()', () => {
it.each([['1.22.0'], ['2.1.0'], ['2.2.0']])(
'uses yarn.lock with yarn v%s',
async (yarnVersion) => {
yarn.getYarnLock.mockReturnValue({
isYarn1: yarnVersion === '1.22.0',
lockfileVersion: yarnVersion === '2.2.0' ? 6 : undefined,
lockedVersions: {
'a@1.0.0': '1.0.0',
'b@2.0.0': '2.0.0',
'c@2.0.0': '3.0.0',
},
});
const packageFiles = [
{
npmLock: 'package-lock.json',
yarnLock: 'yarn.lock',
constraints: {},
deps: [
{
depName: 'a',
currentValue: '1.0.0',
},
{
depName: 'b',
currentValue: '2.0.0',
},
{
depType: 'engines',
depName: 'yarn',
currentValue: `^${yarnVersion}`,
},
],
},
];
await getLockedVersions(packageFiles);
// FIXME: explicit assert condition
expect(packageFiles).toMatchSnapshot();
}
);
it.each([
['1.22.0', undefined],
['2.1.0', undefined],
['2.2.0', 6],
['3.0.0', 8],
])('uses yarn.lock with yarn v%s', async (yarnVersion, lockfileVersion) => {
yarn.getYarnLock.mockReturnValue({
isYarn1: yarnVersion === '1.22.0',
lockfileVersion,
lockedVersions: {
'a@1.0.0': '1.0.0',
'b@2.0.0': '2.0.0',
'c@2.0.0': '3.0.0',
},
});
const packageFiles = [
{
npmLock: 'package-lock.json',
yarnLock: 'yarn.lock',
constraints: {},
deps: [
{
depName: 'a',
currentValue: '1.0.0',
},
{
depName: 'b',
currentValue: '2.0.0',
},
{
depType: 'engines',
depName: 'yarn',
currentValue: `^${yarnVersion}`,
},
],
},
];
await getLockedVersions(packageFiles);
// FIXME: explicit assert condition
expect(packageFiles).toMatchSnapshot();
});

it.each([['6.0.0'], ['7.0.0']])(
'uses package-lock.json with npm v%s',
Expand Down
5 changes: 4 additions & 1 deletion lib/manager/npm/extract/locked-versions.ts
Expand Up @@ -22,7 +22,10 @@ export async function getLockedVersions(
}
const { lockfileVersion, isYarn1 } = lockFileCache[yarnLock];
if (!isYarn1) {
if (lockfileVersion >= 6) {
if (lockfileVersion >= 8) {
// https://github.com/yarnpkg/berry/commit/9bcd27ae34aee77a567dd104947407532fa179b3
packageFile.constraints.yarn = '^3.0.0';
} else if (lockfileVersion >= 6) {
// https://github.com/yarnpkg/berry/commit/f753790380cbda5b55d028ea84b199445129f9ba
packageFile.constraints.yarn = '^2.2.0';
} else {
Expand Down
32 changes: 30 additions & 2 deletions lib/manager/npm/extract/yarn.ts
@@ -1,7 +1,12 @@
import { structUtils } from '@yarnpkg/core';
import is from '@sindresorhus/is';
import { miscUtils, structUtils } from '@yarnpkg/core';
import { parseSyml } from '@yarnpkg/parsers';
import { logger } from '../../../logger';
import { readLocalFile } from '../../../util/fs';
import {
getSiblingFileName,
localPathExists,
readLocalFile,
} from '../../../util/fs';
import type { LockFile } from './types';

export async function getYarnLock(filePath: string): Promise<LockFile> {
Expand Down Expand Up @@ -36,3 +41,26 @@ export async function getYarnLock(filePath: string): Promise<LockFile> {
return { isYarn1: true, lockedVersions: {} };
}
}

export function getZeroInstallPaths(yarnrcYml: string): string[] {
const conf = parseSyml(yarnrcYml);
const paths = [conf.cacheFolder || './.yarn/cache', '.pnp.cjs', '.pnp.js'];
if (miscUtils.tryParseOptionalBoolean(conf.pnpEnableInlining) === false) {
paths.push(conf.pnpDataPath || './.pnp.data.json');
}
return paths;
}

export async function isZeroInstall(yarnrcYmlPath: string): Promise<boolean> {
const yarnrcYml = await readLocalFile(yarnrcYmlPath, 'utf8');
if (is.string(yarnrcYml)) {
const paths = getZeroInstallPaths(yarnrcYml);
for (const p of paths) {
if (await localPathExists(getSiblingFileName(yarnrcYmlPath, p))) {
logger.debug(`Detected Yarn zero-install in ${p}`);
return true;
}
}
}
return false;
}

0 comments on commit 62a540c

Please sign in to comment.