Skip to content

Commit 6a2e93c

Browse files
author
Kent C. Dodds
authored
fix(error): when an error occurs, stop processes (#53)
* fix(error): when an error occurs, stop processes This is a significant refactor to make it so when an error occurs in one parallel script, the others are killed and also to ensure that an error in one serial script will result in the others not being executed. Closes #48 * docs(contributors): update/add contributors contributions This updates the contributions for: - @kentcdodds - @tleunen - @Hypercubed It adds the following new contributors: - @jisaacks - @boneskull - @RobinMalfait - @edm00se - @SamVerschueren Thanks for the contributions everyone!
1 parent 00f9cba commit 6a2e93c

File tree

8 files changed

+364
-87
lines changed

8 files changed

+364
-87
lines changed

.all-contributorsrc

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"doc",
1818
"infra",
1919
"example",
20-
"video"
20+
"video",
21+
"review"
2122
]
2223
},
2324
{
@@ -87,7 +88,8 @@
8788
"contributions": [
8889
"bug",
8990
"code",
90-
"test"
91+
"test",
92+
"review"
9193
]
9294
},
9395
{
@@ -96,7 +98,8 @@
9698
"avatar_url": "https://avatars.githubusercontent.com/u/509946?v=3",
9799
"profile": "http://www.hypercubed.com",
98100
"contributions": [
99-
"example"
101+
"example",
102+
"review"
100103
]
101104
},
102105
{
@@ -108,6 +111,42 @@
108111
"code",
109112
"test"
110113
]
114+
},
115+
{
116+
"login": "boneskull",
117+
"name": "Christopher Hiller",
118+
"avatar_url": "https://avatars.githubusercontent.com/u/924465?v=3",
119+
"profile": "https://boneskull.com",
120+
"contributions": [
121+
"review"
122+
]
123+
},
124+
{
125+
"login": "RobinMalfait",
126+
"name": "Robin Malfait",
127+
"avatar_url": "https://avatars.githubusercontent.com/u/1834413?v=3",
128+
"profile": "robinmalfait.com",
129+
"contributions": [
130+
"example"
131+
]
132+
},
133+
{
134+
"login": "edm00se",
135+
"name": "Eric McCormick",
136+
"avatar_url": "https://avatars.githubusercontent.com/u/622118?v=3",
137+
"profile": "https://ericmccormick.io",
138+
"contributions": [
139+
"review"
140+
]
141+
},
142+
{
143+
"login": "SamVerschueren",
144+
"name": "Sam Verschueren",
145+
"avatar_url": "https://avatars.githubusercontent.com/u/1913805?v=3",
146+
"profile": "https://twitter.com/SamVerschueren",
147+
"contributions": [
148+
"review"
149+
]
111150
}
112151
]
113152
}

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ All the benefits of npm scripts without the cost of a bloated package.json and l
99
[![downloads][downloads-badge]][npm-stat]
1010
[![MIT License][license-badge]][LICENSE]
1111

12-
[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors)
12+
[![All Contributors](https://img.shields.io/badge/all_contributors-14-orange.svg?style=flat-square)](#contributors)
1313
[![PRs Welcome][prs-badge]][prs]
1414
[![Donate][donate-badge]][donate]
1515
[![Code of Conduct][coc-badge]][coc]
@@ -385,9 +385,9 @@ benefits of npm scripts I wanted to keep).
385385
Thanks goes to these people ([emoji key][emojis]):
386386

387387
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
388-
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub>Kent C. Dodds</sub>](http://kent.doddsfamily.us)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=kentcdodds) [📖](https://github.com/kentcdodds/p-s/commits?author=kentcdodds) 🚇 💡 📹 | [<img src="https://avatars.githubusercontent.com/u/532272?v=3" width="100px;"/><br /><sub>David Wells</sub>](http://davidwells.io)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=DavidWells) | [<img src="https://avatars.githubusercontent.com/u/802242?v=3" width="100px;"/><br /><sub>Abhishek Shende</sub>](https://twitter.com/abhishekisnot)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=abhishekisnot) [⚠️](https://github.com/kentcdodds/p-s/commits?author=abhishekisnot) | [<img src="https://avatars.githubusercontent.com/u/185649?v=3" width="100px;"/><br /><sub>Rowan Oulton</sub>](http://travelog.io)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) [📖](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) [⚠️](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) | [<img src="https://avatars.githubusercontent.com/u/1915716?v=3" width="100px;"/><br /><sub>Gilad Goldberg</sub>](https://github.com/giladgo)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=giladgo) | [<img src="https://avatars.githubusercontent.com/u/14267457?v=3" width="100px;"/><br /><sub>Tim McGee</sub>](https://github.com/tim-mcgee)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=tim-mcgee) [📖](https://github.com/kentcdodds/p-s/commits?author=tim-mcgee) | [<img src="https://avatars.githubusercontent.com/u/175264?v=3" width="100px;"/><br /><sub>Nik Butenko</sub>](http://butenko.me)<br />💡 [💻](https://github.com/kentcdodds/p-s/commits?author=nkbt) |
388+
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub>Kent C. Dodds</sub>](http://kent.doddsfamily.us)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=kentcdodds) [📖](https://github.com/kentcdodds/p-s/commits?author=kentcdodds) 🚇 💡 📹 👀 | [<img src="https://avatars.githubusercontent.com/u/532272?v=3" width="100px;"/><br /><sub>David Wells</sub>](http://davidwells.io)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=DavidWells) | [<img src="https://avatars.githubusercontent.com/u/802242?v=3" width="100px;"/><br /><sub>Abhishek Shende</sub>](https://twitter.com/abhishekisnot)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=abhishekisnot) [⚠️](https://github.com/kentcdodds/p-s/commits?author=abhishekisnot) | [<img src="https://avatars.githubusercontent.com/u/185649?v=3" width="100px;"/><br /><sub>Rowan Oulton</sub>](http://travelog.io)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) [📖](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) [⚠️](https://github.com/kentcdodds/p-s/commits?author=rowanoulton) | [<img src="https://avatars.githubusercontent.com/u/1915716?v=3" width="100px;"/><br /><sub>Gilad Goldberg</sub>](https://github.com/giladgo)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=giladgo) | [<img src="https://avatars.githubusercontent.com/u/14267457?v=3" width="100px;"/><br /><sub>Tim McGee</sub>](https://github.com/tim-mcgee)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=tim-mcgee) [📖](https://github.com/kentcdodds/p-s/commits?author=tim-mcgee) | [<img src="https://avatars.githubusercontent.com/u/175264?v=3" width="100px;"/><br /><sub>Nik Butenko</sub>](http://butenko.me)<br />💡 [💻](https://github.com/kentcdodds/p-s/commits?author=nkbt) |
389389
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
390-
| [<img src="https://avatars.githubusercontent.com/u/1972567?v=3" width="100px;"/><br /><sub>Tommy</sub>](http://www.tommyleunen.com)<br />[🐛](https://github.com/kentcdodds/p-s/issues?q=author%3Atleunen) [💻](https://github.com/kentcdodds/p-s/commits?author=tleunen) [⚠️](https://github.com/kentcdodds/p-s/commits?author=tleunen) | [<img src="https://avatars.githubusercontent.com/u/509946?v=3" width="100px;"/><br /><sub>Jayson Harshbarger</sub>](http://www.hypercubed.com)<br />💡 | [<img src="https://avatars.githubusercontent.com/u/1355481?v=3" width="100px;"/><br /><sub>JD Isaacks</sub>](http://www.jisaacks.com)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=jisaacks) [⚠️](https://github.com/kentcdodds/p-s/commits?author=jisaacks) |
390+
| [<img src="https://avatars.githubusercontent.com/u/1972567?v=3" width="100px;"/><br /><sub>Tommy</sub>](http://www.tommyleunen.com)<br />[🐛](https://github.com/kentcdodds/p-s/issues?q=author%3Atleunen) [💻](https://github.com/kentcdodds/p-s/commits?author=tleunen) [⚠️](https://github.com/kentcdodds/p-s/commits?author=tleunen) 👀 | [<img src="https://avatars.githubusercontent.com/u/509946?v=3" width="100px;"/><br /><sub>Jayson Harshbarger</sub>](http://www.hypercubed.com)<br />💡 👀 | [<img src="https://avatars.githubusercontent.com/u/1355481?v=3" width="100px;"/><br /><sub>JD Isaacks</sub>](http://www.jisaacks.com)<br />[💻](https://github.com/kentcdodds/p-s/commits?author=jisaacks) [⚠️](https://github.com/kentcdodds/p-s/commits?author=jisaacks) | [<img src="https://avatars.githubusercontent.com/u/924465?v=3" width="100px;"/><br /><sub>Christopher Hiller</sub>](https://boneskull.com)<br />👀 | [<img src="https://avatars.githubusercontent.com/u/1834413?v=3" width="100px;"/><br /><sub>Robin Malfait</sub>](robinmalfait.com)<br />💡 | [<img src="https://avatars.githubusercontent.com/u/622118?v=3" width="100px;"/><br /><sub>Eric McCormick</sub>](https://ericmccormick.io)<br />👀 | [<img src="https://avatars.githubusercontent.com/u/1913805?v=3" width="100px;"/><br /><sub>Sam Verschueren</sub>](https://twitter.com/SamVerschueren)<br />👀 |
391391
<!-- ALL-CONTRIBUTORS-LIST:END -->
392392

393393
This project follows the [all-contributors][all-contributors] specification.

other/ERRORS_AND_WARNINGS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,21 @@ This happens when you use the `--require` flag and the module you specify cannot
2727

2828
1. Check that you spelled the module correctly
2929
2. Check that the module you wish to require is require-able
30+
31+
## Failed with exit code
32+
33+
This means that one of the scripts p-s tried to run resulted in a non-zero exit code (a failing exit code)
34+
35+
### To Fix:
36+
37+
Try to run the script without `p-s` and verify that the script is working. If not, fix that. If it's working without `p-s` it could be a problem with `p-s`. Please file an issue.
38+
39+
## Emitted an error
40+
41+
This means that the child process for the specified script emitted an error.
42+
43+
### To Fix:
44+
45+
Look at the error and try to figure out why the script would be failing.
46+
47+
Try to run the script without `p-s` and verify that the script is working. If not, fix that. If it's working without `p-s` it could be a problem with `p-s`. Please file an issue.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"license": "MIT",
2222
"dependencies": {
2323
"arrify": "1.0.1",
24-
"async": "2.0.1",
24+
"bluebird": "3.4.6",
2525
"colors": "1.1.2",
2626
"commander": "2.9.0",
2727
"find-up": "1.1.2",
@@ -30,7 +30,7 @@
3030
"omelette": "0.3.1",
3131
"prefix-matches": "0.0.9",
3232
"shell-escape": "0.2.0",
33-
"spawn-command": "0.0.2"
33+
"spawn-command-with-kill": "1.0.0"
3434
},
3535
"devDependencies": {
3636
"all-contributors-cli": "3.0.6",

src/bin/p-s.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,9 @@ function loadAndRun(scriptsAndArgs, psConfig) {
6060
parallel: scriptsAndArgs.parallel,
6161
logLevel: program.logLevel,
6262
}),
63-
}, result => {
64-
if (result.error) {
65-
log.error(result.error)
66-
process.exit(FAIL_CODE)
67-
}
68-
process.exit(result.code)
63+
}).catch(error => {
64+
log.error(error)
65+
process.exitCode = error.code || FAIL_CODE
6966
})
7067
}
7168

src/get-logger.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ function getLogger(logLevel) {
3131

3232
function getMessage(first, ...rest) {
3333
if (isPlainObject(first) && first.message && first.ref) {
34-
return [...arrify(first.message), getLink(first.ref), ...rest]
34+
return [
35+
...arrify(first.message),
36+
getLink(first.ref),
37+
first.error,
38+
...rest,
39+
].filter(i => !!i)
3540
} else {
3641
return [first, ...rest]
3742
}

src/index.js

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,125 @@
1-
import spawn from 'spawn-command'
2-
import async from 'async'
1+
import spawn from 'spawn-command-with-kill'
2+
import Promise from 'bluebird'
33
import colors from 'colors/safe'
4-
import {isString, find, clone} from 'lodash'
4+
import {isString, clone} from 'lodash'
55
import {sync as findUpSync} from 'find-up'
66
import managePath from 'manage-path'
77
import arrify from 'arrify'
88
import getScriptToRun from './get-script-to-run'
99
import getScriptsFromConfig from './get-scripts-from-config'
1010
import getLogger from './get-logger'
1111

12-
const noop = () => {} // eslint-disable-line func-style, no-empty-function
12+
const NON_ERROR = 0
1313

1414
export default runPackageScripts
1515

16-
function runPackageScripts({scriptConfig, scripts, args, options = {}}, callback = noop) {
16+
function runPackageScripts({scriptConfig, scripts, args, options = {}}) {
1717
if (scripts.length === 0) {
1818
scripts = ['default']
1919
}
2020
const scriptNames = arrify(scripts)
21-
const method = options.parallel ? 'map' : 'mapSeries'
22-
async[method](scriptNames, (scriptName, cb) => {
23-
const child = runPackageScript({scriptConfig, options, scriptName, args})
24-
if (child.on) {
25-
child.on('exit', exitCode => cb(null, exitCode))
26-
} else {
27-
cb(child)
28-
}
29-
}, (err, results) => {
30-
if (err) {
31-
callback({error: err})
32-
} else {
33-
const NON_ERROR = 0
34-
const result = find(results, r => r !== NON_ERROR)
35-
callback({code: result})
21+
if (options.parallel) {
22+
return runParallel()
23+
} else {
24+
return runSeries()
25+
}
26+
27+
function runSeries() {
28+
return scriptNames.reduce((res, scriptName) => {
29+
return res.then(() => (
30+
runPackageScript({scriptConfig, options, scriptName, args})
31+
))
32+
}, Promise.resolve())
33+
}
34+
35+
function runParallel() {
36+
const results = scriptNames.map(script => ({script, code: undefined}))
37+
let aborted = false
38+
39+
const promises = scriptNames.map(scriptName => {
40+
return runPackageScript({scriptConfig, options, scriptName, args})
41+
})
42+
43+
const allPromise = Promise.all(promises.map((promise, index) => {
44+
return promise.then(code => {
45+
if (!aborted) {
46+
results[index].code = code
47+
}
48+
})
49+
})).then(() => results)
50+
51+
allPromise.catch(() => {
52+
/* istanbul ignore if */
53+
if (aborted) {
54+
// this is very unlikely to happen
55+
} else {
56+
abortAll()
57+
}
58+
})
59+
60+
return allPromise
61+
62+
function abortAll() {
63+
aborted = true
64+
promises.forEach(p => p.abort())
3665
}
37-
})
66+
}
3867
}
3968

69+
4070
function runPackageScript({scriptConfig, options, scriptName, args}) {
4171
const scripts = getScriptsFromConfig(scriptConfig, scriptName)
4272
const script = getScriptToRun(scripts, scriptName)
4373
if (!isString(script)) {
44-
return {
74+
return Promise.reject({
4575
message: colors.red(
4676
`Scripts must resolve to strings. There is no script that can be resolved from "${scriptName}"`
4777
),
4878
ref: 'missing-script',
49-
}
79+
})
5080
}
5181
const command = [script, args].join(' ').trim()
5282
const log = getLogger(getLogLevel(options))
5383
log.info(colors.gray('p-s executing: ') + colors.green(command))
54-
return spawn(command, {stdio: 'inherit', env: getEnv()})
84+
let child
85+
const promise = new Promise((resolve, reject) => {
86+
child = spawn(command, {stdio: 'inherit', env: getEnv()})
87+
88+
child.on('error', error => {
89+
child = null
90+
reject({
91+
message: colors.red(
92+
`The script called "${scriptName}" which runs "${command}" emitted an error`
93+
),
94+
ref: 'emitted-an-error',
95+
error,
96+
})
97+
})
98+
99+
child.on('close', code => {
100+
child = null
101+
if (code === NON_ERROR) {
102+
resolve(code)
103+
} else {
104+
reject({
105+
message: colors.red(
106+
`The script called "${scriptName}" which runs "${command}" failed with exit code ${code}`
107+
),
108+
ref: 'failed-with-exit-code',
109+
code,
110+
})
111+
}
112+
})
113+
})
114+
115+
promise.abort = function abort() {
116+
if (child !== null) {
117+
child.kill()
118+
child = null
119+
}
120+
}
121+
122+
return promise
55123
}
56124

57125
function getLogLevel({silent, logLevel}) {

0 commit comments

Comments
 (0)