diff --git a/.gitignore b/.gitignore index c7648ae57..d0f9317f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,10 @@ coverage -node_modules +node_modules/* .DS_Store output + +#we must commit this single package to the repo as ncc cannot bundle it and the dist bundle is configure to use run-script as external package +#yes, below looks strange but this is how it is done with git +!node_modules/@npmcli/ +node_modules/@npmcli/* +!node_modules/@npmcli/run-script/ \ No newline at end of file diff --git a/node_modules/@npmcli/run-script/LICENSE b/node_modules/@npmcli/run-script/LICENSE new file mode 100644 index 000000000..19cec97b1 --- /dev/null +++ b/node_modules/@npmcli/run-script/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) npm, Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/@npmcli/run-script/README.md b/node_modules/@npmcli/run-script/README.md new file mode 100644 index 000000000..ff8f5d354 --- /dev/null +++ b/node_modules/@npmcli/run-script/README.md @@ -0,0 +1,149 @@ +# @npmcli/run-script + +Run a lifecycle script for a package (descendant of npm-lifecycle) + +## USAGE + +```js +const runScript = require('@npmcli/run-script') + +runScript({ + // required, the script to run + event: 'install', + + // extra args to pass to the command, defaults to [] + args: [], + + // required, the folder where the package lives + path: '/path/to/package/folder', + + // optional, defaults to /bin/sh on unix, or cmd.exe on windows + scriptShell: '/bin/bash', + + // optional, defaults to false + // return stdout and stderr as strings rather than buffers + stdioString: true, + + // optional, additional environment variables to add + // note that process.env IS inherited by default + // Always set: + // - npm_package_json The package.json file in the folder + // - npm_lifecycle_event The event that this is being run for + // - npm_lifecycle_script The script being run + // The fields described in https://github.com/npm/rfcs/pull/183 + env: { + npm_package_from: 'foo@bar', + npm_package_resolved: 'https://registry.npmjs.org/foo/-/foo-1.2.3.tgz', + npm_package_integrity: 'sha512-foobarbaz', + }, + + // defaults to 'pipe'. Can also pass an array like you would to node's + // exec or spawn functions. Note that if it's anything other than + // 'pipe' then the stdout/stderr values on the result will be missing. + // npm cli sets this to 'inherit' for explicit run-scripts (test, etc.) + // but leaves it as 'pipe' for install scripts that run in parallel. + stdio: 'inherit', + + // print the package id and script, and the command to be run, like: + // > somepackage@1.2.3 postinstall + // > make all-the-things + // Defaults true when stdio:'inherit', otherwise suppressed + banner: true, +}) + .then(({ code, signal, stdout, stderr, pkgid, path, event, script }) => { + // do something with the results + }) + .catch(er => { + // command did not work. + // er is decorated with: + // - code + // - signal + // - stdout + // - stderr + // - path + // - pkgid (name@version string) + // - event + // - script + }) +``` + +## API + +Call the exported `runScript` function with an options object. + +Returns a promise that resolves to the result of the execution. Promise +rejects if the execution fails (exits non-zero) or has any other error. +Rejected errors are decorated with the same values as the result object. + +If the stdio options mean that it'll have a piped stdin, then the stdin is +ended immediately on the child process. If stdin is shared with the parent +terminal, then it is up to the user to end it, of course. + +### Results + +- `code` Process exit code +- `signal` Process exit signal +- `stdout` stdout data (Buffer, or String when `stdioString` set to true) +- `stderr` stderr data (Buffer, or String when `stdioString` set to true) +- `path` Path to the package executing its script +- `event` Lifecycle event being run +- `script` Command being run + +### Options + +- `path` Required. The path to the package having its script run. +- `event` Required. The event being executed. +- `args` Optional, default `[]`. Extra arguments to pass to the script. +- `env` Optional, object of fields to add to the environment of the + subprocess. Note that process.env IS inherited by default These are + always set: + - `npm_package_json` The package.json file in the folder + - `npm_lifecycle_event` The event that this is being run for + - `npm_lifecycle_script` The script being run + - The `package.json` fields described in + [RFC183](https://github.com/npm/rfcs/pull/183/files). +- `scriptShell` Optional, defaults to `/bin/sh` on Unix, defaults to + `env.ComSpec` or `cmd` on Windows. Custom script to use to execute the + command. +- `stdio` Optional, defaults to `'pipe'`. The same as the `stdio` argument + passed to `child_process` functions in Node.js. Note that if a stdio + output is set to anything other than `pipe`, it will not be present in + the result/error object. +- `cmd` Optional. Override the script from the `package.json` with + something else, which will be run in an otherwise matching environment. +- `stdioString` Optional, defaults to `false`. Return string values for + `stderr` and `stdout` rather than Buffers. +- `banner` Optional, defaults to `true`. If the `stdio` option is set to + `'inherit'`, then print a banner with the package name and version, event + name, and script command to be run. Set explicitly to `false` to disable + for inherited stdio. + +Note that this does _not_ run pre-event and post-event scripts. The +caller has to manage that process themselves. + +## Differences from [npm-lifecycle](https://github.com/npm/npm-lifecycle) + +This is an implementation to satisfy [RFC +90](https://github.com/npm/rfcs/pull/90), [RFC +77](https://github.com/npm/rfcs/pull/77), and [RFC +73](https://github.com/npm/rfcs/pull/73). + +Apart from those behavior changes in npm v7, this is also just refresh of +the codebase, with modern coding techniques and better test coverage. + +Functionally, this means: + +- Output is not dumped to the top level process's stdio by default. +- Less stuff is put into the environment. +- It is not opinionated about logging. (So, at least with the logging + framework in npm v7.0 and before, the caller has to call + `log.disableProgress()` and `log.enableProgress()` at the appropriate + times, if necessary.) +- The directory containing the `node` executable is _never_ added to the + `PATH` environment variable. (Ie, `--scripts-prepend-node-path` is + effectively always set to `false`.) Doing so causes more unintended side + effects than it ever prevented. +- Hook scripts are not run by this module. If the caller wishes to run + hook scripts, then they can override the default package script with an + explicit `cmd` option pointing to the `node_modules/.hook/${event}` + script. diff --git a/node_modules/@npmcli/run-script/lib/is-server-package.js b/node_modules/@npmcli/run-script/lib/is-server-package.js new file mode 100644 index 000000000..d16862324 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/is-server-package.js @@ -0,0 +1,12 @@ +const util = require('util') +const fs = require('fs') +const { stat } = fs.promises || { stat: util.promisify(fs.stat) } +const { resolve } = require('path') +module.exports = async path => { + try { + const st = await stat(resolve(path, 'server.js')) + return st.isFile() + } catch (er) { + return false + } +} diff --git a/node_modules/@npmcli/run-script/lib/is-windows.js b/node_modules/@npmcli/run-script/lib/is-windows.js new file mode 100644 index 000000000..651917e6a --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/is-windows.js @@ -0,0 +1,2 @@ +const platform = process.env.__FAKE_TESTING_PLATFORM__ || process.platform +module.exports = platform === 'win32' diff --git a/node_modules/@npmcli/run-script/lib/make-spawn-args.js b/node_modules/@npmcli/run-script/lib/make-spawn-args.js new file mode 100644 index 000000000..8f299954a --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/make-spawn-args.js @@ -0,0 +1,40 @@ +/* eslint camelcase: "off" */ +const isWindows = require('./is-windows.js') +const setPATH = require('./set-path.js') +const {resolve} = require('path') +const npm_config_node_gyp = require.resolve('node-gyp/bin/node-gyp.js') + +const makeSpawnArgs = options => { + const { + event, + path, + scriptShell = isWindows ? process.env.ComSpec || 'cmd' : 'sh', + env = {}, + stdio, + cmd, + stdioString = false, + } = options + + const isCmd = /(?:^|\\)cmd(?:\.exe)?$/i.test(scriptShell) + const args = isCmd ? ['/d', '/s', '/c', cmd] : ['-c', cmd] + + const spawnOpts = { + env: setPATH(path, { + // we need to at least save the PATH environment var + ...process.env, + ...env, + npm_package_json: resolve(path, 'package.json'), + npm_lifecycle_event: event, + npm_lifecycle_script: cmd, + npm_config_node_gyp, + }), + stdioString, + stdio, + cwd: path, + ...(isCmd ? { windowsVerbatimArguments: true } : {}), + } + + return [scriptShell, args, spawnOpts] +} + +module.exports = makeSpawnArgs diff --git a/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp b/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp new file mode 100755 index 000000000..5bec64d96 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +node "$npm_config_node_gyp" "$@" diff --git a/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp.cmd b/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp.cmd new file mode 100755 index 000000000..4c6987ac9 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp.cmd @@ -0,0 +1 @@ +@node "%npm_config_node_gyp%" %* diff --git a/node_modules/@npmcli/run-script/lib/package-envs.js b/node_modules/@npmcli/run-script/lib/package-envs.js new file mode 100644 index 000000000..47791fb99 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/package-envs.js @@ -0,0 +1,25 @@ +// https://github.com/npm/rfcs/pull/183 + +const envVal = val => Array.isArray(val) ? val.map(v => envVal(v)).join('\n\n') + : val === null || val === false ? '' + : String(val) + +const packageEnvs = (env, vals, prefix) => { + for (const [key, val] of Object.entries(vals)) { + if (val === undefined) + continue + else if (val && !Array.isArray(val) && typeof val === 'object') + packageEnvs(env, val, `${prefix}${key}_`) + else + env[`${prefix}${key}`] = envVal(val) + } + return env +} + +module.exports = (env, pkg) => packageEnvs({ ...env }, { + name: pkg.name, + version: pkg.version, + config: pkg.config, + engines: pkg.engines, + bin: pkg.bin, +}, 'npm_package_') diff --git a/node_modules/@npmcli/run-script/lib/run-script-pkg.js b/node_modules/@npmcli/run-script/lib/run-script-pkg.js new file mode 100644 index 000000000..ccde173e0 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/run-script-pkg.js @@ -0,0 +1,88 @@ +const makeSpawnArgs = require('./make-spawn-args.js') +const promiseSpawn = require('@npmcli/promise-spawn') +const packageEnvs = require('./package-envs.js') +const { isNodeGypPackage, defaultGypInstallScript } = require('@npmcli/node-gyp') +const signalManager = require('./signal-manager.js') +const isServerPackage = require('./is-server-package.js') + +// you wouldn't like me when I'm angry... +const bruce = (id, event, cmd) => + `\n> ${id ? id + ' ' : ''}${event}\n> ${cmd.trim().replace(/\n/g, '\n> ')}\n` + +const runScriptPkg = async options => { + const { + event, + path, + scriptShell, + env = {}, + stdio = 'pipe', + pkg, + args = [], + stdioString = false, + // note: only used when stdio:inherit + banner = true, + // how long to wait for a process.kill signal + // only exposed here so that we can make the test go a bit faster. + signalTimeout = 500, + } = options + + const {scripts = {}, gypfile} = pkg + let cmd = null + if (options.cmd) + cmd = options.cmd + else if (pkg.scripts && pkg.scripts[event]) + cmd = pkg.scripts[event] + args.map(a => ` ${JSON.stringify(a)}`).join('') + else if ( // If there is no preinstall or install script, default to rebuilding node-gyp packages. + event === 'install' && + !scripts.install && + !scripts.preinstall && + gypfile !== false && + await isNodeGypPackage(path) + ) + cmd = defaultGypInstallScript + else if (event === 'start' && await isServerPackage(path)) + cmd = 'node server.js' + args.map(a => ` ${JSON.stringify(a)}`).join('') + + if (!cmd) + return { code: 0, signal: null } + + if (stdio === 'inherit' && banner !== false) { + // we're dumping to the parent's stdout, so print the banner + console.log(bruce(pkg._id, event, cmd)) + } + + const p = promiseSpawn(...makeSpawnArgs({ + event, + path, + scriptShell, + env: packageEnvs(env, pkg), + stdio, + cmd, + stdioString, + }), { + event, + script: cmd, + pkgid: pkg._id, + path, + }) + + if (stdio === 'inherit') + signalManager.add(p.process) + + if (p.stdin) + p.stdin.end() + + return p.catch(er => { + const { signal } = er + if (stdio === 'inherit' && signal) { + process.kill(process.pid, signal) + // just in case we don't die, reject after 500ms + // this also keeps the node process open long enough to actually + // get the signal, rather than terminating gracefully. + return new Promise((res, rej) => setTimeout(() => rej(er), signalTimeout)) + } else + throw er + }) +} + +module.exports = runScriptPkg diff --git a/node_modules/@npmcli/run-script/lib/run-script.js b/node_modules/@npmcli/run-script/lib/run-script.js new file mode 100644 index 000000000..af33d2113 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/run-script.js @@ -0,0 +1,13 @@ +const rpj = require('read-package-json-fast') +const runScriptPkg = require('./run-script-pkg.js') +const validateOptions = require('./validate-options.js') +const isServerPackage = require('./is-server-package.js') + +const runScript = options => { + validateOptions(options) + const {pkg, path} = options + return pkg ? runScriptPkg(options) + : rpj(path + '/package.json').then(pkg => runScriptPkg({...options, pkg})) +} + +module.exports = Object.assign(runScript, { isServerPackage }) diff --git a/node_modules/@npmcli/run-script/lib/set-path.js b/node_modules/@npmcli/run-script/lib/set-path.js new file mode 100644 index 000000000..d7bd2c288 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/set-path.js @@ -0,0 +1,44 @@ +const {resolve, dirname} = require('path') +const isWindows = require('./is-windows.js') +// the path here is relative, even though it does not need to be +// in order to make the posix tests pass in windows +const nodeGypPath = resolve(__dirname, '../lib/node-gyp-bin') + +// Windows typically calls its PATH environ 'Path', but this is not +// guaranteed, nor is it guaranteed to be the only one. Merge them +// all together in the order they appear in the object. +const setPATH = (projectPath, env) => { + // not require('path').delimiter, because we fake this for testing + const delimiter = isWindows ? ';' : ':' + const PATH = Object.keys(env).filter(p => /^path$/i.test(p) && env[p]) + .map(p => env[p].split(delimiter)) + .reduce((set, p) => set.concat(p.filter(p => !set.includes(p))), []) + .join(delimiter) + + const pathArr = [] + // unshift the ./node_modules/.bin from every folder + // walk up until dirname() does nothing, at the root + // XXX should we specify a cwd that we don't go above? + let p = projectPath + let pp + do { + pathArr.push(resolve(p, 'node_modules', '.bin')) + pp = p + p = dirname(p) + } while (p !== pp) + pathArr.push(nodeGypPath, PATH) + + const pathVal = pathArr.join(delimiter) + + // XXX include the node-gyp-bin path somehow? Probably better for + // npm or arborist or whoever to just provide that by putting it in + // the PATH environ, since that's preserved anyway. + for (const key of Object.keys(env)) { + if (/^path$/i.test(key)) + env[key] = pathVal + } + + return env +} + +module.exports = setPATH diff --git a/node_modules/@npmcli/run-script/lib/signal-manager.js b/node_modules/@npmcli/run-script/lib/signal-manager.js new file mode 100644 index 000000000..556e758c2 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/signal-manager.js @@ -0,0 +1,46 @@ +const runningProcs = new Set() +let handlersInstalled = false + +const forwardedSignals = [ + 'SIGINT', + 'SIGTERM' +] + +const handleSignal = signal => { + for (const proc of runningProcs) { + proc.kill(signal) + } +} + +const setupListeners = () => { + for (const signal of forwardedSignals) { + process.on(signal, handleSignal) + } + handlersInstalled = true +} + +const cleanupListeners = () => { + if (runningProcs.size === 0) { + for (const signal of forwardedSignals) { + process.removeListener(signal, handleSignal) + } + handlersInstalled = false + } +} + +const add = proc => { + runningProcs.add(proc) + if (!handlersInstalled) + setupListeners() + + proc.once('exit', () => { + runningProcs.delete(proc) + cleanupListeners() + }) +} + +module.exports = { + add, + handleSignal, + forwardedSignals +} diff --git a/node_modules/@npmcli/run-script/lib/validate-options.js b/node_modules/@npmcli/run-script/lib/validate-options.js new file mode 100644 index 000000000..48ac5c5d9 --- /dev/null +++ b/node_modules/@npmcli/run-script/lib/validate-options.js @@ -0,0 +1,31 @@ +const validateOptions = options => { + if (typeof options !== 'object' || !options) + throw new TypeError('invalid options object provided to runScript') + + const { + event, + path, + scriptShell, + env = {}, + stdio = 'pipe', + args = [], + cmd, + } = options + + if (!event || typeof event !== 'string') + throw new TypeError('valid event not provided to runScript') + if (!path || typeof path !== 'string') + throw new TypeError('valid path not provided to runScript') + if (scriptShell !== undefined && typeof scriptShell !== 'string') + throw new TypeError('invalid scriptShell option provided to runScript') + if (typeof env !== 'object' || !env) + throw new TypeError('invalid env option provided to runScript') + if (typeof stdio !== 'string' && !Array.isArray(stdio)) + throw new TypeError('invalid stdio option provided to runScript') + if (!Array.isArray(args) || args.some(a => typeof a !== 'string')) + throw new TypeError('invalid args option provided to runScript') + if (cmd !== undefined && typeof cmd !== 'string') + throw new TypeError('invalid cmd option provided to runScript') +} + +module.exports = validateOptions diff --git a/node_modules/@npmcli/run-script/package.json b/node_modules/@npmcli/run-script/package.json new file mode 100644 index 000000000..802dc626b --- /dev/null +++ b/node_modules/@npmcli/run-script/package.json @@ -0,0 +1,84 @@ +{ + "_args": [ + [ + "@npmcli/run-script@1.8.5", + "/Users/wookiee/sources/github-action-for-generator" + ] + ], + "_from": "@npmcli/run-script@1.8.5", + "_id": "@npmcli/run-script@1.8.5", + "_inBundle": false, + "_integrity": "sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A==", + "_location": "/@npmcli/run-script", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@npmcli/run-script@1.8.5", + "name": "@npmcli/run-script", + "escapedName": "@npmcli%2frun-script", + "scope": "@npmcli", + "rawSpec": "1.8.5", + "saveSpec": null, + "fetchSpec": "1.8.5" + }, + "_requiredBy": [ + "/@npmcli/arborist", + "/pacote" + ], + "_resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.5.tgz", + "_spec": "1.8.5", + "_where": "/Users/wookiee/sources/github-action-for-generator", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "https://izs.me" + }, + "bugs": { + "url": "https://github.com/npm/run-script/issues" + }, + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "infer-owner": "^1.0.4", + "node-gyp": "^7.1.0", + "read-package-json-fast": "^2.0.1" + }, + "description": "Run a lifecycle script for a package (descendant of npm-lifecycle)", + "devDependencies": { + "eslint": "^7.19.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^5.0.0", + "minipass": "^3.1.1", + "require-inject": "^1.4.4", + "tap": "^15.0.4" + }, + "files": [ + "lib/**/*.js", + "lib/node-gyp-bin" + ], + "homepage": "https://github.com/npm/run-script#readme", + "license": "ISC", + "main": "lib/run-script.js", + "name": "@npmcli/run-script", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/run-script.git" + }, + "scripts": { + "eslint": "eslint", + "lint": "npm run eslint -- \"lib/**/*.js\"", + "lintfix": "npm run lint -- --fix", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags", + "preversion": "npm test", + "test": "tap" + }, + "tap": { + "check-coverage": true, + "coverage-map": "map.js" + }, + "version": "1.8.5" +}