From 2086e5e93194355cbbea9167ac18646ed978caf1 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Tue, 4 Sep 2018 10:19:53 -0700 Subject: [PATCH] Retry npm install in CI --- .circleci/config.yml | 49 +++++++++++++++------------ .circleci/npm-install-retry.js | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 20 deletions(-) create mode 100755 .circleci/npm-install-retry.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 2f82599b1..80dcf7e67 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' @@ -81,7 +82,7 @@ jobs: steps: *unit_tests_steps lint: docker: - - image: 'node:10' + - image: 'node:8' user: node steps: - checkout @@ -90,8 +91,8 @@ 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: @@ -101,26 +102,26 @@ jobs: NPM_CONFIG_PREFIX: /home/node/.npm-global docs: docker: - - image: 'node:10' + - image: 'node:8' user: node steps: - checkout - run: *npm_install_and_link - - run: - name: Build documentation. - command: npm run docs + - run: npm run docs sample_tests: docker: - - image: 'node:10' + - image: 'node:8' user: node steps: - checkout - 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: @@ -128,41 +129,49 @@ jobs: command: npm run samples-test environment: GCLOUD_PROJECT: long-door-651 - GOOGLE_APPLICATION_CREDENTIALS: /home/node/spanner-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/spanner-samples + working_directory: /home/node/samples/ system_tests: docker: - - image: 'node:10' + - image: 'node:8' user: node steps: - checkout - 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: - - image: 'node:10' + - image: 'node:8' 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 000000000..ae3220d73 --- /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); + } +})();