Add npm preinstall check for npm version 6.9.0 or later

Older npm versions cause the npm-shrinkwrap.json file to be incorrectly
updated. This should avoid regression bugs related to issue #1332.
https://github.com/balena-io/balena-cli/issues/1332

Change-type: patch
Signed-off-by: Paulo Castro <paulo@balena.io>
This commit is contained in:
Paulo Castro 2019-10-17 13:14:31 +01:00
parent 09444f0cff
commit e7c89cf77c
4 changed files with 78 additions and 8 deletions

View File

@ -79,6 +79,7 @@ some additional development tools to be installed first:
* Node.js version 8, 10 or 12 (on Linux/Mac, [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md)
is recommended)
* npm version 6.9.0 or later
* Python 2.7
* g++ compiler
* make

View File

@ -0,0 +1,62 @@
#!/usr/bin/env node
'use strict';
/**
* Check that semver v1 is greater than or equal to semver v2.
*
* We don't `require('semver')` to allow this script to be run as a npm
* 'preinstall' hook, at which point no dependencies have been installed.
*/
function semverGte(v1, v2) {
let v1Array, v2Array; // number[]
try {
const [, major1, minor1, patch1] = /v?(\d+)\.(\d+).(\d+)/.exec(v1);
const [, major2, minor2, patch2] = /v?(\d+)\.(\d+).(\d+)/.exec(v2);
v1Array = [parseInt(major1), parseInt(minor1), parseInt(patch1)];
v2Array = [parseInt(major2), parseInt(minor2), parseInt(patch2)];
} catch (err) {
throw new Error(`Invalid semver versions: '${v1}' or '${v2}'`);
}
for (let i = 0; i < 3; i++) {
if (v1Array[i] < v2Array[i]) {
return false;
} else if (v1Array[i] > v2Array[i]) {
return true;
}
}
return true;
}
function _testSemverGet() {
const assert = require('assert').strict;
assert(semverGte('6.4.1', '6.4.1'));
assert(semverGte('6.4.1', 'v6.4.1'));
assert(semverGte('v6.4.1', '6.4.1'));
assert(semverGte('v6.4.1', 'v6.4.1'));
assert(semverGte('6.4.1', '6.4.0'));
assert(semverGte('6.4.1', '6.3.1'));
assert(semverGte('6.4.1', '5.4.1'));
assert(!semverGte('6.4.1', '6.4.2'));
assert(!semverGte('6.4.1', '6.5.1'));
assert(!semverGte('6.4.1', '7.4.1'));
assert(semverGte('v6.4.1', 'v4.0.0'));
assert(!semverGte('v6.4.1', 'v6.9.0'));
assert(!semverGte('v6.4.1', 'v7.0.0'));
}
function checkNpmVersion() {
const execSync = require('child_process').execSync;
const npmVersion = execSync('npm --version')
.toString()
.trim();
const requiredVersion = '6.9.0';
if (!semverGte(npmVersion, requiredVersion)) {
console.error(`\
Error: npm version '${npmVersion}' detected. Please upgrade to npm v${requiredVersion} or later
because of a bug affecting older versions in relation to the npm-shrinkwrap.json file.`);
process.exit(1);
}
}
checkNpmVersion();

View File

@ -39,6 +39,7 @@
]
},
"scripts": {
"preinstall": "node automation/check-npm-version.js",
"postinstall": "patch-package",
"prebuild": "rimraf build/ build-bin/",
"build": "npm run build:src",
@ -54,7 +55,7 @@
"test:fast": "npm run build:fast && npm run test",
"ci": "npm run test && catch-uncommitted",
"watch": "gulp watch",
"prettify": "prettier --write \"{lib,tests,automation,typings}/**/*.ts\" --config ./node_modules/resin-lint/config/.prettierrc",
"prettify": "prettier --write \"{lib,tests,automation,typings}/**/*.[tj]s\" --config ./node_modules/resin-lint/config/.prettierrc",
"lint": "resin-lint lib/ tests/ && resin-lint --typescript automation/ lib/ typings/ tests/",
"prepublishOnly": "npm run build"
},

View File

@ -48,7 +48,7 @@ index a9d4276..75c2f8b 100644
exports.default = PackWin;
const scripts = {
diff --git a/node_modules/@oclif/dev-cli/lib/tarballs/build.js b/node_modules/@oclif/dev-cli/lib/tarballs/build.js
index 3e613e0..129d041 100644
index 3e613e0..dd23903 100644
--- a/node_modules/@oclif/dev-cli/lib/tarballs/build.js
+++ b/node_modules/@oclif/dev-cli/lib/tarballs/build.js
@@ -17,8 +17,11 @@ const pack = async (from, to) => {
@ -64,7 +64,7 @@ index 3e613e0..129d041 100644
const packCLI = async () => {
const stdout = await qq.x.stdout('npm', ['pack', '--unsafe-perm'], { cwd: c.root });
return path.join(c.root, stdout.split('\n').pop());
@@ -34,6 +37,28 @@ async function build(c, options = {}) {
@@ -34,6 +37,34 @@ async function build(c, options = {}) {
await qq.mv(f, '.');
await qq.rm('package', tarball, 'bin/run.cmd');
};
@ -75,11 +75,17 @@ index 3e613e0..129d041 100644
+ const sources = [
+ 'bin', 'build', 'patches', 'typings', 'CHANGELOG.md', 'INSTALL.md',
+ 'LICENSE', 'package.json', 'npm-shrinkwrap.json', 'README.md',
+ 'TROUBLESHOOTING.md',
+ 'TROUBLESHOOTING.md', 'automation/check-npm-version.js',
+ ];
+ for (const source of sources) {
+ let destDir = ws;
+ const dirname = path.dirname(source);
+ if (dirname && dirname !== '.') {
+ destDir = path.join(ws, dirname);
+ qq.mkdirp(destDir);
+ }
+ console.log(`cp "${source}" -> "${ws}"`);
+ await qq.cp(path.join(c.root, source), ws);
+ await qq.cp(path.join(c.root, source), destDir);
+ }
+ // rename the original balena-cli ./bin/balena entry point for oclif compatibility
+ await qq.mv('bin/balena', 'bin/run');
@ -93,7 +99,7 @@ index 3e613e0..129d041 100644
const updatePJSON = async () => {
qq.cd(c.workspace());
const pjson = await qq.readJSON('package.json');
@@ -56,7 +81,13 @@ async function build(c, options = {}) {
@@ -56,7 +87,13 @@ async function build(c, options = {}) {
lockpath = qq.join(c.root, 'npm-shrinkwrap.json');
}
await qq.cp(lockpath, '.');
@ -108,7 +114,7 @@ index 3e613e0..129d041 100644
}
};
const buildTarget = async (target) => {
@@ -71,7 +102,8 @@ async function build(c, options = {}) {
@@ -71,7 +108,8 @@ async function build(c, options = {}) {
output: path.join(workspace, 'bin', 'node'),
platform: target.platform,
arch: target.arch,
@ -118,7 +124,7 @@ index 3e613e0..129d041 100644
});
if (options.pack === false)
return;
@@ -124,7 +156,8 @@ async function build(c, options = {}) {
@@ -124,7 +162,8 @@ async function build(c, options = {}) {
await qq.writeJSON(c.dist(config.s3Key('manifest')), manifest);
};
log_1.log(`gathering workspace for ${config.bin} to ${c.workspace()}`);