diff --git a/INSTALL.md b/INSTALL.md index 24ec984e..6d3354e2 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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 diff --git a/automation/check-npm-version.js b/automation/check-npm-version.js new file mode 100644 index 00000000..e5200a08 --- /dev/null +++ b/automation/check-npm-version.js @@ -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(); diff --git a/package.json b/package.json index 85c81fb8..5535f2bf 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/patches/@oclif+dev-cli+1.22.0.patch b/patches/@oclif+dev-cli+1.22.0.patch index 6c84a29b..47715957 100644 --- a/patches/@oclif+dev-cli+1.22.0.patch +++ b/patches/@oclif+dev-cli+1.22.0.patch @@ -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()}`);