From 66ef76539c5ee2f015b130f53aa2f325f1bf3f84 Mon Sep 17 00:00:00 2001 From: Yuku Kotani Date: Sat, 9 Sep 2023 04:31:57 +0900 Subject: [PATCH] feat: Add `--cpu` and `--os` option to override platform specific install (#6776) --- lib/commands/install.js | 2 + .../test/lib/commands/config.js.test.cjs | 4 + tap-snapshots/test/lib/docs.js.test.cjs | 34 ++++++++- workspaces/arborist/lib/arborist/reify.js | 4 +- .../test/arborist/reify.js.test.cjs | 75 +++++++++++++++++++ workspaces/arborist/test/arborist/reify.js | 12 +++ .../platform-specifying-test-package.json | 3 + .../platform-specifying-test-package.min.json | 3 + .../config/lib/definitions/definitions.js | 22 ++++++ .../test/type-description.js.test.cjs | 8 ++ 10 files changed, 163 insertions(+), 4 deletions(-) diff --git a/lib/commands/install.js b/lib/commands/install.js index 75f0e2f175b61..3983c8d26c841 100644 --- a/lib/commands/install.js +++ b/lib/commands/install.js @@ -34,6 +34,8 @@ class Install extends ArboristWorkspaceCmd { 'bin-links', 'fund', 'dry-run', + 'cpu', + 'os', ...super.params, ] diff --git a/tap-snapshots/test/lib/commands/config.js.test.cjs b/tap-snapshots/test/lib/commands/config.js.test.cjs index 7c590b1cf3fb4..7a005e4d819c3 100644 --- a/tap-snapshots/test/lib/commands/config.js.test.cjs +++ b/tap-snapshots/test/lib/commands/config.js.test.cjs @@ -34,6 +34,8 @@ exports[`test/lib/commands/config.js TAP config list --json > output matches sna "cidr": null, "color": true, "commit-hooks": true, + "cpu": null, + "os": null, "depth": null, "description": true, "dev": false, @@ -191,6 +193,7 @@ ci-name = null cidr = null color = true commit-hooks = true +cpu = null depth = null description = true dev = false @@ -263,6 +266,7 @@ omit = [] omit-lockfile-registry-resolved = false only = null optional = null +os = null otp = null pack-destination = "." package = [] diff --git a/tap-snapshots/test/lib/docs.js.test.cjs b/tap-snapshots/test/lib/docs.js.test.cjs index 99ddff6e150b3..c6fc0bf5b5035 100644 --- a/tap-snapshots/test/lib/docs.js.test.cjs +++ b/tap-snapshots/test/lib/docs.js.test.cjs @@ -392,6 +392,16 @@ Run git commit hooks when using the \`npm version\` command. +#### \`cpu\` + +* Default: null +* Type: null or String + +Override CPU architecture of native modules to install. Acceptable values +are same as \`cpu\` field of package.json, which comes from \`process.arch\`. + + + #### \`depth\` * Default: \`Infinity\` if \`--all\` is set, otherwise \`1\` @@ -1085,6 +1095,16 @@ time. +#### \`os\` + +* Default: null +* Type: null or String + +Override OS of native modules to install. Acceptable values are same as \`os\` +field of package.json, which comes from \`process.platform\`. + + + #### \`otp\` * Default: null @@ -2035,6 +2055,8 @@ Array [ "cidr", "color", "commit-hooks", + "cpu", + "os", "depth", "description", "dev", @@ -2190,6 +2212,8 @@ Array [ "cidr", "color", "commit-hooks", + "cpu", + "os", "depth", "description", "dev", @@ -2346,6 +2370,7 @@ Object { "ciName": "{ci}", "color": false, "commitHooks": true, + "cpu": null, "defaultTag": "latest", "depth": null, "diff": Array [], @@ -2395,6 +2420,7 @@ Object { "offline": false, "omit": Array [], "omitLockfileRegistryResolved": false, + "os": null, "otp": null, "package": Array [], "packageLock": true, @@ -3204,7 +3230,7 @@ Options: [--global-style] [--omit [--omit ...]] [--strict-peer-deps] [--prefer-dedupe] [--no-package-lock] [--package-lock-only] [--foreground-scripts] [--ignore-scripts] [--no-audit] [--no-bin-links] -[--no-fund] [--dry-run] +[--no-fund] [--dry-run] [--cpu ] [--os ] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] [--include-workspace-root] [--install-links] @@ -3235,6 +3261,8 @@ aliases: add, i, in, ins, inst, insta, instal, isnt, isnta, isntal, isntall #### \`bin-links\` #### \`fund\` #### \`dry-run\` +#### \`cpu\` +#### \`os\` #### \`workspace\` #### \`workspaces\` #### \`include-workspace-root\` @@ -3295,7 +3323,7 @@ Options: [--global-style] [--omit [--omit ...]] [--strict-peer-deps] [--prefer-dedupe] [--no-package-lock] [--package-lock-only] [--foreground-scripts] [--ignore-scripts] [--no-audit] [--no-bin-links] -[--no-fund] [--dry-run] +[--no-fund] [--dry-run] [--cpu ] [--os ] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] [--include-workspace-root] [--install-links] @@ -3326,6 +3354,8 @@ alias: it #### \`bin-links\` #### \`fund\` #### \`dry-run\` +#### \`cpu\` +#### \`os\` #### \`workspace\` #### \`workspaces\` #### \`include-workspace-root\` diff --git a/workspaces/arborist/lib/arborist/reify.js b/workspaces/arborist/lib/arborist/reify.js index 020038b409bb1..0981afdae6ece 100644 --- a/workspaces/arborist/lib/arborist/reify.js +++ b/workspaces/arborist/lib/arborist/reify.js @@ -628,7 +628,7 @@ module.exports = cls => class Reifier extends cls { process.emit('time', timer) this.addTracker('reify', node.name, node.location) - const { npmVersion, nodeVersion } = this.options + const { npmVersion, nodeVersion, cpu, os } = this.options const p = Promise.resolve().then(async () => { // when we reify an optional node, check the engine and platform // first. be sure to ignore the --force and --engine-strict flags, @@ -638,7 +638,7 @@ module.exports = cls => class Reifier extends cls { // eslint-disable-next-line promise/always-return if (node.optional) { checkEngine(node.package, npmVersion, nodeVersion, false) - checkPlatform(node.package, false) + checkPlatform(node.package, false, { cpu, os }) } await this[_checkBins](node) await this[_extractOrLink](node) diff --git a/workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs b/workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs index 2a92b735c3d08..dcc3692a8965e 100644 --- a/workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs +++ b/workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs @@ -3161,6 +3161,44 @@ ArboristNode { } ` +exports[`test/arborist/reify.js TAP fail to install optional deps with matched os and mismatched cpu with os and cpu options > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "platform-specifying-test-package" => EdgeOut { + "name": "platform-specifying-test-package", + "spec": "1.0.0", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-fail-to-install-optional-deps-with-matched-os-and-mismatched-cpu-with-os-and-cpu-options", + "packageName": "platform-test", + "path": "{CWD}/test/arborist/tap-testdir-reify-fail-to-install-optional-deps-with-matched-os-and-mismatched-cpu-with-os-and-cpu-options", + "version": "1.0.0", +} +` + +exports[`test/arborist/reify.js TAP fail to install optional deps with mismatched os and matched cpu with os and cpu options > expect resolving Promise 1`] = ` +ArboristNode { + "edgesOut": Map { + "platform-specifying-test-package" => EdgeOut { + "name": "platform-specifying-test-package", + "spec": "1.0.0", + "to": null, + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-fail-to-install-optional-deps-with-mismatched-os-and-matched-cpu-with-os-and-cpu-options", + "packageName": "platform-test", + "path": "{CWD}/test/arborist/tap-testdir-reify-fail-to-install-optional-deps-with-mismatched-os-and-matched-cpu-with-os-and-cpu-options", + "version": "1.0.0", +} +` + exports[`test/arborist/reify.js TAP failing script means install failure, unless ignoreScripts prod-dep-allinstall-fail --ignore-scripts > expect resolving Promise 1`] = ` ArboristNode { "children": Map { @@ -32993,6 +33031,43 @@ exports[`test/arborist/reify.js TAP store files with a custom indenting > must m ` +exports[`test/arborist/reify.js TAP success to install optional deps with matched platform specifications with os and cpu options > expect resolving Promise 1`] = ` +ArboristNode { + "children": Map { + "platform-specifying-test-package" => ArboristNode { + "edgesIn": Set { + EdgeIn { + "from": "", + "name": "platform-specifying-test-package", + "spec": "1.0.0", + "type": "optional", + }, + }, + "location": "node_modules/platform-specifying-test-package", + "name": "platform-specifying-test-package", + "optional": true, + "path": "{CWD}/test/arborist/tap-testdir-reify-success-to-install-optional-deps-with-matched-platform-specifications-with-os-and-cpu-options/node_modules/platform-specifying-test-package", + "resolved": "https://registry.npmjs.org/platform-specifying-test-package/-/platform-specifying-test-package-1.0.0.tgz", + "version": "1.0.0", + }, + }, + "edgesOut": Map { + "platform-specifying-test-package" => EdgeOut { + "name": "platform-specifying-test-package", + "spec": "1.0.0", + "to": "node_modules/platform-specifying-test-package", + "type": "optional", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-reify-success-to-install-optional-deps-with-matched-platform-specifications-with-os-and-cpu-options", + "packageName": "platform-test", + "path": "{CWD}/test/arborist/tap-testdir-reify-success-to-install-optional-deps-with-matched-platform-specifications-with-os-and-cpu-options", + "version": "1.0.0", +} +` + exports[`test/arborist/reify.js TAP tarball deps with transitive tarball deps > expect resolving Promise 1`] = ` ArboristNode { "children": Map { diff --git a/workspaces/arborist/test/arborist/reify.js b/workspaces/arborist/test/arborist/reify.js index 1f8a0d6310a4e..be55575b5e9d0 100644 --- a/workspaces/arborist/test/arborist/reify.js +++ b/workspaces/arborist/test/arborist/reify.js @@ -464,6 +464,18 @@ t.test('still do not install optional deps with mismatched platform specificatio t.test('fail to install deps with mismatched platform specifications', t => t.rejects(printReified(fixture(t, 'platform-specification')), { code: 'EBADPLATFORM' })) +t.test('success to install optional deps with matched platform specifications with os and cpu options', t => + t.resolveMatchSnapshot(printReified( + fixture(t, 'optional-platform-specification'), { os: 'not-your-os', cpu: 'not-your-cpu' }))) + +t.test('fail to install optional deps with matched os and mismatched cpu with os and cpu options', t => + t.resolveMatchSnapshot(printReified( + fixture(t, 'optional-platform-specification'), { os: 'not-your-os', cpu: 'another-cpu' }))) + +t.test('fail to install optional deps with mismatched os and matched cpu with os and cpu options', t => + t.resolveMatchSnapshot(printReified( + fixture(t, 'optional-platform-specification'), { os: 'another-os', cpu: 'not-your-cpu' }))) + t.test('dry run, do not get anything wet', async t => { const cases = [ 'shrinkwrapped-dep-with-lock-empty', diff --git a/workspaces/arborist/test/fixtures/registry-mocks/content/platform-specifying-test-package.json b/workspaces/arborist/test/fixtures/registry-mocks/content/platform-specifying-test-package.json index 111a0369f0c34..5d4d8ccc7bd58 100644 --- a/workspaces/arborist/test/fixtures/registry-mocks/content/platform-specifying-test-package.json +++ b/workspaces/arborist/test/fixtures/registry-mocks/content/platform-specifying-test-package.json @@ -21,6 +21,9 @@ "os": [ "not-your-os" ], + "cpu": [ + "not-your-cpu" + ], "_id": "platform-specifying-test-package@1.0.0", "_nodeVersion": "12.18.4", "_npmVersion": "6.14.6", diff --git a/workspaces/arborist/test/fixtures/registry-mocks/content/platform-specifying-test-package.min.json b/workspaces/arborist/test/fixtures/registry-mocks/content/platform-specifying-test-package.min.json index 24229a4c86807..a13d09372b177 100644 --- a/workspaces/arborist/test/fixtures/registry-mocks/content/platform-specifying-test-package.min.json +++ b/workspaces/arborist/test/fixtures/registry-mocks/content/platform-specifying-test-package.min.json @@ -15,6 +15,9 @@ }, "os": [ "not-your-os" + ], + "cpu": [ + "not-your-cpu" ] } }, diff --git a/workspaces/config/lib/definitions/definitions.js b/workspaces/config/lib/definitions/definitions.js index fe5cafa1922d9..4c6a1cb551dc9 100644 --- a/workspaces/config/lib/definitions/definitions.js +++ b/workspaces/config/lib/definitions/definitions.js @@ -490,6 +490,28 @@ define('commit-hooks', { flatten, }) +define('cpu', { + default: null, + type: [null, String], + description: ` + Override CPU architecture of native modules to install. + Acceptable values are same as \`cpu\` field of package.json, + which comes from \`process.arch\`. + `, + flatten, +}) + +define('os', { + default: null, + type: [null, String], + description: ` + Override OS of native modules to install. + Acceptable values are same as \`os\` field of package.json, + which comes from \`process.platform\`. + `, + flatten, +}) + define('depth', { default: null, defaultDescription: ` diff --git a/workspaces/config/tap-snapshots/test/type-description.js.test.cjs b/workspaces/config/tap-snapshots/test/type-description.js.test.cjs index 5157d6708b0a4..2b3bc718e2795 100644 --- a/workspaces/config/tap-snapshots/test/type-description.js.test.cjs +++ b/workspaces/config/tap-snapshots/test/type-description.js.test.cjs @@ -95,6 +95,10 @@ Object { "commit-hooks": Array [ "boolean value (true or false)", ], + "cpu": Array [ + null, + Function String(), + ], "depth": Array [ null, "numeric value", @@ -339,6 +343,10 @@ Object { null, "boolean value (true or false)", ], + "os": Array [ + null, + Function String(), + ], "otp": Array [ null, Function String(),