From f6fbc06781e38af436286dad1f489955da2b692d Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 30 Aug 2019 13:55:17 -0700 Subject: [PATCH] ci: pass appropriate configs for file/dir modes Re: https://npm.community/t/6-11-2-npm-ci-installs-package-with-wrong-permissions/9720 Still passing a plain old (non-Figgy Pudding) object into libcipm, duplicating the extra keys added in figgy-config.js. This is not a clean or nice or elegant solution, but it works, without regressing the config env var issue. Pairing with @claudiahdz --- lib/ci.js | 26 ++++++++++++++++--- lib/config/figgy-config.js | 2 +- test/tap/ci-permissions.js | 53 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 test/tap/ci-permissions.js diff --git a/lib/ci.js b/lib/ci.js index 375c1448a1ea1..309ad2f784ec0 100644 --- a/lib/ci.js +++ b/lib/ci.js @@ -2,7 +2,8 @@ const npm = require('./npm.js') const Installer = require('libcipm') -const npmlog = require('npmlog') +const log = require('npmlog') +const path = require('path') ci.usage = 'npm ci' @@ -10,14 +11,33 @@ ci.completion = (cb) => cb(null, []) module.exports = ci function ci (args, cb) { - const opts = Object.create({ log: npmlog }) + const opts = { + // Add some non-npm-config opts by hand. + cache: path.join(npm.config.get('cache'), '_cacache'), + // NOTE: npm has some magic logic around color distinct from the config + // value, so we have to override it here + color: !!npm.color, + hashAlgorithm: 'sha1', + includeDeprecated: false, + log, + 'npm-session': npm.session, + 'project-scope': npm.projectScope, + refer: npm.referer, + dmode: npm.modes.exec, + fmode: npm.modes.file, + umask: npm.modes.umask, + npmVersion: npm.version, + tmp: npm.tmp + } + for (const key in npm.config.list[0]) { if (key !== 'log') { opts[key] = npm.config.list[0][key] } } + return new Installer(opts).run().then(details => { - npmlog.disableProgress() + log.disableProgress() console.log(`added ${details.pkgCount} packages in ${ details.runTime / 1000 }s`) diff --git a/lib/config/figgy-config.js b/lib/config/figgy-config.js index 9e9ca0ba561ef..d704d1502cb44 100644 --- a/lib/config/figgy-config.js +++ b/lib/config/figgy-config.js @@ -9,7 +9,7 @@ const npm = require('../npm.js') const pack = require('../pack.js') const path = require('path') -const npmSession = crypto.randomBytes(8).toString('hex') +const npmSession = npm.session = crypto.randomBytes(8).toString('hex') log.verbose('npm-session', npmSession) const SCOPE_REGISTRY_REGEX = /@.*:registry$/gi diff --git a/test/tap/ci-permissions.js b/test/tap/ci-permissions.js new file mode 100644 index 0000000000000..c73d464236540 --- /dev/null +++ b/test/tap/ci-permissions.js @@ -0,0 +1,53 @@ +const t = require('tap') +const tar = require('tar') +const common = require('../common-tap.js') +const pkg = common.pkg +const rimraf = require('rimraf') +const { writeFileSync, statSync, chmodSync } = require('fs') +const { resolve } = require('path') +const mkdirp = require('mkdirp') + +t.test('setup', t => { + mkdirp.sync(resolve(pkg, 'package')) + const pj = resolve(pkg, 'package', 'package.json') + writeFileSync(pj, JSON.stringify({ + name: 'foo', + version: '1.2.3' + })) + chmodSync(pj, 0o640) + tar.c({ + sync: true, + file: resolve(pkg, 'foo.tgz'), + gzip: true, + cwd: pkg + }, ['package']) + writeFileSync(resolve(pkg, 'package.json'), JSON.stringify({ + name: 'root', + version: '1.2.3', + dependencies: { + foo: 'file:foo.tgz' + } + })) + t.end() +}) + +t.test('run install to generate package-lock', t => + common.npm(['install'], { cwd: pkg }).then(([code]) => t.equal(code, 0))) + +t.test('remove node_modules', t => rimraf(resolve(pkg, 'node_modules'), t.end)) + +t.test('run ci and check modes', t => + common.npm(['ci'], { cwd: pkg, stdio: 'inherit' }).then(([code]) => { + t.equal(code, 0) + const file = resolve(pkg, 'node_modules', 'foo', 'package.json') + // bitwise AND against 0o705 so that we can detect whether + // the file is world-readable. + // Typical unix systems would leave the file 0o644 + // Travis-ci and some other Linux systems will be 0o664 + // Windows is 0o666 + // The regression this is detecting (ie, the default in the tarball) + // leaves the file as 0o640. + // Bitwise-AND 0o705 should always result in 0o604, and never 0o600 + const mode = statSync(file).mode & 0o705 + t.equal(mode, 0o604) + }))