├── .gitignore ├── .travis.yml ├── README.md ├── appveyor.yml ├── bin └── npm-install-retry ├── index.js ├── package.json └── test └── main.tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.2.1" 4 | - "5.1" 5 | - "4.2" 6 | - "iojs" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/jfromaniello/npm-install-retry.svg?branch=master)](https://travis-ci.org/jfromaniello/npm-install-retry) 2 | 3 | ![Build status](https://ci.appveyor.com/api/projects/status/sc7937we6gb0mwoc?svg=true) 4 | 5 | Command line utility that retries `npm install` when NPM fails with flaky errors: 6 | * `npm ERR! cb() never called`, 7 | * `npm ERR! errno ECONNRESET`, 8 | * `npm ERR! shasum check failed`, 9 | * `npm ERR! code EINTEGRITY` 10 | 11 | This happens sporadically and has been reported many times: 12 | 13 | - https://github.com/meteor/meteor/issues/1190 14 | - https://github.com/isaacs/npm/issues/2907 15 | - https://github.com/isaacs/npm/issues/3269 16 | - https://github.com/npm/npm/issues?utf8=%E2%9C%93&q=ECONNRESET+ 17 | - https://github.com/npm/npm/issues/2701 18 | 19 | and still fails. 20 | 21 | 22 | ## Installation 23 | 24 | npm install -g npm-install-retry 25 | 26 | ## Usage 27 | 28 | From command-line: 29 | 30 | npm-install-retry --wait 500 --attempts 10 -- --production 31 | 32 | It has two options wait (defaults to 500) and attempts (default to 10). Everything after `--` goes directly to npm. 33 | 34 | ## License 35 | 36 | MIT 2013 - José F. Romaniello 37 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Test against this version of Node.js 2 | environment: 3 | nodejs_version: "6.2.1" 4 | 5 | # Install scripts. (runs after repo cloning) 6 | install: 7 | # Get the latest stable version of Node.js or io.js 8 | - ps: Install-Product node $env:nodejs_version 9 | # install modules 10 | - npm install 11 | 12 | # Post-install test scripts. 13 | test_script: 14 | # Output useful info for debugging. 15 | - node --version 16 | - npm --version 17 | # run tests 18 | - npm test 19 | 20 | # Don't actually build. 21 | build: off -------------------------------------------------------------------------------- /bin/npm-install-retry: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var program = require('commander'); 4 | var run = require('../'); 5 | 6 | var npmargs = process.argv.indexOf('--') === -1 ? '' : process.argv.slice(process.argv.indexOf('--')+1); 7 | var retryargs = process.argv.indexOf('--') === -1 ? process.argv : process.argv.slice(0, process.argv.indexOf('--')); 8 | 9 | program 10 | .version(require(__dirname + '/../package.json').version) 11 | .option('-a, --attempts [10]', 'Number of attempts before fail [10]', 10) 12 | .option('-w, --wait [500]', 'Milliseconds to wait between tries [10]', 10) 13 | .parse(retryargs); 14 | 15 | run('npm install', npmargs, {wait: program.wait, attempts: program.attempts}, function (err, res) { 16 | if (err) return process.exit(1); 17 | process.exit(res.exitCode); 18 | }); 19 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('colors'); 2 | var exec = require('child_process').exec; 3 | 4 | module.exports = function (command, args, options, callback) { 5 | var times = 1; 6 | function run () { 7 | var runCmd = command + ' ' + (args || []).join(' '); 8 | console.log(('attempt ' + times).bold.green + ': ' + runCmd); 9 | 10 | process.env.npm_config_color = 0; 11 | 12 | var attempt = exec(runCmd, function (err, stdout, stderr) { 13 | matchers = [/npm ERR\! cb\(\) never called\!/ig, /npm ERR\! errno ECONNRESET/ig, 14 | /npm ERR\! shasum check failed/ig, /npm ERR\! code EINTEGRITY/ig]; 15 | var match = matchers.some(function (matcher) { 16 | return stdout.match(matcher) || stderr.match(matcher); 17 | }); 18 | if (match) { 19 | if (times >= options.attempts) { 20 | return callback(new Error('too many attempts')); 21 | } 22 | times++; 23 | return setTimeout(run, options.wait); 24 | } 25 | return callback(null, {times: times, stdout: stdout, exitCode: (err && err.code) || 0}); 26 | }); 27 | 28 | attempt.stdout.pipe(process.stdout); 29 | attempt.stderr.pipe(process.stderr); 30 | } 31 | run(); 32 | }; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-install-retry", 3 | "description": "retry npm install when it fails", 4 | "version": "2.0.0", 5 | "repository": { 6 | "url": "git://github.com/jfromaniello/npm-install-retry.git" 7 | }, 8 | "author": { 9 | "name": "José F. Romaniello", 10 | "email": "jfromaniello@gmail.com", 11 | "url": "http://joseoncode.com" 12 | }, 13 | "bin": { 14 | "npm-install-retry": "./bin/npm-install-retry" 15 | }, 16 | "scripts": { 17 | "test": "mocha" 18 | }, 19 | "dependencies": { 20 | "colors": "~0.6.0-1", 21 | "commander": "~1.1.1" 22 | }, 23 | "devDependencies": { 24 | "mocha": "~1.11.0", 25 | "chai": "~1.7.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/main.tests.js: -------------------------------------------------------------------------------- 1 | var npm_install_retry = require('../'); 2 | var expect = require('chai').expect; 3 | 4 | function Fail3TimesCommand(command) { 5 | this.times = 0; 6 | this.command = command; 7 | } 8 | var isWin = /^win/.test(process.platform); 9 | var cbErrorCommand = isWin ? 'echo npm ERR! cb() never called! 1>&2' : 'echo npm ERR\\! cb\\(\\) never called\\! 1>&2'; 10 | 11 | Fail3TimesCommand.prototype.toString = function() { 12 | if(this.times < 3) { 13 | this.times++; 14 | return this.command; 15 | } 16 | return 'echo peace and love'; 17 | }; 18 | 19 | describe('npm-install-retry', function () { 20 | 21 | function testRetries(command) { 22 | it('should retry after "' + command + '" failed', function (done) { 23 | npm_install_retry(new Fail3TimesCommand(command), '', { wait: 0, attempts: 10 }, function (err, result) { 24 | if (err) return done(err); 25 | expect(result.times).to.eql(4); 26 | done(); 27 | }); 28 | }); 29 | } 30 | 31 | testRetries(cbErrorCommand); 32 | testRetries('echo npm ERR! errno ECONNRESET'); 33 | testRetries('echo npm ERR! shasum check failed'); 34 | testRetries('echo npm ERR! code EINTEGRITY'); 35 | 36 | it('should fail if it fail all attempts', function (done) { 37 | npm_install_retry(cbErrorCommand, '', { wait: 0, attempts: 10 }, function (err, result) { 38 | expect(err.message).to.eql('too many attempts'); 39 | done(); 40 | }); 41 | }); 42 | 43 | it('should have npm_config_color false', function (done) { 44 | var endOfLine = require('os').EOL; 45 | var command = isWin ? 'echo %npm_config_color%' : 'echo $npm_config_color'; 46 | npm_install_retry(command, '', { wait: 0, attempts: 10 }, function (err, result) { 47 | if (err) return done(err); 48 | expect(result.stdout.split(endOfLine)[0].trim()).to.eql('0'); 49 | done(); 50 | }); 51 | }); 52 | 53 | }); --------------------------------------------------------------------------------