Skip to content

Commit 583a253

Browse files
authored
Add error statuses (#1271)
1 parent 0548f7e commit 583a253

File tree

8 files changed

+70
-28
lines changed

8 files changed

+70
-28
lines changed

packages/build/src/core/commands.js

+12-13
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ const execa = require('execa')
44
const pReduce = require('p-reduce')
55

66
const { setEnvChanges } = require('../env/changes.js')
7-
const { cancelBuild } = require('../error/cancel')
87
const { addErrorInfo, getErrorInfo } = require('../error/info')
98
const { reportBuildError } = require('../error/monitor/report')
9+
const { serializeErrorStatus } = require('../error/parse/serialize_status')
1010
const { logCommand, logBuildCommandStart, logCommandSuccess, logPluginError } = require('../log/main')
1111
const { pipeOutput, unpipeOutput } = require('../log/stream')
1212
const { startTimer, endTimer } = require('../log/timer')
@@ -58,7 +58,7 @@ const isSuccessCommand = function({ event }) {
5858
// list of `failedPlugins` (that ran `utils.build.failPlugin()`).
5959
// If an error arises, runs `onError` events.
6060
// Runs `onEnd` events at the end, whether an error was thrown or not.
61-
const runCommands = async function({ commands, configPath, buildDir, nodePath, childEnv, errorMonitor, api }) {
61+
const runCommands = async function({ commands, configPath, buildDir, nodePath, childEnv, errorMonitor }) {
6262
const { index: commandsCount, error: errorA, statuses: statusesB } = await pReduce(
6363
commands,
6464
async (
@@ -81,7 +81,6 @@ const runCommands = async function({ commands, configPath, buildDir, nodePath, c
8181
envChanges,
8282
statuses,
8383
errorMonitor,
84-
api,
8584
error,
8685
failedPlugins,
8786
},
@@ -116,7 +115,6 @@ const runCommand = async function({
116115
envChanges,
117116
statuses,
118117
errorMonitor,
119-
api,
120118
error,
121119
failedPlugins,
122120
}) {
@@ -147,7 +145,7 @@ const runCommand = async function({
147145
const newValues =
148146
newError === undefined
149147
? handleCommandSuccess({ event, package, newEnvChanges, newStatus, methodTimer })
150-
: await handleCommandError({ newError, errorMonitor, api })
148+
: await handleCommandError({ newError, errorMonitor })
151149
return { ...newValues, newIndex: index + 1 }
152150
}
153151

@@ -257,20 +255,21 @@ const handleCommandSuccess = function({ event, package, newEnvChanges, newStatus
257255
// handlers of the same type have been triggered before propagating
258256
// - if `utils.build.failPlugin()` was used, print an error and skip next event
259257
// handlers of that plugin. But do not stop build.
260-
const handleCommandError = async function({ newError, errorMonitor, api }) {
258+
const handleCommandError = async function({ newError, errorMonitor }) {
261259
const { type, location: { package } = {} } = getErrorInfo(newError)
260+
const newStatus = serializeErrorStatus(newError)
262261

263262
if (type === 'failPlugin') {
264-
logPluginError(newError)
265-
await reportBuildError(newError, errorMonitor)
266-
return { failedPlugin: [package] }
263+
return handleFailPlugin({ newStatus, package, newError, errorMonitor })
267264
}
268265

269-
if (type === 'cancelBuild') {
270-
await cancelBuild(api)
271-
}
266+
return { newError, newStatus }
267+
}
272268

273-
return { newError }
269+
const handleFailPlugin = async function({ newStatus, package, newError, errorMonitor }) {
270+
logPluginError(newError)
271+
await reportBuildError(newError, errorMonitor)
272+
return { failedPlugin: [package], newStatus }
274273
}
275274

276275
module.exports = { getCommands, isSuccessCommand, runCommands }

packages/build/src/core/main.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ setColorLevel()
99
require('../error/process')
1010

1111
const { getChildEnv } = require('../env/main')
12+
const { maybeCancelBuild } = require('../error/cancel')
1213
const { removeErrorColors } = require('../error/colors')
1314
const { reportBuildError } = require('../error/monitor/report')
1415
const { startErrorMonitor } = require('../error/monitor/start')
@@ -79,7 +80,6 @@ const build = async function(flags) {
7980
branch,
8081
mode,
8182
errorMonitor,
82-
api,
8383
})
8484

8585
if (dry) {
@@ -97,6 +97,7 @@ const build = async function(flags) {
9797
await trackBuildComplete({ commandsCount, netlifyConfig, duration, siteInfo, mode })
9898
return true
9999
} catch (error) {
100+
await maybeCancelBuild(error, api)
100101
await logOldCliVersionError(mode)
101102
throw error
102103
}
@@ -122,7 +123,6 @@ const buildRun = async function({
122123
branch,
123124
mode,
124125
errorMonitor,
125-
api,
126126
}) {
127127
const utilsData = await startUtils(buildDir)
128128
const childEnv = await getChildEnv({ netlifyConfig, buildDir, context, branch, siteInfo, mode })
@@ -142,7 +142,6 @@ const buildRun = async function({
142142
dry,
143143
constants,
144144
errorMonitor,
145-
api,
146145
})
147146
} finally {
148147
await stopPlugins(childProcesses)
@@ -162,7 +161,6 @@ const executeCommands = async function({
162161
dry,
163162
constants,
164163
errorMonitor,
165-
api,
166164
}) {
167165
const pluginsCommands = await loadPlugins({
168166
pluginsOptions,
@@ -187,7 +185,6 @@ const executeCommands = async function({
187185
nodePath,
188186
childEnv,
189187
errorMonitor,
190-
api,
191188
})
192189
}
193190

packages/build/src/error/cancel.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ const {
22
env: { DEPLOY_ID },
33
} = require('process')
44

5+
const { getErrorInfo } = require('./info')
6+
57
// Cancel builds, for example when a plugin uses `utils.build.cancelBuild()`
6-
const cancelBuild = async function(api) {
8+
const maybeCancelBuild = async function(error, api) {
79
if (api === undefined || !DEPLOY_ID) {
810
return
911
}
1012

13+
const { type } = getErrorInfo(error)
14+
15+
if (type !== 'cancelBuild') {
16+
return
17+
}
18+
1119
await api.cancelSiteDeploy({ deploy_id: DEPLOY_ID })
1220
}
1321

14-
module.exports = { cancelBuild }
22+
module.exports = { maybeCancelBuild }

packages/build/src/error/parse/parse.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ const { getErrorProps } = require('./properties')
77
const { getStackInfo } = require('./stack')
88

99
// Parse all error information into a normalized sets of properties
10-
const parseError = function(error) {
10+
const parseError = function({ error, colors }) {
1111
const {
1212
message,
1313
stack,
1414
errorProps,
1515
errorInfo,
1616
errorInfo: { location = {}, plugin = {} },
17+
state,
1718
title,
1819
isSuccess,
1920
stackType,
@@ -28,20 +29,21 @@ const parseError = function(error) {
2829

2930
const pluginInfo = getPluginInfo(plugin, location)
3031
const locationInfo = getLocationInfo({ stack: stackA, location, locationType })
31-
const errorPropsA = getErrorProps(errorProps, showErrorProps)
32-
return { title: titleA, message: messageA, pluginInfo, locationInfo, errorProps: errorPropsA, isSuccess }
32+
const errorPropsA = getErrorProps({ errorProps, showErrorProps, colors })
33+
return { state, title: titleA, message: messageA, pluginInfo, locationInfo, errorProps: errorPropsA, isSuccess }
3334
}
3435

3536
// Parse error instance into all the basic properties containing information
3637
const parseErrorInfo = function(error) {
3738
const { message, stack, ...errorProps } = normalizeError(error)
3839
const errorInfo = getErrorInfo(errorProps)
39-
const { title, isSuccess, stackType, locationType, showErrorProps, rawStack } = getTypeInfo(errorInfo)
40+
const { state, title, isSuccess, stackType, locationType, showErrorProps, rawStack } = getTypeInfo(errorInfo)
4041
return {
4142
message,
4243
stack,
4344
errorProps,
4445
errorInfo,
46+
state,
4547
title,
4648
isSuccess,
4749
stackType,

packages/build/src/error/parse/properties.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ const { omit } = require('../../utils/omit')
44
const { INFO_SYM } = require('../info')
55

66
// In uncaught exceptions, print error static properties
7-
const getErrorProps = function(errorProps, showErrorProps) {
7+
const getErrorProps = function({ errorProps, showErrorProps, colors }) {
88
const errorPropsA = omit(errorProps, CLEANED_ERROR_PROPS)
99

1010
if (!showErrorProps || Object.keys(errorPropsA).length === 0) {
1111
return
1212
}
1313

14-
return inspect(errorPropsA)
14+
return inspect(errorPropsA, { colors })
1515
}
1616

1717
// Remove error static properties that should not be logged

packages/build/src/error/parse/serialize_log.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { parseError } = require('./parse')
44

55
// Serialize an error object into a title|body string to print in logs
66
const serializeLogError = function(error) {
7-
const { title, message, pluginInfo, locationInfo, errorProps, isSuccess } = parseError(error)
7+
const { title, message, pluginInfo, locationInfo, errorProps, isSuccess } = parseError({ error, colors: true })
88
const body = getBody({ message, pluginInfo, locationInfo, errorProps, isSuccess })
99
return { title, body, isSuccess }
1010
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const { parseError } = require('./parse')
2+
3+
// Serialize an error object to `statuses` properties
4+
const serializeErrorStatus = function(error) {
5+
const { state, title, message, locationInfo, errorProps } = parseError({ error, colors: false })
6+
const text = getText({ locationInfo, errorProps })
7+
return { state, title, summary: message, text }
8+
}
9+
10+
const getText = function({ locationInfo, errorProps }) {
11+
const parts = [locationInfo, getErrorProps(errorProps)].filter(Boolean)
12+
13+
if (parts.length === 0) {
14+
return
15+
}
16+
17+
return parts.join('\n\n')
18+
}
19+
20+
const getErrorProps = function(errorProps) {
21+
if (errorProps === undefined) {
22+
return
23+
}
24+
25+
return `Error properties:\n${errorProps}`
26+
}
27+
28+
module.exports = { serializeErrorStatus }

packages/build/src/error/type.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Retrieve error-type specific information
22
const getTypeInfo = function({ type }) {
33
const typeA = TYPES[type] === undefined ? DEFAULT_TYPE : type
4-
return { type: typeA, ...TYPES[typeA] }
4+
return { type: typeA, state: DEFAULT_STATE, ...TYPES[typeA] }
55
}
66

77
// List of error types, and their related properties
@@ -20,6 +20,8 @@ const getTypeInfo = function({ type }) {
2020
// - `message`: printed as is, but taken from `error.message`.
2121
// Used when `error.stack` is not being correct due to the error being
2222
// passed between different processes.
23+
// Related to error statuses:
24+
// - `state`: error status state. Defaults to `failed_build`
2325
// Related to Bugsnag:
2426
// - `group`: main title shown in Bugsnag. Also used to group errors together
2527
// in Bugsnag, combined with `error.message`.
@@ -67,6 +69,7 @@ const TYPES = {
6769
stackType: 'stack',
6870
locationType: 'buildFail',
6971
severity: 'info',
72+
state: 'failed_plugin',
7073
},
7174

7275
// Plugin called `utils.build.cancelBuild()`
@@ -76,6 +79,7 @@ const TYPES = {
7679
locationType: 'buildFail',
7780
isSuccess: true,
7881
severity: 'info',
82+
state: 'canceled_plugin',
7983
},
8084

8185
// Plugin has an invalid shape
@@ -129,7 +133,11 @@ const TYPES = {
129133
severity: 'error',
130134
},
131135
}
136+
132137
// When no error type matches, it's an uncaught exception, i.e. a bug
133138
const DEFAULT_TYPE = 'exception'
134139

140+
// When no `state` is provided
141+
const DEFAULT_STATE = 'failed_build'
142+
135143
module.exports = { getTypeInfo }

0 commit comments

Comments
 (0)