mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-11 14:36:25 +00:00
feat: pnpm shrinkwrap support (#1392)
This feature adds support for pnpm shrinkwrap.yaml files. Closes #1391
This commit is contained in:
parent
400ca57398
commit
23e217991c
10 changed files with 1844 additions and 62 deletions
|
@ -146,6 +146,17 @@ async function resolvePackageFiles(config) {
|
||||||
);
|
);
|
||||||
packageFile.packageLock = packageLockFileName;
|
packageFile.packageLock = packageLockFileName;
|
||||||
}
|
}
|
||||||
|
const shrinkwrapFileName = upath.join(
|
||||||
|
path.dirname(packageFile.packageFile),
|
||||||
|
'shrinkwrap.yaml'
|
||||||
|
);
|
||||||
|
if (fileList.includes(shrinkwrapFileName)) {
|
||||||
|
logger.debug(
|
||||||
|
{ packageFile: packageFile.packageFile },
|
||||||
|
'Found shrinkwrap.yaml'
|
||||||
|
);
|
||||||
|
packageFile.shrinkwrapYaml = shrinkwrapFileName;
|
||||||
|
}
|
||||||
return packageFile;
|
return packageFile;
|
||||||
} else if (packageFile.packageFile.endsWith('package.js')) {
|
} else if (packageFile.packageFile.endsWith('package.js')) {
|
||||||
// meteor
|
// meteor
|
||||||
|
|
|
@ -3,10 +3,12 @@ const path = require('path');
|
||||||
const upath = require('upath');
|
const upath = require('upath');
|
||||||
const npm = require('./npm');
|
const npm = require('./npm');
|
||||||
const yarn = require('./yarn');
|
const yarn = require('./yarn');
|
||||||
|
const pnpm = require('./pnpm');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
hasPackageLock,
|
hasPackageLock,
|
||||||
hasYarnLock,
|
hasYarnLock,
|
||||||
|
hasShrinkwrapYaml,
|
||||||
determineLockFileDirs,
|
determineLockFileDirs,
|
||||||
writeExistingFiles,
|
writeExistingFiles,
|
||||||
writeUpdatedPackageFiles,
|
writeUpdatedPackageFiles,
|
||||||
|
@ -45,9 +47,26 @@ function hasYarnLock(config, packageFile) {
|
||||||
throw new Error(`hasYarnLock cannot find ${packageFile}`);
|
throw new Error(`hasYarnLock cannot find ${packageFile}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasShrinkwrapYaml(config, packageFile) {
|
||||||
|
logger.trace(
|
||||||
|
{ packageFiles: config.packageFiles, packageFile },
|
||||||
|
'hasShrinkwrapYaml'
|
||||||
|
);
|
||||||
|
for (const p of config.packageFiles) {
|
||||||
|
if (p.packageFile === packageFile) {
|
||||||
|
if (p.shrinkwrapYaml) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(`hasShrinkwrapYaml cannot find ${packageFile}`);
|
||||||
|
}
|
||||||
|
|
||||||
function determineLockFileDirs(config) {
|
function determineLockFileDirs(config) {
|
||||||
const packageLockFileDirs = [];
|
const packageLockFileDirs = [];
|
||||||
const yarnLockFileDirs = [];
|
const yarnLockFileDirs = [];
|
||||||
|
const shrinkwrapYamlDirs = [];
|
||||||
|
|
||||||
for (const upgrade of config.upgrades) {
|
for (const upgrade of config.upgrades) {
|
||||||
if (upgrade.type === 'lockFileMaintenance') {
|
if (upgrade.type === 'lockFileMaintenance') {
|
||||||
|
@ -60,8 +79,11 @@ function determineLockFileDirs(config) {
|
||||||
if (packageFile.packageLock) {
|
if (packageFile.packageLock) {
|
||||||
packageLockFileDirs.push(dirname);
|
packageLockFileDirs.push(dirname);
|
||||||
}
|
}
|
||||||
|
if (packageFile.shrinkwrapYaml) {
|
||||||
|
shrinkwrapYamlDirs.push(dirname);
|
||||||
}
|
}
|
||||||
return { packageLockFileDirs, yarnLockFileDirs };
|
}
|
||||||
|
return { packageLockFileDirs, yarnLockFileDirs, shrinkwrapYamlDirs };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +94,9 @@ function determineLockFileDirs(config) {
|
||||||
if (module.exports.hasPackageLock(config, packageFile.name)) {
|
if (module.exports.hasPackageLock(config, packageFile.name)) {
|
||||||
packageLockFileDirs.push(path.dirname(packageFile.name));
|
packageLockFileDirs.push(path.dirname(packageFile.name));
|
||||||
}
|
}
|
||||||
|
if (module.exports.hasShrinkwrapYaml(config, packageFile.name)) {
|
||||||
|
shrinkwrapYamlDirs.push(path.dirname(packageFile.name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If yarn workspaces are in use, then we need to generate yarn.lock from the workspaces dir
|
// If yarn workspaces are in use, then we need to generate yarn.lock from the workspaces dir
|
||||||
|
@ -87,7 +112,7 @@ function determineLockFileDirs(config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { yarnLockFileDirs, packageLockFileDirs };
|
return { yarnLockFileDirs, packageLockFileDirs, shrinkwrapYamlDirs };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function writeExistingFiles(config) {
|
async function writeExistingFiles(config) {
|
||||||
|
@ -197,6 +222,20 @@ async function writeExistingFiles(config) {
|
||||||
} else {
|
} else {
|
||||||
await fs.remove(upath.join(basedir, 'yarn.lock'));
|
await fs.remove(upath.join(basedir, 'yarn.lock'));
|
||||||
}
|
}
|
||||||
|
// TODO: Update the below with this once https://github.com/pnpm/pnpm/issues/992 is fixed
|
||||||
|
const pnpmBug992 = true;
|
||||||
|
// istanbul ignore next
|
||||||
|
if (
|
||||||
|
packageFile.shrinkwrapYaml &&
|
||||||
|
config.type !== 'lockFileMaintenance' &&
|
||||||
|
!pnpmBug992
|
||||||
|
) {
|
||||||
|
logger.debug(`Writing shrinkwrap.yaml to ${basedir}`);
|
||||||
|
const shrinkwrap = await platform.getFile(packageFile.shrinkwrapYaml);
|
||||||
|
await fs.outputFile(upath.join(basedir, 'shrinkwrap.yaml'), shrinkwrap);
|
||||||
|
} else {
|
||||||
|
await fs.remove(upath.join(basedir, 'shrinkwrap.yaml'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,5 +355,34 @@ async function getUpdatedLockFiles(config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const lockFileDir of dirs.shrinkwrapYamlDirs) {
|
||||||
|
logger.debug(`Generating shrinkwrap.yaml for ${lockFileDir}`);
|
||||||
|
const lockFileName = upath.join(lockFileDir, 'shrinkwrap.yaml');
|
||||||
|
const res = await pnpm.generateLockFile(
|
||||||
|
upath.join(config.tmpDir.path, lockFileDir)
|
||||||
|
);
|
||||||
|
if (res.error) {
|
||||||
|
lockFileErrors.push({
|
||||||
|
lockFile: lockFileName,
|
||||||
|
stderr: res.stderr,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const existingContent = await platform.getFile(
|
||||||
|
lockFileName,
|
||||||
|
config.parentBranch
|
||||||
|
);
|
||||||
|
if (res.lockFile !== existingContent) {
|
||||||
|
logger.debug('shrinkwrap.yaml needs updating');
|
||||||
|
updatedLockFiles.push({
|
||||||
|
name: lockFileName,
|
||||||
|
contents: res.lockFile,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.debug("shrinkwrap.yaml hasn't changed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { lockFileErrors, updatedLockFiles };
|
return { lockFileErrors, updatedLockFiles };
|
||||||
}
|
}
|
||||||
|
|
85
lib/workers/branch/pnpm.js
Normal file
85
lib/workers/branch/pnpm.js
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const upath = require('upath');
|
||||||
|
const { getInstalledPath } = require('get-installed-path');
|
||||||
|
const { exec } = require('child-process-promise');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generateLockFile,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function generateLockFile(tmpDir) {
|
||||||
|
logger.debug(`Spawning pnpm install to create ${tmpDir}/shrinkwrap.yaml`);
|
||||||
|
let lockFile = null;
|
||||||
|
let stdout;
|
||||||
|
let stderr;
|
||||||
|
try {
|
||||||
|
const startTime = process.hrtime();
|
||||||
|
let cmd;
|
||||||
|
try {
|
||||||
|
// See if renovate is installed locally
|
||||||
|
const installedPath = upath.join(
|
||||||
|
await getInstalledPath('pnpm', {
|
||||||
|
local: true,
|
||||||
|
}),
|
||||||
|
'lib/bin/pnpm.js'
|
||||||
|
);
|
||||||
|
cmd = `node ${installedPath}`;
|
||||||
|
} catch (localerr) {
|
||||||
|
logger.debug('No locally installed pnpm found');
|
||||||
|
// Look inside globally installed renovate
|
||||||
|
try {
|
||||||
|
const renovateLocation = await getInstalledPath('renovate');
|
||||||
|
const installedPath = upath.join(
|
||||||
|
await getInstalledPath('pnpm', {
|
||||||
|
local: true,
|
||||||
|
cwd: renovateLocation,
|
||||||
|
}),
|
||||||
|
'lib/bin/pnpm.js'
|
||||||
|
);
|
||||||
|
cmd = `node ${installedPath}`;
|
||||||
|
} catch (nestederr) {
|
||||||
|
logger.debug('Could not find globally nested pnpm');
|
||||||
|
// look for global pnpm
|
||||||
|
try {
|
||||||
|
const installedPath = upath.join(
|
||||||
|
await getInstalledPath('pnpm'),
|
||||||
|
'lib/bin/pnpm.js'
|
||||||
|
);
|
||||||
|
cmd = `node ${installedPath}`;
|
||||||
|
} catch (globalerr) {
|
||||||
|
logger.warn('Could not find globally installed pnpm');
|
||||||
|
cmd = 'pnpm';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.debug(`Using pnpm: ${cmd}`);
|
||||||
|
cmd += ' install';
|
||||||
|
cmd += ' --shrinkwrap-only';
|
||||||
|
cmd += ' --ignore-scripts';
|
||||||
|
cmd += ' --ignore-pnpmfile';
|
||||||
|
// TODO: Switch to native util.promisify once using only node 8
|
||||||
|
({ stdout, stderr } = await exec(cmd, {
|
||||||
|
cwd: tmpDir,
|
||||||
|
shell: true,
|
||||||
|
env: { NODE_ENV: 'dev', PATH: process.env.PATH },
|
||||||
|
}));
|
||||||
|
logger.debug(`pnpm stdout:\n${stdout}`);
|
||||||
|
logger.debug(`pnpm stderr:\n${stderr}`);
|
||||||
|
const duration = process.hrtime(startTime);
|
||||||
|
const seconds = Math.round(duration[0] + duration[1] / 1e9);
|
||||||
|
lockFile = await fs.readFile(upath.join(tmpDir, 'shrinkwrap.yaml'), 'utf8');
|
||||||
|
logger.info(
|
||||||
|
{ seconds, type: 'shrinkwrap.yaml', stdout, stderr },
|
||||||
|
'Generated lockfile'
|
||||||
|
);
|
||||||
|
} catch (err) /* istanbul ignore next */ {
|
||||||
|
logger.info(
|
||||||
|
{
|
||||||
|
err,
|
||||||
|
},
|
||||||
|
'pnpm install error'
|
||||||
|
);
|
||||||
|
return { error: true, stderr: err.stderr };
|
||||||
|
}
|
||||||
|
return { lockFile };
|
||||||
|
}
|
|
@ -71,6 +71,7 @@
|
||||||
"npm": "5.6.0",
|
"npm": "5.6.0",
|
||||||
"openpgp": "2.6.1",
|
"openpgp": "2.6.1",
|
||||||
"parse-link-header": "1.0.1",
|
"parse-link-header": "1.0.1",
|
||||||
|
"pnpm": "1.29.1",
|
||||||
"registry-auth-token": "3.3.1",
|
"registry-auth-token": "3.3.1",
|
||||||
"root-require": "0.3.1",
|
"root-require": "0.3.1",
|
||||||
"semver": "5.4.1",
|
"semver": "5.4.1",
|
||||||
|
|
|
@ -14,6 +14,7 @@ Array [
|
||||||
"npmrc": "npmrc",
|
"npmrc": "npmrc",
|
||||||
"packageFile": "package.json",
|
"packageFile": "package.json",
|
||||||
"packageLock": "package-lock.json",
|
"packageLock": "package-lock.json",
|
||||||
|
"shrinkwrapYaml": "shrinkwrap.yaml",
|
||||||
"yarnLock": "yarn.lock",
|
"yarnLock": "yarn.lock",
|
||||||
"yarnrc": "yarnrc",
|
"yarnrc": "yarnrc",
|
||||||
},
|
},
|
||||||
|
|
|
@ -71,6 +71,7 @@ describe('manager/resolve', () => {
|
||||||
platform.getFileList.mockReturnValueOnce([
|
platform.getFileList.mockReturnValueOnce([
|
||||||
'yarn.lock',
|
'yarn.lock',
|
||||||
'package-lock.json',
|
'package-lock.json',
|
||||||
|
'shrinkwrap.yaml',
|
||||||
]);
|
]);
|
||||||
platform.getFile.mockReturnValueOnce('{"name": "package.json"}');
|
platform.getFile.mockReturnValueOnce('{"name": "package.json"}');
|
||||||
platform.getFile.mockReturnValueOnce('npmrc');
|
platform.getFile.mockReturnValueOnce('npmrc');
|
||||||
|
|
|
@ -5,6 +5,9 @@ Object {
|
||||||
"packageLockFileDirs": Array [
|
"packageLockFileDirs": Array [
|
||||||
"backend",
|
"backend",
|
||||||
],
|
],
|
||||||
|
"shrinkwrapYamlDirs": Array [
|
||||||
|
"frontend",
|
||||||
|
],
|
||||||
"yarnLockFileDirs": Array [
|
"yarnLockFileDirs": Array [
|
||||||
".",
|
".",
|
||||||
],
|
],
|
||||||
|
@ -16,6 +19,9 @@ Object {
|
||||||
"packageLockFileDirs": Array [
|
"packageLockFileDirs": Array [
|
||||||
"backend",
|
"backend",
|
||||||
],
|
],
|
||||||
|
"shrinkwrapYamlDirs": Array [
|
||||||
|
"frontend",
|
||||||
|
],
|
||||||
"yarnLockFileDirs": Array [
|
"yarnLockFileDirs": Array [
|
||||||
".",
|
".",
|
||||||
],
|
],
|
||||||
|
@ -25,6 +31,7 @@ Object {
|
||||||
exports[`workers/branch/lock-files determineLockFileDirs returns root directory if using yarn workspaces 1`] = `
|
exports[`workers/branch/lock-files determineLockFileDirs returns root directory if using yarn workspaces 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"packageLockFileDirs": Array [],
|
"packageLockFileDirs": Array [],
|
||||||
|
"shrinkwrapYamlDirs": Array [],
|
||||||
"yarnLockFileDirs": Array [
|
"yarnLockFileDirs": Array [
|
||||||
".",
|
".",
|
||||||
],
|
],
|
||||||
|
|
|
@ -5,10 +5,12 @@ const upath = require('upath');
|
||||||
|
|
||||||
const npm = require('../../../lib/workers/branch/npm');
|
const npm = require('../../../lib/workers/branch/npm');
|
||||||
const yarn = require('../../../lib/workers/branch/yarn');
|
const yarn = require('../../../lib/workers/branch/yarn');
|
||||||
|
const pnpm = require('../../../lib/workers/branch/pnpm');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
hasPackageLock,
|
hasPackageLock,
|
||||||
hasYarnLock,
|
hasYarnLock,
|
||||||
|
hasShrinkwrapYaml,
|
||||||
determineLockFileDirs,
|
determineLockFileDirs,
|
||||||
writeExistingFiles,
|
writeExistingFiles,
|
||||||
writeUpdatedPackageFiles,
|
writeUpdatedPackageFiles,
|
||||||
|
@ -110,6 +112,53 @@ describe('workers/branch/lock-files', () => {
|
||||||
expect(e).toBeDefined();
|
expect(e).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('hasShrinkWrapYaml', () => {
|
||||||
|
let config;
|
||||||
|
beforeEach(() => {
|
||||||
|
config = {
|
||||||
|
...defaultConfig,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
it('returns true if found and true', () => {
|
||||||
|
config.packageFiles = [
|
||||||
|
{
|
||||||
|
packageFile: 'package.json',
|
||||||
|
shrinkwrapYaml: 'some shrinkwrap',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
expect(hasShrinkwrapYaml(config, 'package.json')).toBe(true);
|
||||||
|
});
|
||||||
|
it('returns false if found and false', () => {
|
||||||
|
config.packageFiles = [
|
||||||
|
{
|
||||||
|
packageFile: 'package.json',
|
||||||
|
shrinkwrapYaml: 'some shrinkwrap',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageFile: 'backend/package.json',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
expect(hasShrinkwrapYaml(config, 'backend/package.json')).toBe(false);
|
||||||
|
});
|
||||||
|
it('throws error if not found', () => {
|
||||||
|
config.packageFiles = [
|
||||||
|
{
|
||||||
|
packageFile: 'package.json',
|
||||||
|
shrinkwrapYaml: 'some package lock',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packageFile: 'backend/package.json',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let e;
|
||||||
|
try {
|
||||||
|
hasShrinkwrapYaml(config, 'frontend/package.json');
|
||||||
|
} catch (err) {
|
||||||
|
e = err;
|
||||||
|
}
|
||||||
|
expect(e).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('determineLockFileDirs', () => {
|
describe('determineLockFileDirs', () => {
|
||||||
let config;
|
let config;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -124,6 +173,10 @@ describe('workers/branch/lock-files', () => {
|
||||||
packageFile: 'backend/package.json',
|
packageFile: 'backend/package.json',
|
||||||
packageLock: 'some package lock',
|
packageLock: 'some package lock',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
packageFile: 'frontend/package.json',
|
||||||
|
shrinkwrapYaml: 'some package lock',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -143,6 +196,10 @@ describe('workers/branch/lock-files', () => {
|
||||||
name: 'backend/package.json',
|
name: 'backend/package.json',
|
||||||
contents: 'some contents',
|
contents: 'some contents',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'frontend/package.json',
|
||||||
|
contents: 'some contents',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const res = determineLockFileDirs(config);
|
const res = determineLockFileDirs(config);
|
||||||
expect(res).toMatchSnapshot();
|
expect(res).toMatchSnapshot();
|
||||||
|
@ -207,7 +264,7 @@ describe('workers/branch/lock-files', () => {
|
||||||
];
|
];
|
||||||
await writeExistingFiles(config);
|
await writeExistingFiles(config);
|
||||||
expect(fs.outputFile.mock.calls).toHaveLength(6);
|
expect(fs.outputFile.mock.calls).toHaveLength(6);
|
||||||
expect(fs.remove.mock.calls).toHaveLength(4);
|
expect(fs.remove.mock.calls).toHaveLength(6);
|
||||||
});
|
});
|
||||||
it('writes package.json of local lib', async () => {
|
it('writes package.json of local lib', async () => {
|
||||||
const renoPath = upath.join(__dirname, '../../../');
|
const renoPath = upath.join(__dirname, '../../../');
|
||||||
|
@ -230,7 +287,7 @@ describe('workers/branch/lock-files', () => {
|
||||||
platform.getFile.mockReturnValue('some lock file contents');
|
platform.getFile.mockReturnValue('some lock file contents');
|
||||||
await writeExistingFiles(config);
|
await writeExistingFiles(config);
|
||||||
expect(fs.outputFile.mock.calls).toHaveLength(4);
|
expect(fs.outputFile.mock.calls).toHaveLength(4);
|
||||||
expect(fs.remove.mock.calls).toHaveLength(0);
|
expect(fs.remove.mock.calls).toHaveLength(1);
|
||||||
});
|
});
|
||||||
it('Try to write package.json of local lib, but file not found', async () => {
|
it('Try to write package.json of local lib, but file not found', async () => {
|
||||||
const renoPath = upath.join(__dirname, '../../../');
|
const renoPath = upath.join(__dirname, '../../../');
|
||||||
|
@ -253,7 +310,7 @@ describe('workers/branch/lock-files', () => {
|
||||||
platform.getFile.mockReturnValue(null);
|
platform.getFile.mockReturnValue(null);
|
||||||
await writeExistingFiles(config);
|
await writeExistingFiles(config);
|
||||||
expect(fs.outputFile.mock.calls).toHaveLength(3);
|
expect(fs.outputFile.mock.calls).toHaveLength(3);
|
||||||
expect(fs.remove.mock.calls).toHaveLength(0);
|
expect(fs.remove.mock.calls).toHaveLength(1);
|
||||||
});
|
});
|
||||||
it('detect malicious intent (error config in package.json) local lib is not in the repo', async () => {
|
it('detect malicious intent (error config in package.json) local lib is not in the repo', async () => {
|
||||||
const renoPath = upath.join(__dirname, '../../../');
|
const renoPath = upath.join(__dirname, '../../../');
|
||||||
|
@ -276,7 +333,7 @@ describe('workers/branch/lock-files', () => {
|
||||||
platform.getFile.mockReturnValue(null);
|
platform.getFile.mockReturnValue(null);
|
||||||
await writeExistingFiles(config);
|
await writeExistingFiles(config);
|
||||||
expect(fs.outputFile.mock.calls).toHaveLength(3);
|
expect(fs.outputFile.mock.calls).toHaveLength(3);
|
||||||
expect(fs.remove.mock.calls).toHaveLength(0);
|
expect(fs.remove.mock.calls).toHaveLength(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('writeUpdatedPackageFiles', () => {
|
describe('writeUpdatedPackageFiles', () => {
|
||||||
|
@ -336,6 +393,10 @@ describe('workers/branch/lock-files', () => {
|
||||||
yarn.generateLockFile.mockReturnValue({
|
yarn.generateLockFile.mockReturnValue({
|
||||||
lockFile: 'some lock file contents',
|
lockFile: 'some lock file contents',
|
||||||
});
|
});
|
||||||
|
pnpm.generateLockFile = jest.fn();
|
||||||
|
pnpm.generateLockFile.mockReturnValue({
|
||||||
|
lockFile: 'some lock file contents',
|
||||||
|
});
|
||||||
lockFiles.determineLockFileDirs = jest.fn();
|
lockFiles.determineLockFileDirs = jest.fn();
|
||||||
});
|
});
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -353,6 +414,7 @@ describe('workers/branch/lock-files', () => {
|
||||||
lockFiles.determineLockFileDirs.mockReturnValueOnce({
|
lockFiles.determineLockFileDirs.mockReturnValueOnce({
|
||||||
packageLockFileDirs: [],
|
packageLockFileDirs: [],
|
||||||
yarnLockFileDirs: [],
|
yarnLockFileDirs: [],
|
||||||
|
shrinkwrapYamlDirs: [],
|
||||||
});
|
});
|
||||||
const res = await getUpdatedLockFiles(config);
|
const res = await getUpdatedLockFiles(config);
|
||||||
expect(res).toMatchSnapshot();
|
expect(res).toMatchSnapshot();
|
||||||
|
@ -363,6 +425,7 @@ describe('workers/branch/lock-files', () => {
|
||||||
lockFiles.determineLockFileDirs.mockReturnValueOnce({
|
lockFiles.determineLockFileDirs.mockReturnValueOnce({
|
||||||
packageLockFileDirs: ['a', 'b'],
|
packageLockFileDirs: ['a', 'b'],
|
||||||
yarnLockFileDirs: ['c', 'd'],
|
yarnLockFileDirs: ['c', 'd'],
|
||||||
|
shrinkwrapYamlDirs: ['e'],
|
||||||
});
|
});
|
||||||
const res = await getUpdatedLockFiles(config);
|
const res = await getUpdatedLockFiles(config);
|
||||||
expect(res).toMatchSnapshot();
|
expect(res).toMatchSnapshot();
|
||||||
|
@ -370,17 +433,19 @@ describe('workers/branch/lock-files', () => {
|
||||||
expect(res.updatedLockFiles).toHaveLength(0);
|
expect(res.updatedLockFiles).toHaveLength(0);
|
||||||
expect(npm.generateLockFile.mock.calls).toHaveLength(2);
|
expect(npm.generateLockFile.mock.calls).toHaveLength(2);
|
||||||
expect(yarn.generateLockFile.mock.calls).toHaveLength(2);
|
expect(yarn.generateLockFile.mock.calls).toHaveLength(2);
|
||||||
expect(platform.getFile.mock.calls).toHaveLength(4);
|
expect(platform.getFile.mock.calls).toHaveLength(5);
|
||||||
});
|
});
|
||||||
it('sets error if receiving null', async () => {
|
it('sets error if receiving null', async () => {
|
||||||
lockFiles.determineLockFileDirs.mockReturnValueOnce({
|
lockFiles.determineLockFileDirs.mockReturnValueOnce({
|
||||||
packageLockFileDirs: ['a', 'b'],
|
packageLockFileDirs: ['a', 'b'],
|
||||||
yarnLockFileDirs: ['c', 'd'],
|
yarnLockFileDirs: ['c', 'd'],
|
||||||
|
shrinkwrapYamlDirs: ['e'],
|
||||||
});
|
});
|
||||||
npm.generateLockFile.mockReturnValueOnce({ error: true });
|
npm.generateLockFile.mockReturnValueOnce({ error: true });
|
||||||
yarn.generateLockFile.mockReturnValueOnce({ error: true });
|
yarn.generateLockFile.mockReturnValueOnce({ error: true });
|
||||||
|
pnpm.generateLockFile.mockReturnValueOnce({ error: true });
|
||||||
const res = await getUpdatedLockFiles(config);
|
const res = await getUpdatedLockFiles(config);
|
||||||
expect(res.lockFileErrors).toHaveLength(2);
|
expect(res.lockFileErrors).toHaveLength(3);
|
||||||
expect(res.updatedLockFiles).toHaveLength(0);
|
expect(res.updatedLockFiles).toHaveLength(0);
|
||||||
expect(npm.generateLockFile.mock.calls).toHaveLength(2);
|
expect(npm.generateLockFile.mock.calls).toHaveLength(2);
|
||||||
expect(yarn.generateLockFile.mock.calls).toHaveLength(2);
|
expect(yarn.generateLockFile.mock.calls).toHaveLength(2);
|
||||||
|
@ -390,15 +455,17 @@ describe('workers/branch/lock-files', () => {
|
||||||
lockFiles.determineLockFileDirs.mockReturnValueOnce({
|
lockFiles.determineLockFileDirs.mockReturnValueOnce({
|
||||||
packageLockFileDirs: ['a', 'b'],
|
packageLockFileDirs: ['a', 'b'],
|
||||||
yarnLockFileDirs: ['c', 'd'],
|
yarnLockFileDirs: ['c', 'd'],
|
||||||
|
shrinkwrapYamlDirs: ['e'],
|
||||||
});
|
});
|
||||||
npm.generateLockFile.mockReturnValueOnce('some new lock file contents');
|
npm.generateLockFile.mockReturnValueOnce('some new lock file contents');
|
||||||
yarn.generateLockFile.mockReturnValueOnce('some new lock file contents');
|
yarn.generateLockFile.mockReturnValueOnce('some new lock file contents');
|
||||||
|
pnpm.generateLockFile.mockReturnValueOnce('some new lock file contents');
|
||||||
const res = await getUpdatedLockFiles(config);
|
const res = await getUpdatedLockFiles(config);
|
||||||
expect(res.lockFileErrors).toHaveLength(0);
|
expect(res.lockFileErrors).toHaveLength(0);
|
||||||
expect(res.updatedLockFiles).toHaveLength(2);
|
expect(res.updatedLockFiles).toHaveLength(3);
|
||||||
expect(npm.generateLockFile.mock.calls).toHaveLength(2);
|
expect(npm.generateLockFile.mock.calls).toHaveLength(2);
|
||||||
expect(yarn.generateLockFile.mock.calls).toHaveLength(2);
|
expect(yarn.generateLockFile.mock.calls).toHaveLength(2);
|
||||||
expect(platform.getFile.mock.calls).toHaveLength(4);
|
expect(platform.getFile.mock.calls).toHaveLength(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
95
test/workers/branch/pnpm.spec.js
Normal file
95
test/workers/branch/pnpm.spec.js
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
const pnpmHelper = require('../../../lib/workers/branch/pnpm');
|
||||||
|
|
||||||
|
const { getInstalledPath } = require('get-installed-path');
|
||||||
|
|
||||||
|
jest.mock('fs-extra');
|
||||||
|
jest.mock('child-process-promise');
|
||||||
|
jest.mock('get-installed-path');
|
||||||
|
|
||||||
|
getInstalledPath.mockImplementation(() => null);
|
||||||
|
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const { exec } = require('child-process-promise');
|
||||||
|
|
||||||
|
describe('generateLockFile', () => {
|
||||||
|
it('generates lock files', async () => {
|
||||||
|
getInstalledPath.mockReturnValueOnce('node_modules/pnpm');
|
||||||
|
exec.mockReturnValueOnce({
|
||||||
|
stdout: '',
|
||||||
|
stderror: '',
|
||||||
|
});
|
||||||
|
fs.readFile = jest.fn(() => 'package-lock-contents');
|
||||||
|
const res = await pnpmHelper.generateLockFile('some-dir');
|
||||||
|
expect(fs.readFile.mock.calls.length).toEqual(1);
|
||||||
|
expect(res.lockFile).toEqual('package-lock-contents');
|
||||||
|
});
|
||||||
|
it('catches errors', async () => {
|
||||||
|
getInstalledPath.mockReturnValueOnce('node_modules/pnpm');
|
||||||
|
exec.mockReturnValueOnce({
|
||||||
|
stdout: '',
|
||||||
|
stderror: 'some-error',
|
||||||
|
});
|
||||||
|
fs.readFile = jest.fn(() => {
|
||||||
|
throw new Error('not found');
|
||||||
|
});
|
||||||
|
const res = await pnpmHelper.generateLockFile('some-dir');
|
||||||
|
expect(fs.readFile.mock.calls.length).toEqual(1);
|
||||||
|
expect(res.error).toBe(true);
|
||||||
|
expect(res.lockFile).not.toBeDefined();
|
||||||
|
});
|
||||||
|
it('finds pnpm embedded in renovate', async () => {
|
||||||
|
getInstalledPath.mockImplementationOnce(() => {
|
||||||
|
throw new Error('not found');
|
||||||
|
});
|
||||||
|
getInstalledPath.mockImplementationOnce(() => '/node_modules/renovate');
|
||||||
|
getInstalledPath.mockImplementationOnce(
|
||||||
|
() => '/node_modules/renovate/node_modules/pnpm'
|
||||||
|
);
|
||||||
|
exec.mockReturnValueOnce({
|
||||||
|
stdout: '',
|
||||||
|
stderror: '',
|
||||||
|
});
|
||||||
|
fs.readFile = jest.fn(() => 'package-lock-contents');
|
||||||
|
const res = await pnpmHelper.generateLockFile('some-dir');
|
||||||
|
expect(fs.readFile.mock.calls.length).toEqual(1);
|
||||||
|
expect(res.lockFile).toEqual('package-lock-contents');
|
||||||
|
});
|
||||||
|
it('finds pnpm globally', async () => {
|
||||||
|
getInstalledPath.mockImplementationOnce(() => {
|
||||||
|
throw new Error('not found');
|
||||||
|
});
|
||||||
|
getInstalledPath.mockImplementationOnce(() => '/node_modules/renovate');
|
||||||
|
getInstalledPath.mockImplementationOnce(() => {
|
||||||
|
throw new Error('not found');
|
||||||
|
});
|
||||||
|
getInstalledPath.mockImplementationOnce(() => '/node_modules/pnpm');
|
||||||
|
exec.mockReturnValueOnce({
|
||||||
|
stdout: '',
|
||||||
|
stderror: '',
|
||||||
|
});
|
||||||
|
fs.readFile = jest.fn(() => 'package-lock-contents');
|
||||||
|
const res = await pnpmHelper.generateLockFile('some-dir');
|
||||||
|
expect(fs.readFile.mock.calls.length).toEqual(1);
|
||||||
|
expect(res.lockFile).toEqual('package-lock-contents');
|
||||||
|
});
|
||||||
|
it('uses fallback pnpm', async () => {
|
||||||
|
getInstalledPath.mockImplementationOnce(() => {
|
||||||
|
throw new Error('not found');
|
||||||
|
});
|
||||||
|
getInstalledPath.mockImplementationOnce(() => '/node_modules/renovate');
|
||||||
|
getInstalledPath.mockImplementationOnce(() => {
|
||||||
|
throw new Error('not found');
|
||||||
|
});
|
||||||
|
getInstalledPath.mockImplementationOnce(() => {
|
||||||
|
throw new Error('not found');
|
||||||
|
});
|
||||||
|
exec.mockReturnValueOnce({
|
||||||
|
stdout: '',
|
||||||
|
stderror: '',
|
||||||
|
});
|
||||||
|
fs.readFile = jest.fn(() => 'package-lock-contents');
|
||||||
|
const res = await pnpmHelper.generateLockFile('some-dir');
|
||||||
|
expect(fs.readFile.mock.calls.length).toEqual(1);
|
||||||
|
expect(res.lockFile).toEqual('package-lock-contents');
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue