Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/build/src/core/flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,12 @@ Default: false`,
},
debug: {
boolean: true,
describe: 'Print debugging information',
describe: 'Print user-facing debugging information',
hidden: true,
},
verbose: {
boolean: true,
describe: 'Print internal debugging information',
hidden: true,
},
Comment on lines 135 to 144
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the need for this ASAP I'm ok with the flag, but what do you think of having debug levels in the future? It would be more suited IMO vs these flags which can be confusing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was thinking this should be the next step as well!

Note than from a user standpoint, they use the NETLIFY_BUILD_DEBUG environment variable for both debug and verbose, so there's currently a single level from their perspective. The buildbot and CLI do not pass the verbose flag, so the only difference is for the tests, which sets verbose as false and debug as true.

sendStatus: {
Expand Down
10 changes: 10 additions & 0 deletions packages/build/src/core/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ const tExecBuild = async function ({
baseRelDir,
env: envOpt,
debug,
verbose,
nodePath,
functionsDistDir,
cacheDir,
Expand Down Expand Up @@ -273,6 +274,7 @@ const tExecBuild = async function ({
errorParams,
logs,
debug,
verbose,
timers: timersA,
sendStatus,
saveConfig,
Expand Down Expand Up @@ -321,6 +323,7 @@ const runAndReportBuild = async function ({
errorParams,
logs,
debug,
verbose,
timers,
sendStatus,
saveConfig,
Expand Down Expand Up @@ -360,6 +363,7 @@ const runAndReportBuild = async function ({
errorParams,
logs,
debug,
verbose,
timers,
sendStatus,
saveConfig,
Expand Down Expand Up @@ -451,6 +455,7 @@ const initAndRunBuild = async function ({
errorParams,
logs,
debug,
verbose,
sendStatus,
saveConfig,
timers,
Expand Down Expand Up @@ -521,6 +526,7 @@ const initAndRunBuild = async function ({
errorParams,
logs,
debug,
verbose,
saveConfig,
timers: timersB,
testOpts,
Expand Down Expand Up @@ -573,6 +579,7 @@ const runBuild = async function ({
errorParams,
logs,
debug,
verbose,
saveConfig,
timers,
testOpts,
Expand All @@ -583,7 +590,9 @@ const runBuild = async function ({
childProcesses,
packageJson,
timers,
logs,
debug,
verbose,
})

const { steps, events } = getSteps(pluginsSteps)
Expand Down Expand Up @@ -623,6 +632,7 @@ const runBuild = async function ({
configOpts,
logs,
debug,
verbose,
saveConfig,
timers: timersA,
testOpts,
Expand Down
1 change: 1 addition & 0 deletions packages/build/src/core/normalize_flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const getDefaultFlags = function ({ env: envOpt = {} }, combinedEnv) {
mode: REQUIRE_MODE,
offline: false,
telemetry: false,
verbose: Boolean(combinedEnv.NETLIFY_BUILD_DEBUG),
functionsDistDir: DEFAULT_FUNCTIONS_DIST,
cacheDir: DEFAULT_CACHE_DIR,
deployId: combinedEnv.DEPLOY_ID,
Expand Down
50 changes: 50 additions & 0 deletions packages/build/src/log/messages/ipc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict'

const { log } = require('../logger')

const logVerbose = function (logs, verbose, message) {
if (!verbose) {
return
}

log(logs, message)
}

const logSendingEventToChild = function (logs, verbose) {
logVerbose(logs, verbose, 'Step starting.')
}

const logSentEventToChild = function (logs, verbose) {
logVerbose(logs, verbose, 'Step started.')
}

const logPluginMethodStart = function (verbose) {
logVerbose(undefined, verbose, 'Plugin logic started.')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logs is undefined when inside the plugin's child process, which is normal.

}

const logPluginMethodEnd = function (verbose) {
logVerbose(undefined, verbose, 'Plugin logic ended.')
}

const logSendingEventToParent = function (verbose, error) {
const message = error instanceof Error ? `Step erroring.\n${error.stack}` : 'Stop closing.'
logVerbose(undefined, verbose, message)
}

const logReceivedEventFromChild = function (logs, verbose) {
logVerbose(logs, verbose, 'Step ended.')
}

const logStepCompleted = function (logs, verbose) {
logVerbose(logs, verbose, 'Step completed.')
}

module.exports = {
logSendingEventToChild,
logSentEventToChild,
logPluginMethodStart,
logPluginMethodEnd,
logSendingEventToParent,
logReceivedEventFromChild,
logStepCompleted,
}
4 changes: 2 additions & 2 deletions packages/build/src/plugins/child/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ const { normalizeError } = require('../../error/parse/normalize')
const { sendEventToParent } = require('../ipc')

// Handle any top-level error and communicate it back to parent
const handleError = async function (error) {
const handleError = async function (error, verbose) {
const errorA = normalizeError(error)
addDefaultErrorInfo(errorA, { type: 'pluginInternal' })
const errorPayload = errorToJson(errorA)
await sendEventToParent('error', errorPayload)
await sendEventToParent('error', errorPayload, verbose, errorA)
}

// On uncaught exceptions and unhandled rejections, print the stack trace.
Expand Down
4 changes: 2 additions & 2 deletions packages/build/src/plugins/child/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const { validatePlugin } = require('./validate')
// This also validates the plugin.
// Do it when parent requests it using the `load` event.
// Also figure out the list of plugin steps. This is also passed to the parent.
const load = async function ({ pluginPath, inputs, packageJson }) {
const load = async function ({ pluginPath, inputs, packageJson, verbose }) {
const tsNodeService = registerTypeScript(pluginPath)
const logic = await getLogic({ pluginPath, inputs, tsNodeService })

Expand All @@ -21,7 +21,7 @@ const load = async function ({ pluginPath, inputs, packageJson }) {
const events = Object.keys(methods)

// Context passed to every event handler
const context = { methods, inputs, packageJson }
const context = { methods, inputs, packageJson, verbose }

return { events, context }
}
Expand Down
23 changes: 16 additions & 7 deletions packages/build/src/plugins/child/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,42 @@ const { run } = require('./run')

// Boot plugin child process.
const bootPlugin = async function () {
const state = { context: { verbose: false } }

try {
handleProcessErrors()
setInspectColors()

const state = {}
// We need to fire them in parallel because `process.send()` can be slow
// to await, i.e. parent might send `load` event before child `ready` event
// returns.
await Promise.all([handleEvents(state), sendEventToParent('ready')])
await Promise.all([handleEvents(state), sendEventToParent('ready', {}, false)])
} catch (error) {
await handleError(error)
await handleError(error, state.context.verbose)
}
}

// Wait for events from parent to perform plugin methods
const handleEvents = async function (state) {
await getEventsFromParent((callId, eventName, payload) => handleEvent(callId, eventName, payload, state))
await getEventsFromParent((callId, eventName, payload) => handleEvent({ callId, eventName, payload, state }))
}

// Each event can pass `context` information to the next event
const handleEvent = async function (callId, eventName, payload, state) {
const handleEvent = async function ({
callId,
eventName,
payload,
state,
state: {
context: { verbose },
},
}) {
try {
const { context, ...response } = await EVENTS[eventName](payload, state.context)
state.context = { ...state.context, ...context }
await sendEventToParent(callId, response)
await sendEventToParent(callId, response, verbose)
} catch (error) {
await handleError(error)
await handleError(error, verbose)
}
}

Expand Down
10 changes: 9 additions & 1 deletion packages/build/src/plugins/child/run.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
'use strict'

const { getNewEnvChanges, setEnvChanges } = require('../../env/changes')
const { logPluginMethodStart, logPluginMethodEnd } = require('../../log/messages/ipc')

const { cloneNetlifyConfig, getConfigMutations } = require('./diff')
const { getUtils } = require('./utils')

// Run a specific plugin event handler
const run = async function ({ event, error, constants, envChanges, netlifyConfig }, { methods, inputs, packageJson }) {
const run = async function (
{ event, error, constants, envChanges, netlifyConfig },
{ methods, inputs, packageJson, verbose },
) {
const method = methods[event]
const runState = {}
const utils = getUtils({ event, constants, runState })
const netlifyConfigCopy = cloneNetlifyConfig(netlifyConfig)
const runOptions = { utils, constants, inputs, netlifyConfig: netlifyConfigCopy, packageJson, error }

const envBefore = setEnvChanges(envChanges)

logPluginMethodStart(verbose)
await method(runOptions)
logPluginMethodEnd(verbose)

const newEnvChanges = getNewEnvChanges(envBefore, netlifyConfig, netlifyConfigCopy)

const configMutations = getConfigMutations(netlifyConfig, netlifyConfigCopy, event)
Expand Down
20 changes: 16 additions & 4 deletions packages/build/src/plugins/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@ const { v4: uuidv4 } = require('uuid')

const { jsonToError, errorToJson } = require('../error/build')
const { addErrorInfo } = require('../error/info')
const {
logSendingEventToChild,
logSentEventToChild,
logReceivedEventFromChild,
logSendingEventToParent,
} = require('../log/messages/ipc')

// Send event from child to parent process then wait for response
// We need to fire them in parallel because `process.send()` can be slow
// to await, i.e. child might send response before parent start listening for it
const callChild = async function (childProcess, eventName, payload) {
const callChild = async function ({ childProcess, eventName, payload, logs, verbose }) {
const callId = uuidv4()
const [response] = await Promise.all([
getEventFromChild(childProcess, callId),
sendEventToChild(childProcess, callId, eventName, payload),
sendEventToChild({ childProcess, callId, eventName, payload, logs, verbose }),
])
logReceivedEventFromChild(logs, verbose)
return response
}

Expand Down Expand Up @@ -84,9 +91,13 @@ Plugin methods should instead:
- on failure: call utils.build.failPlugin() or utils.build.failBuild()`

// Send event from parent to child process
const sendEventToChild = async function (childProcess, callId, eventName, payload) {
const sendEventToChild = async function ({ childProcess, callId, eventName, payload, logs, verbose }) {
logSendingEventToChild(logs, verbose)

const payloadA = serializePayload(payload)
await promisify(childProcess.send.bind(childProcess))([callId, eventName, payloadA])

logSentEventToChild(logs, verbose)
}

// Respond to events from parent to child process.
Expand All @@ -109,7 +120,8 @@ const getEventsFromParent = function (callback) {
}

// Send event from child to parent process
const sendEventToParent = async function (callId, payload) {
const sendEventToParent = async function (callId, payload, verbose, error) {
logSendingEventToParent(verbose, error)
await promisify(process.send.bind(process))([callId, payload])
}

Expand Down
18 changes: 12 additions & 6 deletions packages/build/src/plugins/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ const { callChild } = require('./ipc')

// Retrieve all plugins steps
// Can use either a module name or a file path to the plugin.
const loadPlugins = async function ({ pluginsOptions, childProcesses, packageJson, timers, debug }) {
const loadPlugins = async function ({ pluginsOptions, childProcesses, packageJson, timers, logs, debug, verbose }) {
return pluginsOptions.length === 0
? { pluginsSteps: [], timers }
: await loadAllPlugins({ pluginsOptions, childProcesses, packageJson, timers, debug })
: await loadAllPlugins({ pluginsOptions, childProcesses, packageJson, timers, logs, debug, verbose })
}

const tLoadAllPlugins = async function ({ pluginsOptions, childProcesses, packageJson, debug }) {
const tLoadAllPlugins = async function ({ pluginsOptions, childProcesses, packageJson, logs, debug, verbose }) {
const pluginsSteps = await Promise.all(
pluginsOptions.map((pluginOptions, index) =>
loadPlugin(pluginOptions, { childProcesses, index, packageJson, debug }),
loadPlugin(pluginOptions, { childProcesses, index, packageJson, logs, debug, verbose }),
),
)
const pluginsStepsA = pluginsSteps.flat()
Expand All @@ -31,13 +31,19 @@ const loadAllPlugins = measureDuration(tLoadAllPlugins, 'load_plugins')
// Do it by executing the plugin `load` event handler.
const loadPlugin = async function (
{ packageName, pluginPackageJson, pluginPackageJson: { version } = {}, pluginPath, inputs, loadedFrom, origin },
{ childProcesses, index, packageJson, debug },
{ childProcesses, index, packageJson, logs, debug, verbose },
) {
const { childProcess } = childProcesses[index]
const loadEvent = 'load'

try {
const { events } = await callChild(childProcess, 'load', { pluginPath, inputs, packageJson })
const { events } = await callChild({
childProcess,
eventName: 'load',
payload: { pluginPath, inputs, packageJson, verbose },
logs,
verbose: false,
})
const pluginSteps = events.map((event) => ({
event,
packageName,
Expand Down
15 changes: 9 additions & 6 deletions packages/build/src/steps/plugin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const { addErrorInfo } = require('../error/info')
const { logStepCompleted } = require('../log/messages/ipc')
const { pipePluginOutput, unpipePluginOutput } = require('../log/stream')
const { callChild } = require('../plugins/ipc')
const { getSuccessStatus } = require('../status/success')
Expand Down Expand Up @@ -28,6 +29,7 @@ const firePluginStep = async function ({
error,
logs,
debug,
verbose,
}) {
const listeners = pipePluginOutput(childProcess, logs)

Expand All @@ -37,12 +39,12 @@ const firePluginStep = async function ({
newEnvChanges,
configMutations: newConfigMutations,
status,
} = await callChild(childProcess, 'run', {
event,
error,
envChanges,
netlifyConfig,
constants,
} = await callChild({
childProcess,
eventName: 'run',
payload: { event, error, envChanges, netlifyConfig, constants },
logs,
verbose,
})
const {
netlifyConfig: netlifyConfigA,
Expand Down Expand Up @@ -80,6 +82,7 @@ const firePluginStep = async function ({
return { newError }
} finally {
await unpipePluginOutput(childProcess, logs, listeners)
logStepCompleted(logs, verbose)
}
}

Expand Down
Loading