diff --git a/README.md b/README.md index 51a682af..12371f1a 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ For syntax, see [lookup.json](./lib/lookup.json), the available attributes are: "head": true Use the head of the default branch "prefix": "v" Specify the prefix used in the module version. "flaky": true Ignore failures -"skip": true Completely skip the module +"skip": true Completely skip the module. May also be a condition (platform, arch, distro, semver range, or an array/object of these, eg "win32" or ["aix", ">=24"]) to skip only when it matches "expectFail" Expect the module to fail, error if it passes "repo": "https://github.com/pugjs/jade" - Use a different github repo "stripAnsi": true Strip ansi data from output stream of npm @@ -140,6 +140,8 @@ For syntax, see [lookup.json](./lib/lookup.json), the available attributes are: "install": ["install", "--param1", "--param2"] - Array of command line parameters passed to `npm` or `yarn` or `pnpm` as install arguments "maintainers": ["user1", "user2"] - List of module maintainers to be contacted with issues "scripts": ["script1", "script2"] - List of scripts from package.json to run instead of 'test' + An entry may also be an object, eg `{ "script": "script2", "skip": "win32" }`, + where the optional "skip" takes the same values as the top-level "skip"; if every entry is skipped, use the top-level "skip" instead "tags": ["tag1", "tag2"] Specify which tags apply to the module "useGitClone": true Use a shallow git clone instead of downloading the module "submodules": ["path/to/submodule"] Initialize only the listed submodules when using a git clone diff --git a/lib/lookup.js b/lib/lookup.js index 94434888..0a608b2a 100644 --- a/lib/lookup.js +++ b/lib/lookup.js @@ -176,7 +176,29 @@ export function lookup(context) { `${context.module.name} lookup-scripts`, rep.scripts ); - context.module.scripts = rep.scripts; + const scripts = rep.scripts + .map((script) => { + if (typeof script === 'string') { + return script; + } + if ( + !script || + typeof script !== 'object' || + typeof script.script !== 'string' + ) { + throw new TypeError( + `${detail.name}: each "scripts" entry must be a string or an object with a string "script" property` + ); + } + return defaultMatcher.isMatch(script.skip) ? null : script.script; + }) + .filter((script) => script !== null); + if (scripts.length === 0) { + throw new Error( + `${detail.name}: every "scripts" entry is skipped; use the top-level "skip" instead` + ); + } + context.module.scripts = scripts; } if (rep.tags) { context.module.tags = rep.tags; diff --git a/lib/lookup.json b/lib/lookup.json index 906c2c85..d4a97032 100644 --- a/lib/lookup.json +++ b/lib/lookup.json @@ -329,7 +329,7 @@ "resolve": { "prefix": "v", "maintainers": "ljharb", - "scripts": ["tests-only", "posttest"] + "scripts": ["tests-only", { "script": "posttest", "skip": "win32" }] }, "rewire": { "prefix": "v", diff --git a/test/fixtures/custom-lookup-scripts-skip-all.json b/test/fixtures/custom-lookup-scripts-skip-all.json new file mode 100644 index 00000000..af89ca4f --- /dev/null +++ b/test/fixtures/custom-lookup-scripts-skip-all.json @@ -0,0 +1,6 @@ +{ + "omg-i-pass-with-scripts": { + "prefix": "v", + "scripts": [{ "script": "test", "skip": true }] + } +} diff --git a/test/fixtures/custom-lookup-scripts-skip-invalid.json b/test/fixtures/custom-lookup-scripts-skip-invalid.json new file mode 100644 index 00000000..81b887ff --- /dev/null +++ b/test/fixtures/custom-lookup-scripts-skip-invalid.json @@ -0,0 +1,6 @@ +{ + "omg-i-pass-with-scripts": { + "prefix": "v", + "scripts": ["test", { "skip": "win32" }] + } +} diff --git a/test/fixtures/custom-lookup-scripts-skip.json b/test/fixtures/custom-lookup-scripts-skip.json new file mode 100644 index 00000000..9764a83e --- /dev/null +++ b/test/fixtures/custom-lookup-scripts-skip.json @@ -0,0 +1,12 @@ +{ + "omg-i-pass-with-scripts": { + "prefix": "v", + "scripts": [ + "test-build", + { "script": "test" }, + { "script": "test-skipped", "skip": true }, + { "script": "test-not-skipped", "skip": false }, + { "script": "test-skipped-condition", "skip": ">=0" } + ] + } +} diff --git a/test/test-lookup.js b/test/test-lookup.js index 3aa0e217..9f7523e7 100644 --- a/test/test-lookup.js +++ b/test/test-lookup.js @@ -244,6 +244,79 @@ test('lookup: module in table with scripts', (t) => { t.end(); }); +test('lookup: module in table with conditionally skipped scripts', (t) => { + t.plan(1); + const context = { + module: parsePackageArg('omg-i-pass-with-scripts'), + meta: createFakeMeta({ + name: 'omg-i-pass-with-scripts', + repository: { + url: 'git+https://github.com/nodejs/citgm' + } + }), + options: { + lookup: 'test/fixtures/custom-lookup-scripts-skip.json' + }, + emit: function () {} + }; + + lookup(context); + t.strictSame( + context.module.scripts, + ['test-build', 'test', 'test-not-skipped'], + 'lookup should omit scripts whose skip condition matches' + ); + t.end(); +}); + +test('lookup: module in table with invalid scripts entry', (t) => { + t.plan(1); + const context = { + module: parsePackageArg('omg-i-pass-with-scripts'), + meta: createFakeMeta({ + name: 'omg-i-pass-with-scripts', + repository: { + url: 'git+https://github.com/nodejs/citgm' + } + }), + options: { + lookup: 'test/fixtures/custom-lookup-scripts-skip-invalid.json' + }, + emit: function () {} + }; + + t.throws( + () => lookup(context), + { name: 'TypeError', message: /must be a string or an object/ }, + 'lookup should throw on a scripts entry without a string script' + ); + t.end(); +}); + +test('lookup: module in table with every script skipped', (t) => { + t.plan(1); + const context = { + module: parsePackageArg('omg-i-pass-with-scripts'), + meta: createFakeMeta({ + name: 'omg-i-pass-with-scripts', + repository: { + url: 'git+https://github.com/nodejs/citgm' + } + }), + options: { + lookup: 'test/fixtures/custom-lookup-scripts-skip-all.json' + }, + emit: function () {} + }; + + t.throws( + () => lookup(context), + /every "scripts" entry is skipped/, + 'lookup should throw when every script is skipped' + ); + t.end(); +}); + test('lookup: module in table with useGitClone', (t) => { t.plan(3); const context = {