From 05a31eef1ebe3c60a62f97d7b35fab4bc4491e91 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Tue, 31 Jul 2018 10:40:03 +0800 Subject: [PATCH] refactor: don't create .npminstall.done file (#277) --- bin/install.js | 17 ++++++- lib/download/npm.js | 6 +-- lib/format_install_options.js | 2 +- lib/get.js | 13 ++--- lib/global_install.js | 1 - lib/install.js | 5 +- lib/local_install.js | 10 ++-- lib/utils.js | 30 +++++++++-- package.json | 2 +- test/bundleDependencies.test.js | 4 +- test/cleanup.test.js | 17 ++++--- .../install-enable-prune-on-pkg/package.json | 14 +++++ test/ignoreScripts.test.js | 4 +- test/index.test.js | 6 +-- test/install-enable-prune.test.js | 51 ++++++++++++++----- test/install-production.test.js | 1 - test/installRemote.test.js | 2 +- 17 files changed, 122 insertions(+), 63 deletions(-) create mode 100644 test/fixtures/install-enable-prune-on-pkg/package.json diff --git a/bin/install.js b/bin/install.js index 57d249a9..9d725b5a 100755 --- a/bin/install.js +++ b/bin/install.js @@ -153,7 +153,7 @@ if (inChina) { registry = registry || globalConfig.chineseRegistry; } // for env.npm_config_registry -registry = registry || 'https://registry.npmjs.org'; +registry = registry || 'https://registry.npmjs.com'; const proxy = argv.proxy || process.env.npm_proxy || process.env.npm_config_proxy; @@ -279,6 +279,21 @@ co(function* () { const exists = yield fs.exists(pkgFile); if (!exists) { console.warn(chalk.yellow(`npminstall WARN package.json not exists: ${pkgFile}`)); + } else { + // try to read npminstall config from package.json + const pkg = yield utils.readJSON(pkgFile); + pkg.config = pkg.config || {}; + pkg.config.npminstall = pkg.config.npminstall || {}; + // { + // "config": { + // "npminstall": { + // "prune": true + // } + // } + // } + if (pkg.config.npminstall.prune === true) { + config.prune = true; + } } } yield installLocal(config); diff --git a/lib/download/npm.js b/lib/download/npm.js index 2fbaa0a4..717ab082 100644 --- a/lib/download/npm.js +++ b/lib/download/npm.js @@ -118,6 +118,7 @@ function* getFullPackageMeta(name, options) { timeout: options.timeout, followRedirect: true, gzip: true, + dataType: 'json', }, options); options.totalJSONSize += result.res.size; options.totalJSONCount += 1; @@ -126,7 +127,6 @@ function* getFullPackageMeta(name, options) { function* download(pkg, options) { const ungzipDir = options.ungzipDir || utils.getPackageStorePath(options.storeDir, pkg); - const donefile = path.join(ungzipDir, '.npminstall.done'); // make sure only one download for a version const key = `download:${pkg.name}@${pkg.version}`; @@ -147,7 +147,7 @@ function* download(pkg, options) { done: false, }; - if (yield fs.exists(donefile)) { + if (yield utils.isInstallDone(ungzipDir)) { options.cache[key].done = true; options.events.emit(key); // debug('[%s@%s] Exists', pkg.name, pkg.version); @@ -204,7 +204,7 @@ function* download(pkg, options) { pkg.dependencies = Object.assign({}, pkg.dependencies, pkg.__fixDependencies); } - yield fs.writeFile(donefile, Date()); + yield utils.setInstallDone(ungzipDir); const pkgMeta = { _from: `${pkg.name}@${pkg.version}`, diff --git a/lib/format_install_options.js b/lib/format_install_options.js index 0a923790..6722aa41 100644 --- a/lib/format_install_options.js +++ b/lib/format_install_options.js @@ -48,7 +48,7 @@ module.exports = function formatInstallOptions(options) { options.latestPackages = new Map(); options.cache = {}; assert(options.root && typeof options.root === 'string', 'options.root required and must be string'); - options.registry = options.registry || 'https://registry.npmjs.org'; + options.registry = options.registry || 'https://registry.npmjs.com'; if (!options.targetDir) { options.targetDir = options.root; } diff --git a/lib/get.js b/lib/get.js index 917602b7..1a2bbc5e 100644 --- a/lib/get.js +++ b/lib/get.js @@ -63,21 +63,14 @@ function* get(url, options, globalOptions) { function* _get(url, options, retry, globalOptions) { try { - const result = yield urllib.request(url, options); - if (result.data && Buffer.isBuffer(result.data) && result.data.length > 0 && - options.headers.accept && - options.headers.accept.indexOf('application/vnd.npm.install-v1+json') >= 0) { - result.data = JSON.parse(result.data); - debug('%s support vnd.npm.install-v1+json format, modified', result.data.name, result.data.modified); - } - return result; + return yield urllib.request(url, options); } catch (err) { retry--; if (retry > 0) { const delay = 100 * (MAX_RETRY - retry); const logger = globalOptions && globalOptions.console || console; - logger.warn('[npminstall:get] retry GET %s after %sms, retry left %s, error: %s', - url, delay, retry, err); + logger.warn('[npminstall:get] retry GET %s after %sms, retry left %s, error: %s, status: %s, headers: %j, \nstack: %s', + url, delay, retry, err, err.status, err.headers, err.stack); yield utils.sleep(delay); return yield _get(url, options, retry, globalOptions); } diff --git a/lib/global_install.js b/lib/global_install.js index 5a968ffa..b42c4f82 100644 --- a/lib/global_install.js +++ b/lib/global_install.js @@ -53,7 +53,6 @@ module.exports = function* globalInstall(options) { console.info(chalk.gray(`Copying ${result.dir} to ${targetDir}`)); yield utils.rimraf(targetDir); yield fs.rename(result.dir, targetDir); - yield fs.unlink(path.join(targetDir, '.npminstall.done')); yield utils.rimraf(tmpDir); console.info(chalk.gray(`Installing ${realPkg.name}'s dependencies to ${targetDir}/node_modules`)); diff --git a/lib/install.js b/lib/install.js index 4536aad7..12b6ee89 100644 --- a/lib/install.js +++ b/lib/install.js @@ -123,7 +123,6 @@ function* _install(parentDir, pkg, ancestors, options) { const info = yield download(p, options); const realPkg = info.package; const realPkgDir = info.dir; - const donefile = path.join(realPkgDir, '.npminstall.done'); // record version options.packageVersions[realPkg.name] = options.packageVersions[realPkg.name] || new Set(); @@ -283,9 +282,9 @@ function* _install(parentDir, pkg, ancestors, options) { } catch (err) { // delete donefile when install error, make sure this package won't be skipped during next installation. try { - yield utils.rimraf(donefile); + yield utils.unsetInstallDone(realPkgDir); } catch (e) { - options.console.warn(chalk.yellow(`rmdir donefile: ${donefile} error: ${e}, ignore it`)); + options.console.warn(chalk.yellow(`unsetInstallDone: ${realPkgDir} error: ${e}, ignore it`)); } throw err; } diff --git a/lib/local_install.js b/lib/local_install.js index 0da3935d..5372a2dd 100644 --- a/lib/local_install.js +++ b/lib/local_install.js @@ -30,7 +30,7 @@ const bin = require('./bin'); * npm install * @param {Object} options - install options * - {String} root - npm install root dir - * - {String} [registry] - npm registry url, default is `https://registry.npmjs.org` + * - {String} [registry] - npm registry url, default is `https://registry.npmjs.com` * - {String} [targetDir] - node_modules target dir, default is ${root}. * - {String} [storeDir] - npm modules store dir, default is `${targetDir}/node_modules` * - {Number} [timeout] - npm registry request timeout, default is 60000 ms @@ -339,7 +339,6 @@ function* runPostInstallTasks(options) { const pkg = task.pkg; const root = task.root; const displayName = task.displayName; - const donefile = path.join(root, '.npminstall.done'); const installScript = pkg.scripts.install; const postinstallScript = pkg.scripts.postinstall; try { @@ -378,9 +377,9 @@ function* runPostInstallTasks(options) { } catch (err) { // If post install execute error, make sure this package won't be skipped during next installation. try { - yield utils.rimraf(donefile); + yield utils.unsetInstallDone(root); } catch (e) { - options.console.warn(chalk.yellow(`rmdir donefile: ${donefile} error: ${e}, ignore it`)); + options.console.warn(chalk.yellow(`unsetInstallDone: ${root} error: ${e}, ignore it`)); } if (task.optional) { console.warn(chalk.red('%s optional error: %s'), displayName, err.stack); @@ -492,7 +491,4 @@ function finishInstall(options) { } else { options.console.info.apply(console, logArguments); } - - const doneFile = path.join(options.storeDir, '.npminstall.done'); - fs.writeFileSync(doneFile, `All packages installed at ${Date()}`); } diff --git a/lib/utils.js b/lib/utils.js index 817f3836..2c986325 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -44,6 +44,27 @@ exports.readPackageJSON = function* readPackageJSON(root) { return pkg; }; +const INSTALL_DONE_KEY = '__npminstall_done'; + +// 设置 pkg 安装完成的标记 +exports.setInstallDone = function* setInstallDone(pkgRoot) { + yield exports.addMetaToJSONFile(path.join(pkgRoot, 'package.json'), { + [INSTALL_DONE_KEY]: Date(), + }); +}; + +exports.unsetInstallDone = function* setInstallDone(pkgRoot) { + yield exports.addMetaToJSONFile(path.join(pkgRoot, 'package.json'), { + [INSTALL_DONE_KEY]: false, + }); +}; + +// 判断 pkg 是否已经安装完成 +exports.isInstallDone = function* isInstallDone(pkgRoot) { + const pkg = yield exports.readJSON(path.join(pkgRoot, 'package.json')); + return !!pkg[INSTALL_DONE_KEY]; +}; + exports.addMetaToJSONFile = function* (filepath, meta) { yield fs.chmod(filepath, '644'); const pkg = yield exports.readJSON(filepath); @@ -128,7 +149,7 @@ function setNpmPackageEnv(env, key, value) { exports.formatPackageUrl = function formatUrl(registry, name) { if (name[0] === '@') { // dont encodeURIComponent @ char, it will be 405 - // https://registry.npmjs.org/%40rstacruz%2Ftap-spec/%3E%3D4.1.1 + // https://registry.npmjs.com/%40rstacruz%2Ftap-spec/%3E%3D4.1.1 name = '@' + utility.encodeURIComponent(name.substring(1)); } const parsed = url.parse(registry); @@ -348,7 +369,6 @@ exports.copyInstall = function* (src, options) { } const targetdir = options.ungzipDir || exports.getPackageStorePath(options.storeDir, realPkg); - const donefile = path.join(targetdir, '.npminstall.done'); const key = `copy:${targetdir}`; const result = { dir: targetdir, @@ -370,10 +390,10 @@ exports.copyInstall = function* (src, options) { done: false, }; - if (!(yield fs.exists(donefile))) { + if (!(yield exports.isInstallDone(targetdir))) { yield fse.emptydir(targetdir); yield fse.copy(src, targetdir); - yield fs.writeFile(donefile, Date()); + yield exports.setInstallDone(targetdir); result.exists = false; } @@ -412,7 +432,7 @@ function* getRemotePackage(name, registry, globalOptions) { const registries = [ registry ].concat([ 'https://registry.npm.taobao.org', 'https://r.cnpmjs.org', - 'https://registry.npmjs.org', + 'https://registry.npmjs.com', ]); let lastErr; let pkg; diff --git a/package.json b/package.json index eba9358c..488fd884 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "runscript": "^1.2.1", "semver": "^5.3.0", "tar": "^4.0.1", - "urllib": "^2.24.0", + "urllib": "^2.29.1", "utility": "^1.14.0", "uuid": "^3.0.1" }, diff --git a/test/bundleDependencies.test.js b/test/bundleDependencies.test.js index cb45be21..472c7fa2 100644 --- a/test/bundleDependencies.test.js +++ b/test/bundleDependencies.test.js @@ -34,7 +34,7 @@ describe('test/bundleDependencies.test.js', () => { // only node-pre-gyp dir exists const dirs = yield fs.readdir(path.join(tmp, 'node_modules/')); - assert.deepEqual(dirs.sort(), [ '.bin', '_node-pre-gyp@0.6.19@node-pre-gyp', '.npminstall.done', 'node-pre-gyp' ].sort()); + assert.deepEqual(dirs.sort(), [ '.bin', '_node-pre-gyp@0.6.19@node-pre-gyp', 'node-pre-gyp' ].sort()); }); it('should install bundleDependencies not exist(nyc@6.4.2)', function* () { @@ -53,7 +53,7 @@ describe('test/bundleDependencies.test.js', () => { root: tmp, pkgs: [{ name: 'sqlite3', - version: '4.0.0', + version: '4.0.2', // version: '3.1.3', }], }); diff --git a/test/cleanup.test.js b/test/cleanup.test.js index 31a768e6..a6ad56b0 100644 --- a/test/cleanup.test.js +++ b/test/cleanup.test.js @@ -5,6 +5,7 @@ const path = require('path'); const rimraf = require('rimraf'); const fs = require('mz/fs'); const mkdirp = require('mkdirp'); +const utils = require('../lib/utils'); const npminstall = require('./npminstall'); describe('test/cleanup.test.js', () => { @@ -34,8 +35,8 @@ describe('test/cleanup.test.js', () => { } assert(throwError); - let exists = yield fs.exists(path.join(tmp, 'node_modules/_install-error@1.0.1@install-error/.npminstall.done')); - assert.equal(exists, false); + let done = yield utils.isInstallDone(path.join(tmp, 'node_modules/_install-error@1.0.1@install-error/package.json')); + assert.equal(done, false); const dirs = yield fs.readdir(path.join(tmp, 'node_modules')); assert.deepEqual(dirs, [ '_install-error@1.0.1@install-error' ]); @@ -52,8 +53,8 @@ describe('test/cleanup.test.js', () => { throwError = true; } assert.equal(throwError, true); - exists = yield fs.exists(path.join(tmp, 'node_modules/_install-error@1.0.1@install-error/.npminstall.done')); - assert.equal(exists, false); + done = yield utils.isInstallDone(path.join(tmp, 'node_modules/_install-error@1.0.1@install-error/.npminstall.done')); + assert.equal(done, false); }); it('should remove donefile when execute postinstall script failed', function* () { @@ -69,8 +70,8 @@ describe('test/cleanup.test.js', () => { } assert.equal(throwError, true); - let exists = yield fs.exists(path.join(tmp, 'node_modules/_postinstall-error@1.0.0@postinstall-error/.npminstall.done')); - assert.equal(exists, false); + let done = yield utils.isInstallDone(path.join(tmp, 'node_modules/_postinstall-error@1.0.0@postinstall-error/.npminstall.done')); + assert.equal(done, false); // install again will try to download throwError = false; @@ -83,7 +84,7 @@ describe('test/cleanup.test.js', () => { throwError = true; } assert.equal(throwError, true); - exists = yield fs.exists(path.join(tmp, 'node_modules/_postinstall-error@1.0.0@postinstall-error/.npminstall.done')); - assert.equal(exists, false); + done = yield utils.isInstallDone(path.join(tmp, 'node_modules/_postinstall-error@1.0.0@postinstall-error/.npminstall.done')); + assert.equal(done, false); }); }); diff --git a/test/fixtures/install-enable-prune-on-pkg/package.json b/test/fixtures/install-enable-prune-on-pkg/package.json new file mode 100644 index 00000000..7dab2ff7 --- /dev/null +++ b/test/fixtures/install-enable-prune-on-pkg/package.json @@ -0,0 +1,14 @@ +{ + "name": "install-enable-prune-on-pkg", + "dependencies": { + "pedding": "~1.0.0", + "npm": "*", + "egg": "*", + "antd": "*" + }, + "config": { + "npminstall": { + "prune": true + } + } +} diff --git a/test/ignoreScripts.test.js b/test/ignoreScripts.test.js index d1a2e312..6aca34d5 100644 --- a/test/ignoreScripts.test.js +++ b/test/ignoreScripts.test.js @@ -23,8 +23,8 @@ describe('test/ignoreScripts.test.js', () => { }); const dirs = yield fs.readdir(path.join(root, 'node_modules')); - assert.deepEqual(dirs.sort(), [ '_pkg@1.0.0@pkg', '.npminstall.done', '.package_versions.json', 'pkg' ].sort()); + assert.deepEqual(dirs.sort(), [ '_pkg@1.0.0@pkg', '.package_versions.json', 'pkg' ].sort()); const files = yield fs.readdir(path.join(root, 'node_modules/pkg')); - assert.deepEqual(files, [ '.npminstall.done', 'index.js', 'package.json' ]); + assert.deepEqual(files, [ 'index.js', 'package.json' ]); }); }); diff --git a/test/index.test.js b/test/index.test.js index b904cb10..6f4321ab 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -5,6 +5,7 @@ const fs = require('mz/fs'); const path = require('path'); const rimraf = require('rimraf'); const npminstall = require('./npminstall'); +const utils = require('../lib/utils'); const mkdirp = require('../lib/utils').mkdirp; const readJSON = require('../lib/utils').readJSON; @@ -31,8 +32,7 @@ describe('test/index.test.js', () => { { name: 'contributors' }, ], }); - // tmp/node_modules/.npminstall.done file should exists - assert(fs.existsSync(path.join(tmp, 'node_modules/.npminstall.done'))); + assert(yield utils.isInstallDone(path.join(tmp, 'node_modules/mocha'))); }); it('should handle @types/escodegen@0.0.2 tgz', function* () { @@ -42,7 +42,7 @@ describe('test/index.test.js', () => { { name: '@types/escodegen', version: '0.0.2' }, ], }); - assert(fs.existsSync(path.join(tmp, 'node_modules/.npminstall.done'))); + assert(yield utils.isInstallDone(path.join(tmp, 'node_modules/@types/escodegen'))); assert(fs.existsSync(path.join(tmp, 'node_modules/@types/escodegen/package.json'))); assert(require(path.join(tmp, 'node_modules/@types/escodegen/package.json')).name === '@types/escodegen'); }); diff --git a/test/install-enable-prune.test.js b/test/install-enable-prune.test.js index ce672380..5eca3ca3 100644 --- a/test/install-enable-prune.test.js +++ b/test/install-enable-prune.test.js @@ -9,22 +9,45 @@ const coffee = require('coffee'); const npminstall = path.join(__dirname, '..', 'bin', 'install.js'); describe('test/install-enable-prune.test.js', () => { - const cwd = path.join(__dirname, 'fixtures', 'install-enable-prune'); + describe('--prune', () => { + const cwd = path.join(__dirname, 'fixtures', 'install-enable-prune'); - function cleanup() { - rimraf.sync(path.join(cwd, 'node_modules')); - } + function cleanup() { + rimraf.sync(path.join(cwd, 'node_modules')); + } - beforeEach(() => { - cleanup(); + beforeEach(() => { + cleanup(); + }); + afterEach(cleanup); + + it('should install --prune', function* () { + yield coffee.fork(npminstall, [ '--prune', '--production' ], { cwd }) + .debug() + .expect('code', 0) + .end(); + assert(!fs.existsSync(path.join(cwd, 'node_modules/egg/README.md'))); + }); }); - afterEach(cleanup); - - it('should install --prune', function* () { - yield coffee.fork(npminstall, [ '--prune', '--production' ], { cwd }) - .debug() - .expect('code', 0) - .end(); - assert(!fs.existsSync(path.join(cwd, 'node_modules/egg/README.md'))); + + describe('pkg.config.npminstall.prune', () => { + const cwd = path.join(__dirname, 'fixtures', 'install-enable-prune-on-pkg'); + + function cleanup() { + rimraf.sync(path.join(cwd, 'node_modules')); + } + + beforeEach(() => { + cleanup(); + }); + afterEach(cleanup); + + it('should install with prune', function* () { + yield coffee.fork(npminstall, [], { cwd }) + .debug() + .expect('code', 0) + .end(); + assert(!fs.existsSync(path.join(cwd, 'node_modules/egg/README.md'))); + }); }); }); diff --git a/test/install-production.test.js b/test/install-production.test.js index 463a5503..c548384c 100644 --- a/test/install-production.test.js +++ b/test/install-production.test.js @@ -7,7 +7,6 @@ const npminstall = path.join(__dirname, '..', 'bin', 'install.js'); describe('test/install-production.test.js', () => { function cleanup(cwd) { - rimraf.sync(path.join(cwd, 'node_modules', '.npminstall.done')); rimraf.sync(path.join(cwd, 'node_modules', '.package_versions.json')); } diff --git a/test/installRemote.test.js b/test/installRemote.test.js index 6efe3716..5926b60f 100644 --- a/test/installRemote.test.js +++ b/test/installRemote.test.js @@ -34,7 +34,7 @@ describe('test/installRemote.test.js', () => { assert.equal(pkg.name, 'taffydb'); const dirs = yield fs.readdir(path.join(root, 'node_modules')); - assert.deepEqual(dirs.sort(), [ '.tmp', 'pedding', 'taffydb', '_pedding@1.0.0@pedding', '_taffydb@2.7.2@taffydb', '.npminstall.done', '.package_versions.json' ].sort()); + assert.deepEqual(dirs.sort(), [ '.tmp', 'pedding', 'taffydb', '_pedding@1.0.0@pedding', '_taffydb@2.7.2@taffydb', '.package_versions.json' ].sort()); }); it('should install remote/taffydb/-/taffydb-2.7.2.tgz', function* () {