From 6416a1dc12418d7e4c637d05ab194b16d5f02df0 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Tue, 4 Sep 2018 11:51:58 -0700 Subject: [PATCH] Retry npm install in CI (#184) --- .circleci/config.yml | 41 ++++++++++++++--------- .circleci/npm-install-retry.js | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 15 deletions(-) create mode 100755 .circleci/npm-install-retry.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 55ffec78..80dcf7e6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -59,16 +59,17 @@ jobs: - image: 'node:6' user: node steps: &unit_tests_steps - - checkout + - checkout - run: &npm_install_and_link name: Install and link the module command: |- mkdir -p /home/node/.npm-global - npm install + ./.circleci/npm-install-retry.js environment: NPM_CONFIG_PREFIX: /home/node/.npm-global - run: npm test - run: node_modules/.bin/codecov + node8: docker: - image: 'node:8' @@ -90,13 +91,15 @@ jobs: name: Link the module being tested to the samples. command: | cd samples/ - npm install npm link ../ + ./../.circleci/npm-install-retry.js environment: NPM_CONFIG_PREFIX: /home/node/.npm-global - run: name: Run linting. command: npm run lint + environment: + NPM_CONFIG_PREFIX: /home/node/.npm-global docs: docker: - image: 'node:8' @@ -104,9 +107,7 @@ jobs: steps: - checkout - run: *npm_install_and_link - - run: - name: Build documentation. - command: npm run docs + - run: npm run docs sample_tests: docker: - image: 'node:8' @@ -116,9 +117,11 @@ jobs: - run: name: Decrypt credentials. command: | - openssl aes-256-cbc -d -in .circleci/key.json.enc \ + if ! [[ -z "${SYSTEM_TESTS_ENCRYPTION_KEY}" ]]; then + openssl aes-256-cbc -d -in .circleci/key.json.enc \ -out .circleci/key.json \ -k "${SYSTEM_TESTS_ENCRYPTION_KEY}" + fi - run: *npm_install_and_link - run: *samples_npm_install_and_link - run: @@ -126,13 +129,16 @@ jobs: command: npm run samples-test environment: GCLOUD_PROJECT: long-door-651 - GOOGLE_APPLICATION_CREDENTIALS: /home/node/bigquery-samples/.circleci/key.json + GOOGLE_APPLICATION_CREDENTIALS: /home/node/samples/.circleci/key.json NPM_CONFIG_PREFIX: /home/node/.npm-global - run: name: Remove unencrypted key. - command: rm .circleci/key.json + command: | + if ! [[ -z "${SYSTEM_TESTS_ENCRYPTION_KEY}" ]]; then + rm .circleci/key.json + fi when: always - working_directory: /home/node/bigquery-samples/ + working_directory: /home/node/samples/ system_tests: docker: - image: 'node:8' @@ -142,19 +148,23 @@ jobs: - run: name: Decrypt credentials. command: | - openssl aes-256-cbc -d -in .circleci/key.json.enc \ + if ! [[ -z "${SYSTEM_TESTS_ENCRYPTION_KEY}" ]]; then + openssl aes-256-cbc -d -in .circleci/key.json.enc \ -out .circleci/key.json \ -k "${SYSTEM_TESTS_ENCRYPTION_KEY}" + fi - run: *npm_install_and_link - run: name: Run system tests. command: npm run system-test environment: - GCLOUD_PROJECT: long-door-651 GOOGLE_APPLICATION_CREDENTIALS: .circleci/key.json - run: name: Remove unencrypted key. - command: rm .circleci/key.json + command: | + if ! [[ -z "${SYSTEM_TESTS_ENCRYPTION_KEY}" ]]; then + rm .circleci/key.json + fi when: always publish_npm: docker: @@ -162,5 +172,6 @@ jobs: user: node steps: - checkout - - run: 'echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc' - - run: npm publish + - run: ./.circleci/npm-install-retry.js + - run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc + - run: npm publish --access=public diff --git a/.circleci/npm-install-retry.js b/.circleci/npm-install-retry.js new file mode 100755 index 00000000..ae3220d7 --- /dev/null +++ b/.circleci/npm-install-retry.js @@ -0,0 +1,60 @@ +#!/usr/bin/env node + +let spawn = require('child_process').spawn; + +// +//USE: ./index.js [... NPM ARGS] +// + +let timeout = process.argv[2] || 60000; +let attempts = process.argv[3] || 3; +let args = process.argv.slice(4); +if (args.length === 0) { + args = ['install']; +} + +(function npm() { + let timer; + args.push('--verbose'); + let proc = spawn('npm', args); + proc.stdout.pipe(process.stdout); + proc.stderr.pipe(process.stderr); + proc.stdin.end(); + proc.stdout.on('data', () => { + setTimer(); + }); + proc.stderr.on('data', () => { + setTimer(); + }); + + // side effect: this also restarts when npm exits with a bad code even if it + // didnt timeout + proc.on('close', (code, signal) => { + clearTimeout(timer); + if (code || signal) { + console.log('[npm-are-you-sleeping] npm exited with code ' + code + ''); + + if (--attempts) { + console.log('[npm-are-you-sleeping] restarting'); + npm(); + } else { + console.log('[npm-are-you-sleeping] i tried lots of times. giving up.'); + throw new Error("npm install fails"); + } + } + }); + + function setTimer() { + clearTimeout(timer); + timer = setTimeout(() => { + console.log('[npm-are-you-sleeping] killing npm with SIGTERM'); + proc.kill('SIGTERM'); + // wait a couple seconds + timer = setTimeout(() => { + // its it's still not closed sigkill + console.log('[npm-are-you-sleeping] killing npm with SIGKILL'); + proc.kill('SIGKILL'); + }, 2000); + }, timeout); + } +})();