diff --git a/doc/cli/npm-pack.md b/doc/cli/npm-pack.md index 807663ac210fe..0c90995ac2965 100644 --- a/doc/cli/npm-pack.md +++ b/doc/cli/npm-pack.md @@ -17,6 +17,9 @@ If the same package is specified multiple times, then the file will be overwritten the second time. If no arguments are supplied, then npm packs the current package folder. +In that case, `prepack` and `postpack` script hooks are invoked. `postpack` is +invoked with the **npm_lifecycle_postpack_archive** environment variable holding the full path of the +generated archive. The `--dry-run` argument will do everything that pack usually does without actually packing anything. Reports on what would have gone into the tarball. diff --git a/doc/cli/npm-publish.md b/doc/cli/npm-publish.md index c582ad8470c2a..412813587b406 100644 --- a/doc/cli/npm-publish.md +++ b/doc/cli/npm-publish.md @@ -64,6 +64,11 @@ installs will use the strongest supported algorithm to verify downloads. Similar to `--dry-run` see `npm-pack(1)`, which figures out the files to be included and packs them into a tarball to be uploaded to the registry. +The following script hooks are invoked : +- `prepublishOnly` and then `prepack` before any processing +- `postpack` after generating the archive (path to the archive is available in the **npm_lifecycle_postpack_archive** environment variable) +- `publish` and than `postpublish` after uploading + ## SEE ALSO * npm-registry(7) @@ -74,3 +79,4 @@ included and packs them into a tarball to be uploaded to the registry. * npm-dist-tag(1) * npm-pack(1) * npm-profile(1) +* npm-scripts(7) diff --git a/doc/misc/npm-scripts.md b/doc/misc/npm-scripts.md index 22ded49001632..306c3a73493f7 100644 --- a/doc/misc/npm-scripts.md +++ b/doc/misc/npm-scripts.md @@ -20,11 +20,11 @@ following scripts: run BEFORE a tarball is packed (on `npm pack`, `npm publish`, and when installing git dependencies) * postpack: - Run AFTER the tarball has been generated and moved to its final destination. + Run AFTER the tarball has been generated and moved to its final local destination (on `npm pack`, `npm publish` in temporaty file just before upload). * publish, postpublish: Run AFTER the package is published. * preinstall: - Run BEFORE the package is installed + Run BEFORE the package is installed. * install, postinstall: Run AFTER the package is installed. * preuninstall, uninstall: diff --git a/lib/pack.js b/lib/pack.js index 78e5bfd174d7b..8ab764b995d59 100644 --- a/lib/pack.js +++ b/lib/pack.js @@ -137,6 +137,7 @@ function packDirectory (mani, dir, target, filename, logIt, dryRun) { }).then((pkg) => { return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'packing'}, (tmp) => { const tmpTarget = path.join(tmp, path.basename(target)) + const finalTarget = dryRun ? tmpTarget : path.resolve(target) const tarOpt = { file: tmpTarget, @@ -154,8 +155,6 @@ function packDirectory (mani, dir, target, filename, logIt, dryRun) { // specifically with @ signs, so we just neutralize that one // and any such future "features" by prepending `./` .then((files) => tar.create(tarOpt, files.map((f) => `./${f}`))) - .then(() => getContents(pkg, tmpTarget, filename, logIt)) - // thread the content info through .tap(() => { if (dryRun) { log.verbose('pack', '--dry-run mode enabled. Skipping write.') @@ -163,7 +162,13 @@ function packDirectory (mani, dir, target, filename, logIt, dryRun) { return move(tmpTarget, target, {Promise: BB, fs}) } }) - .tap(() => lifecycle(pkg, 'postpack', dir)) + .tap(() => { + const env = { npm_lifecycle_postpack_archive: finalTarget } + lifecycle(pkg, 'postpack', dir, { env }) + }) + // compute pkg stats after postpack, in case postpack modifies + // the archive + .then(() => getContents(pkg, finalTarget, filename, logIt)) }) }) } diff --git a/node_modules/npm-lifecycle/index.js b/node_modules/npm-lifecycle/index.js index 0972870b18c97..95ecd0d7ed016 100644 --- a/node_modules/npm-lifecycle/index.js +++ b/node_modules/npm-lifecycle/index.js @@ -437,6 +437,11 @@ function makeEnv (data, opts, prefix, env) { } } + // assign script specific environnement variables + if (typeof opts.env === "object") { + Object.assign(env, opts.env) + } + if (prefix !== 'npm_package_') return env prefix = 'npm_config_' diff --git a/test/broken-under-nyc-and-travis/lifecycle-path.js b/test/broken-under-nyc-and-travis/lifecycle-path.js index 6209319b412f6..0cfa5d3d62c5e 100644 --- a/test/broken-under-nyc-and-travis/lifecycle-path.js +++ b/test/broken-under-nyc-and-travis/lifecycle-path.js @@ -4,6 +4,7 @@ var path = require('path') var mkdirp = require('mkdirp') var osenv = require('osenv') var rimraf = require('rimraf') +var which = require('which') var test = require('tap').test var common = require('../common-tap.js') @@ -21,6 +22,9 @@ if (isWindows) { PATH = '/bin:/usr/bin' } +var systemNode = which.sync('node', { nothrow: true, path: PATH }) +// the path to the system wide node (null if none) + test('setup', function (t) { cleanup() mkdirp.sync(pkg) @@ -183,6 +187,12 @@ function checkPath (testconfig, t) { 'The node binary used for scripts is.*' + process.execPath.replace(/[/\\]/g, '.')) t.match(stderr, regex, 'reports the current binary vs conflicting') + } else if (systemNode !== null) { + var regexSystemNode = new RegExp( + 'The node binary used for scripts is.*' + + systemNode.replace(/[/\\]/g, '.') + ) + t.match(stderr, regexSystemNode, 'reports the system binary vs conflicting') } else { t.match(stderr, /there is no node binary in the current PATH/, 'informs user that there is no node binary in PATH') } diff --git a/test/tap/pack.js b/test/tap/pack.js index 23c8296d003cd..59963df1e6bb1 100644 --- a/test/tap/pack.js +++ b/test/tap/pack.js @@ -197,3 +197,32 @@ test('postpack', (t) => { }) .then(() => rimraf(testDir)) }) + +test('postpack-env', (t) => { + const fixture = new Tacks(new Dir({ + 'package.json': new File({ + name: 'generic-package', + version: '90000.100001.5', + scripts: { + postpack: 'node -e "var fs = require(\'fs\'); fs.writeFileSync(\'../myenv.json\',JSON.stringify(process.env,null,2)); if (!fs.existsSync(process.env.npm_lifecycle_postpack_archive)) { throw new Error(\'tar archive does not exist on postpack\') }"' + } + }) + })) + + return rimraf(testDir) + .then(() => fixture.create(testDir)) + .then(() => common.npm([ + 'pack', + '--loglevel', 'notice', + '--cache', cache, + '--tmp', tmp, + '--prefix', testDir, + '--no-global' + ], { + cwd: testDir + })) + .spread((code, stdout, stderr) => { + t.equal(code, 0, 'npm pack exited ok') + }) + .then(() => rimraf(testDir)) +})