Skip to content

Commit

Permalink
Handle EPIPE on stdout gracefully
Browse files Browse the repository at this point in the history
  • Loading branch information
arbscht committed Apr 12, 2019
1 parent 8a671f1 commit 3057157
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 0 deletions.
@@ -0,0 +1,56 @@
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 04 Apr 2019 10:02:38 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 1181
Cache-Control: max-age=3600, public
Content-Disposition: inline
Etag: W/"78309fbf8af4479c47eca65b0c5e3f51"
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Request-Id: MdyDA59Ki6
X-Runtime: 0.089005
X-Ua-Compatible: IE=edge
X-Xss-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000
Content-Security-Policy: object-src 'none'; worker-src https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net https://gitlab.com blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://www.gstatic.com/recaptcha/ https://apis.google.com; style-src 'self' 'unsafe-inline' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net; img-src * data: blob:; frame-src 'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com https://*.codesandbox.io; frame-ancestors 'self'; connect-src 'self' https://assets.gitlab-static.net https://gl-canary.freetls.fastly.net wss://gitlab.com https://sentry.gitlab.net https://customers.gitlab.com https://snowplow.trx.gitlab.net

{
"name": "kanban",
"version": "0.0.1",
"repository": "gitlab.com/leanlabsio/kanban",
"scripts": {
"install": "npm install",
"build": "grunt build",
"watch": "grunt watch"
},
"devDependencies": {
"grunt": "~0.4.1",
"grunt-cli": "~0.1.13",
"grunt-contrib-copy": "^0.5.0",
"grunt-contrib-concat": "~0.5.0",
"grunt-contrib-watch": "~0.5.3",
"grunt-contrib-uglify": "~0.7.0",
"grunt-sass": "1.0.0",
"grunt-contrib-connect": "~0.8.0",
"grunt-connect-proxy": "~0.1.11"
},
"dependencies": {
"angular": "=1.5.6",
"angular-lodash": "https://github.com/EMSSConsulting/angular-lodash.git#68a726c",
"foundation-sites": "5.5.2",
"angular-foundation": "https://github.com/pineconellc/angular-foundation.git#8f3f260",
"angular-loading-bar": "=0.5.2",
"angular-storage": "=0.0.6",
"angular-ui-router": "=0.3.0",
"angularjs-datepicker": "=0.2.15",
"font-awesome": "=4.6.3",
"markdown-it": "=5.0.2",
"markdown-it-emoji": "=1.1.0",
"ng-sortable": "=1.3.6",
"sass-flex-mixin": "=1.0.3",
"lodash": "=4.13.1",
"twemoji": "=2.1.0",
"angular-file-upload": "=2.3.4"
}
}
@@ -0,0 +1,18 @@
HTTP/1.1 200 OK
Date: Thu, 04 Apr 2019 10:01:49 GMT
Content-Type: application/vnd.npm.install-v1+json
Content-Length: 4072
Connection: keep-alive
Set-Cookie: __cfduid=df244f3eed75eb2c0e38ba688ab565fc41554372108; expires=Fri, 03-Apr-20 10:01:48 GMT; path=/; domain=.registry.yarnpkg.com; HttpOnly
CF-Cache-Status: MISS
Cache-Control: max-age=300
CF-Ray: 4c22716d384c0b20-SYD
Accept-Ranges: bytes
ETag: "49fffc656197ace1f624570132931c23"
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Last-Modified: Thu, 01 Nov 2018 01:11:09 GMT
Vary: accept-encoding, accept
x-amz-meta-rev: 3-72d30e72712ca5e8a55f6613d7a30236
Server: cloudflare

{"versions":{"1.0.0":{"name":"is-pnp","version":"1.0.0","bin":{"is-pnp":"./bin.js"},"_hasShrinkwrap":false,"directories":{},"dist":{"shasum":"47d3d7151df242eb7ca4768fc2e7f6228871899a","integrity":"sha512-Lx0Sh5h20HtLz+xnlh4NRhQ6w231tGN4yuU3oBi6vn7oSbiIk2V4sqoIn00bczm9ojwkFPzzFUeSOIKEgYpszA==","tarball":"https://registry.npmjs.org/is-pnp/-/is-pnp-1.0.0.tgz","fileCount":5,"unpackedSize":2268,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2lGmCRA9TVsSAnZWagAARHYP/1botUA//2RoqJEP3nHB\nnQ0G1qhSYL685aLgZTWJW9pS3ucPzxxeofbnByOsbSjUqycwsHBqL/DDeYsq\nz3b0dmfcfESK+CSafYECSnj8kPJH5N5VU2UXs+A+wLEM0D0nSe4wfJLVptA0\nrOpeYrkfwX8ZC1EGLGvQM4cMqpk3Ji6RBZHtD0U/ttYR3A0vP5P76oO2/tBl\n3QSK3nJSpU83OkMBPVY3hnDrGkCjJqwvcqWoWUG0/BtHS5KYD99yrxFAzT5f\nMhtqCZiediDcGtrMhP5b0VAkqdrwFPg/tZPpgqYCevR+2ipWhkxhD0Z4yyx1\n2AuJnEoQKRiBiFPIv5aiJZD9Jj00iey4tdQ1NcXnRtAA16Lo0t4MX/OnkyJW\nBfcSd795mVpe4AO9u6bqMDZfkQrBRunxa7OKKCEXUGihm0YNtTWKat5ym1ia\n9H9Bwlgm3GoFAuHQMu07ZEwidLpWoQfjrTUOufw+/rKNSIRoMNuL3Z/8i4Hj\nazYRMgdzb9rkf6+F7LLJKnyZaDY/bS/KIb61LdkfkX8JCcKUfhVAd5KIvI4e\nAwLAUfHwWM2u2smbexFzvgvXnNFmVrcCGBbMcIrQ+JfiJ+izFVJ7YyLY+Fpt\nwxbV0Vug6gyYDyDtjFCdeL2wFmgR3+nASXWFY4CZ+ZoFX+ontSZ4U5b9PTIs\n1ojq\r\n=DAoM\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">=6"}},"1.0.1":{"name":"is-pnp","version":"1.0.1","bin":{"is-pnp":"./sources/cli.js"},"_hasShrinkwrap":false,"directories":{},"dist":{"shasum":"ec0e4e443287214ce90bcb6fcd112d20de51f120","integrity":"sha512-uwFQ9SabYoNJwZnDqHUs+WTPvegmoLHjVQqkX4+MiKSjhRBRk+n/zKBAex4yvWnzbN352W7FUuYX8L5yEVmiVA==","tarball":"https://registry.npmjs.org/is-pnp/-/is-pnp-1.0.1.tgz","fileCount":5,"unpackedSize":2276,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2lJICRA9TVsSAnZWagAAX2IP/jE0+3aAdO7cu9DXNJnb\njoi3rDo8CRfARhWbJRhUj7omDxeP37U3dIIkK69AL8XZSPPUQ8o2uH1AOjZB\niI8cp0IX4bgalRXzEWPj3v8l3z6/BwJoUXy7N5ugdxron+AYj4kS4I1jI6Pa\nUo3z096sc8zAgXFE00RiKwUBGNw+4xpg0yVri/vXpezsahi8r4qZbrc9/hr3\nWzoIK6GwQnGpn5FEhdiuPbB0QiZvalYHn5v2IWtMpQG4ed6jM/vSBAqKqkFv\n95McUD13wz7iJ59BaJHwbypD/WFWz34ZBPZ8QpONk6UAJ2IZwh6J2Wj44k7a\nNtiEj3xo3A5stikfELI//H0AruTI6sSI++EJ8YvHQUj73s6kG0Z/FzPptgKp\nKYLSqXNAC/zHiId1s7uj+jsCLXIm4G2bPQUTsDMfejDS6TfjAVO2COxRF34q\ncLGzYFL1/R+/hO4ckoLl7vz6Vm/zSlMaxZPDUFDy8d0ScxHZOL+760cdIhjT\nr1XGyXAmyjGLWTHvA6sE5X0H0PgghQErsLYXi679KBdnZjN2+Xcmv0pNIqbg\nwS7SBjV+KGpc1PnwtZdfvZcegvY+252zsBvRye9/hcQ0Rdmetbod8XicA3Bw\nuTLC2N9opHeTI7a9bKrACXkSApJftjzYSfnycb27r9bgIAY6BeNhr7JFEMKV\nnxUD\r\n=8Blc\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">=6"}},"1.0.2":{"name":"is-pnp","version":"1.0.2","bin":{"is-pnp":"./sources/cli.js"},"_hasShrinkwrap":false,"directories":{},"dist":{"shasum":"cbe5d6ad751897822fd92539ac5cfa37c04f3852","integrity":"sha512-BzoewUq0EZFJYKlYpObv2xRdTRJQXwMLbk2Y5l8Dhl0EQGPhdPrSdBlQaAT46gIKjAkNBNScDiUDbuBCTDNCsQ==","tarball":"https://registry.npmjs.org/is-pnp/-/is-pnp-1.0.2.tgz","fileCount":5,"unpackedSize":2282,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2lKpCRA9TVsSAnZWagAAkFcP+QHfioEIu01ww57y/Jnw\neOtM4JPvx0hDMKxMBKSyvVHmjZKwqxvkAnqdyybHts7i6auvuuMKDO43oaRO\n5GY5i/u29arwBKnPgeVrC6TUnbVxiSP4KBtfzRs3YCXbQ0MfSPmXiH4nzzi/\ngWeNGutgCAbefJjB0eahVCUgO7DL+sM8kEt2iNSDM1gKUDSVq5yvsZD20tX+\n/60eLG4xtz/2zXDKjoqo+DZtvW9kdLJ6TlvbDU1mt1OBSzkTiakIe1XXymVT\n+gRC0QGKGMP+YJG+VothHez/JAagl/92c/JDsb54HkW/OkUKCHFyUAw0P3h1\n46p0vEr54rHa7wT6O0fKg7q+MN1mMOKR3wO258LQW20n+DQJK1hxcJOkk+xf\n6iOa2d6Ywox9yT9B/V555BEZ2fYbnNb4gw+oo3/YyJcZ1Ooadt5kvEyDb67+\ntOaOMBXZh7/FR8EghwBBqlOndWCD4OQYVXVCwIdf/PFyUtB1+Obd4/1H+Dkw\nmg01pi7jYHG8mKLIMNdjggWi966nJ3fZqBDLW0eRtFzCbe+8VSOPKGRSHHnI\n99SD9oY/dgmimcakZ3hInuGlMGf0UrSLjHwi24y0ae1O0AlnQz0bXU0qHAH2\nH/Npr3zjOcAULwvHqtu2l1mNv/PX8GtafyybiMaXBcERearcUN/48tbvcbFX\nZmBP\r\n=z5fE\r\n-----END PGP SIGNATURE-----\r\n"},"engines":{"node":">=6"}}},"name":"is-pnp","dist-tags":{"latest":"1.0.2"},"modified":"2018-11-01T01:11:07.975Z"}
Binary file not shown.
75 changes: 75 additions & 0 deletions __tests__/pipe.js
@@ -0,0 +1,75 @@
/* @flow */
/* eslint max-len: 0 */

import execa from 'execa';
import getStream from 'get-stream';
import {sh} from 'puka';
import makeTemp from './_temp.js';
import * as fs from '../src/util/fs.js';

const path = require('path');

function runYarnStreaming(args: Array<string> = [], options: Object = {}): execa.ExecaChildPromise {
if (!options['env']) {
options['env'] = {...process.env};
options['extendEnv'] = false;
}
options['env']['FORCE_COLOR'] = 0;

return execa.shell(sh`${path.resolve(__dirname, '../bin/yarn')} ${args}`, options);
}

test('terminate console stream quietly on EPIPE', async () => {
const cwd = await makeTemp();
const packageJsonPath = path.join(cwd, 'package.json');
const initialManifestFile = JSON.stringify({name: 'test', license: 'ISC', version: '1.0.0'});

await fs.writeFile(packageJsonPath, initialManifestFile);

const {stdout, stderr} = runYarnStreaming(['versions'], {cwd});

stdout.destroy();

let stderrOutput = '';
await getStream(stderr).then(e => {
stderrOutput = e;
});

expect(stderrOutput.toString()).not.toMatch(/EPIPE/);
});

test('terminate console stream preserving zero exit code on EPIPE', async () => {
const cwd = await makeTemp();
const packageJsonPath = path.join(cwd, 'package.json');
const initialManifestFile = JSON.stringify({name: 'test', license: 'ISC', version: '1.0.0'});

await fs.writeFile(packageJsonPath, initialManifestFile);

const proc = runYarnStreaming(['versions'], {cwd});

const {stdout} = proc;

stdout.destroy();

proc.on('exit', function(code, signal) {
expect(code).toEqual(0);
});
});

test('terminate console stream preserving non-zero exit code on EPIPE', async () => {
const cwd = await makeTemp();
const packageJsonPath = path.join(cwd, 'package.json');
const initialManifestFile = JSON.stringify({name: 'test', license: 'ISC', version: '1.0.0'});

await fs.writeFile(packageJsonPath, initialManifestFile);

const proc = runYarnStreaming(['add'], {cwd});

const {stdout} = proc;

stdout.destroy();

proc.on('exit', function(code, signal) {
expect(code).toEqual(1);
});
});
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -83,6 +83,7 @@
"execa": "^0.11.0",
"fancy-log": "^1.3.2",
"flow-bin": "^0.66.0",
"get-stream": "^5.0.0",
"git-release-notes": "^3.0.0",
"gulp": "^4.0.0",
"gulp-babel": "^7.0.0",
Expand Down
8 changes: 8 additions & 0 deletions src/cli/index.js
Expand Up @@ -26,6 +26,14 @@ import handleSignals from '../util/signal-handler.js';
import {boolify, boolifyWithDefault} from '../util/conversion.js';
import {ProcessTermError} from '../errors';

process.stdout.prependListener('error', err => {
// swallow err only if downstream consumer process closed pipe early
if (err.code === 'EPIPE' || err.code === 'ERR_STREAM_DESTROYED') {
return;
}
throw err;
});

function findProjectRoot(base: string): string {
let prev = null;
let dir = base;
Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Expand Up @@ -3281,6 +3281,13 @@ get-stream@^4.0.0:
dependencies:
pump "^3.0.0"

get-stream@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.0.0.tgz#7d1f137576da207fa83d843f44824a70ef999b5f"
integrity sha512-frc9F97ehylll1YI31eJRw5M21M86GCItj5U3S3hOEUa6JG6LtcvRKYDAtV/9E5lVasY8QJSPVsrMg/mbCSP5w==
dependencies:
pump "^3.0.0"

get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
Expand Down

0 comments on commit 3057157

Please sign in to comment.