From db5cab07bd018671cc61daf1493f688ef10bbe0e Mon Sep 17 00:00:00 2001 From: "yanwang.lj" Date: Fri, 28 Feb 2025 00:30:39 +0800 Subject: [PATCH] feat: add concurrency and retry options --- bin/install.js | 5 +++++ lib/download/npm.js | 2 +- lib/format_install_options.js | 3 +++ lib/local_install.js | 4 +++- .../fixtures/options-concurrency/package.json | 6 ++++++ test/fixtures/options-retry/package.json | 6 ++++++ test/install_concurrency.test.js | 19 +++++++++++++++++++ test/install_retry.test.js | 19 +++++++++++++++++++ 8 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/options-concurrency/package.json create mode 100644 test/fixtures/options-retry/package.json create mode 100644 test/install_concurrency.test.js create mode 100644 test/install_retry.test.js diff --git a/bin/install.js b/bin/install.js index 409f4139..22e59c4b 100755 --- a/bin/install.js +++ b/bin/install.js @@ -152,6 +152,8 @@ Options: --high-speed-store: specify high speed store script to cache tgz files, and so on. Should export '* getStream(url)' function. --dependencies-tree: install with dependencies tree to restore the last install. --force-link-latest: force link latest version package to module root path. + --concurrency: install concurrency, default is 10 + --retry: retry count, default is 3 ` ); process.exit(0); @@ -296,6 +298,9 @@ debug('argv: %j, env: %j', argv, env); config.trace = argv.trace; config.engineStrict = argv['engine-strict']; config.registryOnly = argv['registry-only']; + config.concurrency = process.env.concurrency || argv.concurrency; + config.retry = process.env.retry || argv.retry; + if (config.production || argv.global) { // make sure show detail on production install or global install config.detail = true; diff --git a/lib/download/npm.js b/lib/download/npm.js index ccef22f3..30490d30 100644 --- a/lib/download/npm.js +++ b/lib/download/npm.js @@ -340,7 +340,7 @@ async function download(pkg, options) { const tarballUrls = utils.parseTarballUrls(pkg.dist.tarball); let tarballUrlIndex = 0; let tarballUrl; - while (count < 3) { + while (count < options.retry) { tarballUrl = tarballUrls[tarballUrlIndex++]; if (!tarballUrl) { tarballUrlIndex = 0; diff --git a/lib/format_install_options.js b/lib/format_install_options.js index 7610d2e1..cc66de5f 100644 --- a/lib/format_install_options.js +++ b/lib/format_install_options.js @@ -140,6 +140,9 @@ module.exports = function formatInstallOptions(options) { options.pruneCount = 0; options.pruneSize = 0; + options.concurrency = isNaN(options.concurrency) ? 10 : Number(options.concurrency); + options.retry = isNaN(options.retry) ? 3 : Number(options.retry); + debug('options: %j', options); return options; diff --git a/lib/local_install.js b/lib/local_install.js index a54b068b..c6373a85 100644 --- a/lib/local_install.js +++ b/lib/local_install.js @@ -48,6 +48,8 @@ const Context = require('./context'); * - {Boolean} [trace] - show memory and cpu usages traces of installation * - {Boolean} [flatten] - flatten dependencies by matching ancestors' dependencies * - {Boolean} [disableDedupe] - disable dedupe mode, will back to npm@2 node_modules tree + * - {Number} [concurrency] - install concurrency, default is `10` + * - {Number} [retry] - retry count, default is `3` * @param {Object} context - install context */ module.exports = async (options, context = new Context()) => { @@ -147,7 +149,7 @@ async function _install(options, context) { await installOne(options.targetDir, childPkg, options, context); }; - await pMap(pkgs, mapper, 10); + await pMap(pkgs, mapper, options.concurrency); options.downloadFinished = Date.now(); options.spinner && options.spinner.succeed(`Installed ${pkgs.length} packages`); diff --git a/test/fixtures/options-concurrency/package.json b/test/fixtures/options-concurrency/package.json new file mode 100644 index 00000000..158e7483 --- /dev/null +++ b/test/fixtures/options-concurrency/package.json @@ -0,0 +1,6 @@ +{ + "name": "options-concurrency", + "dependencies": { + "lodash": "*" + } +} diff --git a/test/fixtures/options-retry/package.json b/test/fixtures/options-retry/package.json new file mode 100644 index 00000000..fe7039a2 --- /dev/null +++ b/test/fixtures/options-retry/package.json @@ -0,0 +1,6 @@ +{ + "name": "options-retry", + "dependencies": { + "ladaygagaloadshnotexisit": "*" + } +} diff --git a/test/install_concurrency.test.js b/test/install_concurrency.test.js new file mode 100644 index 00000000..bca6257c --- /dev/null +++ b/test/install_concurrency.test.js @@ -0,0 +1,19 @@ +const coffee = require('coffee'); +const helper = require('./helper'); + +describe('test/install_concurrency.test.js', () => { + describe('concurrency', () => { + const root = helper.fixtures('options-concurrency'); + const cleanup = helper.cleanup(root); + + beforeEach(cleanup); + afterEach(cleanup); + + it('should use 5 concurrency success', async () => { + await coffee.fork(helper.npminstall, [ '--concurrency', '5' ], { cwd: root }) + .debug() + .expect('code', 0) + .end(); + }); + }); +}); diff --git a/test/install_retry.test.js b/test/install_retry.test.js new file mode 100644 index 00000000..882c8aad --- /dev/null +++ b/test/install_retry.test.js @@ -0,0 +1,19 @@ +const coffee = require('coffee'); +const helper = require('./helper'); + +describe('test/install_retry.test.js', () => { + describe('retry', () => { + const root = helper.fixtures('options-retry'); + const cleanup = helper.cleanup(root); + + beforeEach(cleanup); + afterEach(cleanup); + + it('should use 5 retry failed', async () => { + await coffee.fork(helper.npminstall, [ '--retry', '5' ], { cwd: root }) + .debug() + .notExpect('code', 0) + .end(); + }); + }); +});