@netlify/build 29.12.8 → 29.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/core/build.js +12 -6
- package/lib/core/flags.js +39 -0
- package/lib/core/main.js +88 -61
- package/lib/core/normalize_flags.js +3 -0
- package/lib/core/types.js +1 -1
- package/lib/error/report.js +2 -0
- package/lib/log/messages/config.js +1 -0
- package/lib/steps/core_step.js +2 -1
- package/lib/steps/error.js +2 -0
- package/lib/steps/run_step.js +111 -88
- package/lib/steps/run_steps.js +2 -1
- package/lib/tracing/main.js +52 -0
- package/package.json +5 -2
package/lib/core/build.js
CHANGED
|
@@ -12,6 +12,7 @@ import { reportStatuses } from '../status/report.js';
|
|
|
12
12
|
import { getDevSteps, getSteps } from '../steps/get.js';
|
|
13
13
|
import { runSteps } from '../steps/run_steps.js';
|
|
14
14
|
import { initTimers, measureDuration } from '../time/main.js';
|
|
15
|
+
import { startTracing } from '../tracing/main.js';
|
|
15
16
|
import { getConfigOpts, loadConfig } from './config.js';
|
|
16
17
|
import { getConstants } from './constants.js';
|
|
17
18
|
import { doDryRun } from './dry.js';
|
|
@@ -27,11 +28,12 @@ export const startBuild = function (flags) {
|
|
|
27
28
|
if (!flags.quiet) {
|
|
28
29
|
logBuildStart(logs);
|
|
29
30
|
}
|
|
30
|
-
const { bugsnagKey, ...flagsA } = normalizeFlags(flags, logs);
|
|
31
|
-
const errorMonitor = startErrorMonitor({ flags: flagsA, logs, bugsnagKey });
|
|
31
|
+
const { bugsnagKey, tracing, ...flagsA } = normalizeFlags(flags, logs);
|
|
32
|
+
const errorMonitor = startErrorMonitor({ flags: { tracing, ...flagsA }, logs, bugsnagKey });
|
|
33
|
+
startTracing(tracing);
|
|
32
34
|
return { ...flagsA, errorMonitor, logs, timers };
|
|
33
35
|
};
|
|
34
|
-
const tExecBuild = async function ({ config, defaultConfig, cachedConfig, cachedConfigPath, outputConfigPath, cwd, repositoryRoot, apiHost, token, siteId, context, branch, baseRelDir, env: envOpt, debug, systemLogFile, verbose, nodePath, functionsDistDir, edgeFunctionsDistDir, cacheDir, dry, mode, offline, deployId, buildId, testOpts, errorMonitor, errorParams, logs, timers, buildbotServerSocket, sendStatus, saveConfig, featureFlags, timeline, devCommand, quiet, framework, }) {
|
|
36
|
+
const tExecBuild = async function ({ config, defaultConfig, cachedConfig, cachedConfigPath, outputConfigPath, cwd, repositoryRoot, apiHost, token, siteId, context, branch, baseRelDir, env: envOpt, debug, systemLogFile, verbose, nodePath, functionsDistDir, edgeFunctionsDistDir, cacheDir, dry, mode, offline, deployId, buildId, testOpts, errorMonitor, errorParams, logs, timers, buildbotServerSocket, sendStatus, saveConfig, featureFlags, timeline, devCommand, quiet, framework, explicitSecretKeys, }) {
|
|
35
37
|
const configOpts = getConfigOpts({
|
|
36
38
|
config,
|
|
37
39
|
defaultConfig,
|
|
@@ -126,6 +128,7 @@ const tExecBuild = async function ({ config, defaultConfig, cachedConfig, cached
|
|
|
126
128
|
timeline,
|
|
127
129
|
devCommand,
|
|
128
130
|
quiet,
|
|
131
|
+
explicitSecretKeys,
|
|
129
132
|
});
|
|
130
133
|
return {
|
|
131
134
|
pluginsOptions: pluginsOptionsA,
|
|
@@ -140,7 +143,7 @@ const tExecBuild = async function ({ config, defaultConfig, cachedConfig, cached
|
|
|
140
143
|
};
|
|
141
144
|
export const execBuild = measureDuration(tExecBuild, 'total', { parentTag: 'build_site' });
|
|
142
145
|
// Runs a build then report any plugin statuses
|
|
143
|
-
export const runAndReportBuild = async function ({ pluginsOptions, netlifyConfig, configOpts, siteInfo, configPath, outputConfigPath, headersPath, redirectsPath, buildDir, repositoryRoot, nodePath, packageJson, userNodeVersion, childEnv, context, branch, buildbotServerSocket, constants, dry, mode, api, errorMonitor, deployId, errorParams, logs, debug, systemLog, verbose, timers, sendStatus, saveConfig, testOpts, featureFlags, timeline, devCommand, quiet, }) {
|
|
146
|
+
export const runAndReportBuild = async function ({ pluginsOptions, netlifyConfig, configOpts, siteInfo, configPath, outputConfigPath, headersPath, redirectsPath, buildDir, repositoryRoot, nodePath, packageJson, userNodeVersion, childEnv, context, branch, buildbotServerSocket, constants, dry, mode, api, errorMonitor, deployId, errorParams, logs, debug, systemLog, verbose, timers, sendStatus, saveConfig, testOpts, featureFlags, timeline, devCommand, quiet, explicitSecretKeys, }) {
|
|
144
147
|
try {
|
|
145
148
|
const { stepsCount, netlifyConfig: netlifyConfigA, statuses, pluginsOptions: pluginsOptionsA, failedPlugins, timers: timersA, configMutations, metrics, } = await initAndRunBuild({
|
|
146
149
|
pluginsOptions,
|
|
@@ -179,6 +182,7 @@ export const runAndReportBuild = async function ({ pluginsOptions, netlifyConfig
|
|
|
179
182
|
timeline,
|
|
180
183
|
devCommand,
|
|
181
184
|
quiet,
|
|
185
|
+
explicitSecretKeys,
|
|
182
186
|
});
|
|
183
187
|
await Promise.all([
|
|
184
188
|
reportStatuses({
|
|
@@ -239,7 +243,7 @@ export const runAndReportBuild = async function ({ pluginsOptions, netlifyConfig
|
|
|
239
243
|
}
|
|
240
244
|
};
|
|
241
245
|
// Initialize plugin processes then runs a build
|
|
242
|
-
const initAndRunBuild = async function ({ pluginsOptions, netlifyConfig, configOpts, siteInfo, configPath, outputConfigPath, headersPath, redirectsPath, buildDir, repositoryRoot, nodePath, packageJson, userNodeVersion, childEnv, context, branch, dry, mode, api, errorMonitor, deployId, errorParams, logs, debug, systemLog, verbose, sendStatus, saveConfig, timers, testOpts, buildbotServerSocket, constants, featureFlags, timeline, devCommand, quiet, }) {
|
|
246
|
+
const initAndRunBuild = async function ({ pluginsOptions, netlifyConfig, configOpts, siteInfo, configPath, outputConfigPath, headersPath, redirectsPath, buildDir, repositoryRoot, nodePath, packageJson, userNodeVersion, childEnv, context, branch, dry, mode, api, errorMonitor, deployId, errorParams, logs, debug, systemLog, verbose, sendStatus, saveConfig, timers, testOpts, buildbotServerSocket, constants, featureFlags, timeline, devCommand, quiet, explicitSecretKeys, }) {
|
|
243
247
|
const { pluginsOptions: pluginsOptionsA, timers: timersA } = await getPluginsOptions({
|
|
244
248
|
pluginsOptions,
|
|
245
249
|
netlifyConfig,
|
|
@@ -305,6 +309,7 @@ const initAndRunBuild = async function ({ pluginsOptions, netlifyConfig, configO
|
|
|
305
309
|
timeline,
|
|
306
310
|
devCommand,
|
|
307
311
|
quiet,
|
|
312
|
+
explicitSecretKeys,
|
|
308
313
|
});
|
|
309
314
|
await Promise.all([
|
|
310
315
|
warnOnMissingSideFiles({ buildDir, netlifyConfig: netlifyConfigA, logs }),
|
|
@@ -332,7 +337,7 @@ const initAndRunBuild = async function ({ pluginsOptions, netlifyConfig, configO
|
|
|
332
337
|
};
|
|
333
338
|
// Load plugin main files, retrieve their event handlers then runs them,
|
|
334
339
|
// together with the build command
|
|
335
|
-
const runBuild = async function ({ childProcesses, pluginsOptions, netlifyConfig, configOpts, packageJson, configPath, outputConfigPath, userNodeVersion, headersPath, redirectsPath, buildDir, repositoryRoot, nodePath, childEnv, context, branch, dry, buildbotServerSocket, constants, mode, api, errorMonitor, deployId, errorParams, logs, debug, systemLog, verbose, saveConfig, timers, testOpts, featureFlags, timeline, devCommand, quiet, }) {
|
|
340
|
+
const runBuild = async function ({ childProcesses, pluginsOptions, netlifyConfig, configOpts, packageJson, configPath, outputConfigPath, userNodeVersion, headersPath, redirectsPath, buildDir, repositoryRoot, nodePath, childEnv, context, branch, dry, buildbotServerSocket, constants, mode, api, errorMonitor, deployId, errorParams, logs, debug, systemLog, verbose, saveConfig, timers, testOpts, featureFlags, timeline, devCommand, quiet, explicitSecretKeys, }) {
|
|
336
341
|
const { pluginsSteps, timers: timersA } = await loadPlugins({
|
|
337
342
|
pluginsOptions,
|
|
338
343
|
childProcesses,
|
|
@@ -379,6 +384,7 @@ const runBuild = async function ({ childProcesses, pluginsOptions, netlifyConfig
|
|
|
379
384
|
featureFlags,
|
|
380
385
|
quiet,
|
|
381
386
|
userNodeVersion,
|
|
387
|
+
explicitSecretKeys,
|
|
382
388
|
});
|
|
383
389
|
return {
|
|
384
390
|
stepsCount,
|
package/lib/core/flags.js
CHANGED
|
@@ -189,6 +189,40 @@ Default: false`,
|
|
|
189
189
|
describe: 'Statsd port',
|
|
190
190
|
hidden: true,
|
|
191
191
|
},
|
|
192
|
+
tracing: {
|
|
193
|
+
describe: 'Tracing related options',
|
|
194
|
+
hidden: true,
|
|
195
|
+
},
|
|
196
|
+
'tracing.enabled': {
|
|
197
|
+
boolean: true,
|
|
198
|
+
describe: 'Enable distributed tracing for build',
|
|
199
|
+
hidden: true,
|
|
200
|
+
},
|
|
201
|
+
'tracing.host': {
|
|
202
|
+
string: true,
|
|
203
|
+
describe: 'Traces backend host',
|
|
204
|
+
hidden: true,
|
|
205
|
+
},
|
|
206
|
+
'tracing.port': {
|
|
207
|
+
number: true,
|
|
208
|
+
describe: 'Traces backend port',
|
|
209
|
+
hidden: true,
|
|
210
|
+
},
|
|
211
|
+
'tracing.traceId': {
|
|
212
|
+
string: true,
|
|
213
|
+
describe: 'Trace ID used to stitch the emited traces to',
|
|
214
|
+
hidden: true,
|
|
215
|
+
},
|
|
216
|
+
'tracing.parentSpanId': {
|
|
217
|
+
string: true,
|
|
218
|
+
describe: 'Parent Span ID used to stitch the root parent span to',
|
|
219
|
+
hidden: true,
|
|
220
|
+
},
|
|
221
|
+
'tracing.traceFlags': {
|
|
222
|
+
number: true,
|
|
223
|
+
describe: 'Trace flags containing the trace settings for the given trace ID',
|
|
224
|
+
hidden: true,
|
|
225
|
+
},
|
|
192
226
|
offline: {
|
|
193
227
|
boolean: true,
|
|
194
228
|
describe: `Do not send requests to the Netlify API to retrieve site settings.
|
|
@@ -203,4 +237,9 @@ Default: false`,
|
|
|
203
237
|
describe: 'The sequence of lifecycle events to run',
|
|
204
238
|
hidden: true,
|
|
205
239
|
},
|
|
240
|
+
explicitSecretKeys: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
describe: 'Env var keys that are marked as secret explicitly.',
|
|
243
|
+
hidden: true,
|
|
244
|
+
},
|
|
206
245
|
};
|
package/lib/core/main.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import { trace } from '@opentelemetry/api';
|
|
1
2
|
import { handleBuildError } from '../error/handle.js';
|
|
2
3
|
import { reportError } from '../error/report.js';
|
|
3
4
|
import { getSystemLogger } from '../log/logger.js';
|
|
4
5
|
import { logTimer, logBuildSuccess } from '../log/messages/core.js';
|
|
5
6
|
import { trackBuildComplete } from '../telemetry/main.js';
|
|
6
7
|
import { reportTimers } from '../time/report.js';
|
|
8
|
+
import { stopTracing, setMultiSpanAttributes } from '../tracing/main.js';
|
|
7
9
|
import { execBuild, startBuild } from './build.js';
|
|
8
10
|
import { reportMetrics } from './report_metrics.js';
|
|
9
11
|
import { getSeverity } from './severity.js';
|
|
10
12
|
export { startDev } from './dev.js';
|
|
11
13
|
export { runCoreSteps } from '../steps/run_core_steps.js';
|
|
14
|
+
const tracer = trace.getTracer('core');
|
|
12
15
|
/**
|
|
13
16
|
* Main entry point of Netlify Build.
|
|
14
17
|
* Runs a builds and returns whether it succeeded or not.
|
|
@@ -19,67 +22,91 @@ export default async function buildSite(flags = {}) {
|
|
|
19
22
|
const { errorMonitor, framework, mode, logs, debug, systemLogFile, testOpts, statsdOpts, dry, telemetry, buildId, deployId, ...flagsA } = startBuild(flags);
|
|
20
23
|
const errorParams = { errorMonitor, mode, logs, debug, testOpts };
|
|
21
24
|
const systemLog = getSystemLogger(logs, debug, systemLogFile);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
errorParams
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
25
|
+
const attributes = {
|
|
26
|
+
'deploy.id': deployId,
|
|
27
|
+
'build.id': buildId,
|
|
28
|
+
'deploy.context': flagsA.context,
|
|
29
|
+
'site.id': flagsA.siteId,
|
|
30
|
+
};
|
|
31
|
+
const rootCtx = setMultiSpanAttributes(attributes);
|
|
32
|
+
return await tracer.startActiveSpan('exec-build', {}, rootCtx, async (span) => {
|
|
33
|
+
try {
|
|
34
|
+
const { pluginsOptions, netlifyConfig: netlifyConfigA, siteInfo, userNodeVersion, stepsCount, timers, durationNs, configMutations, metrics, } = await execBuild({
|
|
35
|
+
...flagsA,
|
|
36
|
+
buildId,
|
|
37
|
+
systemLogFile,
|
|
38
|
+
deployId,
|
|
39
|
+
dry,
|
|
40
|
+
errorMonitor,
|
|
41
|
+
mode,
|
|
42
|
+
logs,
|
|
43
|
+
debug,
|
|
44
|
+
testOpts,
|
|
45
|
+
errorParams,
|
|
46
|
+
framework,
|
|
47
|
+
});
|
|
48
|
+
await handleBuildSuccess({
|
|
49
|
+
framework,
|
|
50
|
+
dry,
|
|
51
|
+
logs,
|
|
52
|
+
timers,
|
|
53
|
+
durationNs,
|
|
54
|
+
statsdOpts,
|
|
55
|
+
systemLog,
|
|
56
|
+
metrics,
|
|
57
|
+
});
|
|
58
|
+
const { success, severityCode, status } = getSeverity('success');
|
|
59
|
+
span.setAttributes({
|
|
60
|
+
'build.execution.success': success,
|
|
61
|
+
'build.execution.code': severityCode,
|
|
62
|
+
'build.execution.status': status,
|
|
63
|
+
});
|
|
64
|
+
await telemetryReport({
|
|
65
|
+
buildId,
|
|
66
|
+
deployId,
|
|
67
|
+
status,
|
|
68
|
+
stepsCount,
|
|
69
|
+
pluginsOptions,
|
|
70
|
+
durationNs,
|
|
71
|
+
siteInfo,
|
|
72
|
+
telemetry,
|
|
73
|
+
userNodeVersion,
|
|
74
|
+
framework,
|
|
75
|
+
testOpts,
|
|
76
|
+
errorParams,
|
|
77
|
+
});
|
|
78
|
+
return { success, severityCode, netlifyConfig: netlifyConfigA, logs, configMutations };
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const { severity } = await handleBuildError(error, errorParams);
|
|
82
|
+
const { pluginsOptions, siteInfo, userNodeVersion } = errorParams;
|
|
83
|
+
const { success, severityCode, status } = getSeverity(severity);
|
|
84
|
+
span.setAttributes({
|
|
85
|
+
'build.execution.success': success,
|
|
86
|
+
'build.execution.code': severityCode,
|
|
87
|
+
'build.execution.status': status,
|
|
88
|
+
});
|
|
89
|
+
await telemetryReport({
|
|
90
|
+
buildId,
|
|
91
|
+
deployId,
|
|
92
|
+
status,
|
|
93
|
+
pluginsOptions,
|
|
94
|
+
siteInfo,
|
|
95
|
+
telemetry,
|
|
96
|
+
userNodeVersion,
|
|
97
|
+
framework,
|
|
98
|
+
testOpts,
|
|
99
|
+
errorParams,
|
|
100
|
+
});
|
|
101
|
+
await reportError(error, statsdOpts, framework);
|
|
102
|
+
return { success, severityCode, logs };
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
span.end();
|
|
106
|
+
// Ensure we flush the resulting spans
|
|
107
|
+
await stopTracing();
|
|
108
|
+
}
|
|
109
|
+
});
|
|
83
110
|
}
|
|
84
111
|
// Logs and reports that a build successfully ended
|
|
85
112
|
const handleBuildSuccess = async function ({ framework, dry, logs, timers, durationNs, statsdOpts, systemLog, metrics, }) {
|
|
@@ -7,6 +7,7 @@ const DEFAULT_EDGE_FUNCTIONS_DIST = '.netlify/edge-functions-dist/';
|
|
|
7
7
|
const DEFAULT_FUNCTIONS_DIST = '.netlify/functions/';
|
|
8
8
|
const DEFAULT_CACHE_DIR = '.netlify/cache/';
|
|
9
9
|
const DEFAULT_STATSD_PORT = 8125;
|
|
10
|
+
const DEFAULT_OTEL_TRACING_PORT = 4317;
|
|
10
11
|
/** Normalize CLI flags */
|
|
11
12
|
export const normalizeFlags = function (flags, logs) {
|
|
12
13
|
const rawFlags = removeFalsy(flags);
|
|
@@ -20,6 +21,7 @@ export const normalizeFlags = function (flags, logs) {
|
|
|
20
21
|
...rawFlags,
|
|
21
22
|
...telemetryFlag,
|
|
22
23
|
statsdOpts: { ...defaultFlags.statsd, ...rawFlags.statsd },
|
|
24
|
+
tracingOpts: { ...defaultFlags.tracing, ...rawFlags.tracing },
|
|
23
25
|
featureFlags: { ...defaultFlags.featureFlags, ...rawFlags.featureFlags },
|
|
24
26
|
};
|
|
25
27
|
const normalizedFlags = removeFalsy(mergedFlags);
|
|
@@ -51,6 +53,7 @@ const getDefaultFlags = function ({ env: envOpt = {} }, combinedEnv) {
|
|
|
51
53
|
testOpts: {},
|
|
52
54
|
featureFlags: DEFAULT_FEATURE_FLAGS,
|
|
53
55
|
statsd: { port: DEFAULT_STATSD_PORT },
|
|
56
|
+
tracing: { enabled: false, port: DEFAULT_OTEL_TRACING_PORT },
|
|
54
57
|
timeline: 'build',
|
|
55
58
|
quiet: false,
|
|
56
59
|
};
|
package/lib/core/types.js
CHANGED
|
@@ -5,4 +5,4 @@ export var SeverityCode;
|
|
|
5
5
|
SeverityCode[SeverityCode["userError"] = 3] = "userError";
|
|
6
6
|
SeverityCode[SeverityCode["pluginError"] = 4] = "pluginError";
|
|
7
7
|
SeverityCode[SeverityCode["systemError"] = 5] = "systemError";
|
|
8
|
-
})(SeverityCode
|
|
8
|
+
})(SeverityCode || (SeverityCode = {}));
|
package/lib/error/report.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isNetlifyMaintainedPlugin } from '../plugins/internal.js';
|
|
2
2
|
import { closeClient, formatTags, normalizeTagName, startClient, validateStatsDOptions, } from '../report/statsd.js';
|
|
3
|
+
import { addErrorToActiveSpan } from '../tracing/main.js';
|
|
3
4
|
import { getErrorInfo } from './info.js';
|
|
4
5
|
const TOP_PARENT_TAG = 'run_netlify_build';
|
|
5
6
|
/**
|
|
@@ -7,6 +8,7 @@ const TOP_PARENT_TAG = 'run_netlify_build';
|
|
|
7
8
|
* Sends to statsd daemon.
|
|
8
9
|
*/
|
|
9
10
|
export const reportError = async function (error, statsdOpts, framework) {
|
|
11
|
+
addErrorToActiveSpan(error);
|
|
10
12
|
if (!validateStatsDOptions(statsdOpts)) {
|
|
11
13
|
return;
|
|
12
14
|
}
|
package/lib/steps/core_step.js
CHANGED
|
@@ -2,7 +2,7 @@ import { setEnvChanges } from '../env/changes.js';
|
|
|
2
2
|
import { addErrorInfo, isBuildError } from '../error/info.js';
|
|
3
3
|
import { updateNetlifyConfig, listConfigSideFiles } from './update_config.js';
|
|
4
4
|
// Fire a core step
|
|
5
|
-
export const fireCoreStep = async function ({ coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, constants, buildbotServerSocket, events, logs, nodePath, childEnv, context, branch, envChanges, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, debug, systemLog, saveConfig, userNodeVersion, }) {
|
|
5
|
+
export const fireCoreStep = async function ({ coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, constants, buildbotServerSocket, events, logs, nodePath, childEnv, context, branch, envChanges, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, debug, systemLog, saveConfig, userNodeVersion, explicitSecretKeys, }) {
|
|
6
6
|
try {
|
|
7
7
|
const configSideFiles = await listConfigSideFiles([headersPath, redirectsPath]);
|
|
8
8
|
const childEnvA = setEnvChanges(envChanges, { ...childEnv });
|
|
@@ -28,6 +28,7 @@ export const fireCoreStep = async function ({ coreStep, coreStepId, coreStepName
|
|
|
28
28
|
systemLog,
|
|
29
29
|
saveConfig,
|
|
30
30
|
userNodeVersion,
|
|
31
|
+
explicitSecretKeys,
|
|
31
32
|
});
|
|
32
33
|
const { netlifyConfig: netlifyConfigA, configMutations: configMutationsA, headersPath: headersPathA, redirectsPath: redirectsPathA, } = await updateNetlifyConfig({
|
|
33
34
|
configOpts,
|
package/lib/steps/error.js
CHANGED
|
@@ -3,6 +3,7 @@ import { handleBuildError } from '../error/handle.js';
|
|
|
3
3
|
import { getFullErrorInfo, parseErrorInfo } from '../error/parse/parse.js';
|
|
4
4
|
import { serializeErrorStatus } from '../error/parse/serialize_status.js';
|
|
5
5
|
import { isSoftFailEvent } from '../plugins/events.js';
|
|
6
|
+
import { addErrorToActiveSpan } from '../tracing/main.js';
|
|
6
7
|
// Handle build command errors and plugin errors:
|
|
7
8
|
// - usually, propagate the error to make the build stop.
|
|
8
9
|
// - `utils.build.cancelBuild()` also cancels the build by calling the API
|
|
@@ -11,6 +12,7 @@ import { isSoftFailEvent } from '../plugins/events.js';
|
|
|
11
12
|
// plugin.
|
|
12
13
|
// This also computes error statuses that are sent to the API.
|
|
13
14
|
export const handleStepError = function ({ event, newError, childEnv, mode, api, errorMonitor, deployId, coreStep, netlifyConfig, logs, debug, testOpts, }) {
|
|
15
|
+
addErrorToActiveSpan(newError);
|
|
14
16
|
// Core steps do not report error statuses
|
|
15
17
|
if (coreStep !== undefined) {
|
|
16
18
|
return { newError };
|
package/lib/steps/run_step.js
CHANGED
|
@@ -1,98 +1,120 @@
|
|
|
1
|
+
import { trace } from '@opentelemetry/api';
|
|
1
2
|
import { addMutableConstants } from '../core/constants.js';
|
|
2
3
|
import { logStepStart } from '../log/messages/steps.js';
|
|
3
4
|
import { runsAlsoOnBuildFailure, runsOnlyOnBuildFailure } from '../plugins/events.js';
|
|
4
5
|
import { normalizeTagName } from '../report/statsd.js';
|
|
5
6
|
import { measureDuration } from '../time/main.js';
|
|
7
|
+
import { setMultiSpanAttributes } from '../tracing/main.js';
|
|
6
8
|
import { fireCoreStep } from './core_step.js';
|
|
7
9
|
import { firePluginStep } from './plugin.js';
|
|
8
10
|
import { getStepReturn } from './return.js';
|
|
11
|
+
const tracer = trace.getTracer('steps');
|
|
9
12
|
// Run a step (core, build command or plugin)
|
|
10
|
-
export const runStep = async function ({ event, childProcess, packageName, coreStep, coreStepId, coreStepName, coreStepDescription, pluginPackageJson, loadedFrom, origin, condition, configPath, outputConfigPath, buildDir, repositoryRoot, nodePath, index, childEnv, context, branch, envChanges, constants, steps, buildbotServerSocket, events, mode, api, errorMonitor, deployId, errorParams, error, failedPlugins, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, logs, debug, systemLog, verbose, saveConfig, timers, testOpts, featureFlags, quiet, userNodeVersion, }) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
13
|
+
export const runStep = async function ({ event, childProcess, packageName, coreStep, coreStepId, coreStepName, coreStepDescription, pluginPackageJson, loadedFrom, origin, condition, configPath, outputConfigPath, buildDir, repositoryRoot, nodePath, index, childEnv, context, branch, envChanges, constants, steps, buildbotServerSocket, events, mode, api, errorMonitor, deployId, errorParams, error, failedPlugins, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, logs, debug, systemLog, verbose, saveConfig, timers, testOpts, featureFlags, quiet, userNodeVersion, explicitSecretKeys, }) {
|
|
14
|
+
// Add relevant attributes to the upcoming span context
|
|
15
|
+
const attributes = {
|
|
16
|
+
'build.execution.step.name': coreStepName,
|
|
17
|
+
'build.execution.step.description': coreStepDescription,
|
|
18
|
+
'build.execution.step.package_name': packageName,
|
|
19
|
+
'build.execution.step.id': coreStepId,
|
|
20
|
+
'build.execution.step.loaded_from': loadedFrom,
|
|
21
|
+
'build.execution.step.origin': origin,
|
|
22
|
+
'build.execution.step.event': event,
|
|
23
|
+
};
|
|
24
|
+
const spanCtx = setMultiSpanAttributes(attributes);
|
|
25
|
+
return tracer.startActiveSpan(`run-step-${coreStepId}`, {}, spanCtx, async (span) => {
|
|
26
|
+
const constantsA = await addMutableConstants({ constants, buildDir, netlifyConfig });
|
|
27
|
+
const shouldRun = await shouldRunStep({
|
|
28
|
+
event,
|
|
29
|
+
packageName,
|
|
30
|
+
error,
|
|
31
|
+
failedPlugins,
|
|
32
|
+
netlifyConfig,
|
|
33
|
+
condition,
|
|
34
|
+
constants: constantsA,
|
|
35
|
+
buildbotServerSocket,
|
|
36
|
+
buildDir,
|
|
37
|
+
saveConfig,
|
|
38
|
+
explicitSecretKeys,
|
|
39
|
+
});
|
|
40
|
+
span.setAttribute('build.execution.step.should_run', shouldRun);
|
|
41
|
+
if (!shouldRun) {
|
|
42
|
+
span.end();
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
if (!quiet) {
|
|
46
|
+
logStepStart({ logs, event, packageName, coreStepDescription, error, netlifyConfig });
|
|
47
|
+
}
|
|
48
|
+
const fireStep = getFireStep(packageName, coreStepId, event);
|
|
49
|
+
const { newEnvChanges, netlifyConfig: netlifyConfigA = netlifyConfig, configMutations: configMutationsA = configMutations, headersPath: headersPathA = headersPath, redirectsPath: redirectsPathA = redirectsPath, newError, newStatus, timers: timersA, durationNs, metrics, } = await fireStep({
|
|
50
|
+
event,
|
|
51
|
+
childProcess,
|
|
52
|
+
packageName,
|
|
53
|
+
pluginPackageJson,
|
|
54
|
+
loadedFrom,
|
|
55
|
+
origin,
|
|
56
|
+
coreStep,
|
|
57
|
+
coreStepId,
|
|
58
|
+
coreStepName,
|
|
59
|
+
configPath,
|
|
60
|
+
outputConfigPath,
|
|
61
|
+
buildDir,
|
|
62
|
+
repositoryRoot,
|
|
63
|
+
nodePath,
|
|
64
|
+
childEnv,
|
|
65
|
+
context,
|
|
66
|
+
branch,
|
|
67
|
+
envChanges,
|
|
68
|
+
constants: constantsA,
|
|
69
|
+
steps,
|
|
70
|
+
buildbotServerSocket,
|
|
71
|
+
events,
|
|
72
|
+
error,
|
|
73
|
+
logs,
|
|
74
|
+
debug,
|
|
75
|
+
systemLog,
|
|
76
|
+
verbose,
|
|
77
|
+
saveConfig,
|
|
78
|
+
timers,
|
|
79
|
+
errorParams,
|
|
80
|
+
configOpts,
|
|
81
|
+
netlifyConfig,
|
|
82
|
+
configMutations,
|
|
83
|
+
headersPath,
|
|
84
|
+
redirectsPath,
|
|
85
|
+
featureFlags,
|
|
86
|
+
userNodeVersion,
|
|
87
|
+
explicitSecretKeys,
|
|
88
|
+
});
|
|
89
|
+
const newValues = await getStepReturn({
|
|
90
|
+
event,
|
|
91
|
+
packageName,
|
|
92
|
+
newError,
|
|
93
|
+
newEnvChanges,
|
|
94
|
+
newStatus,
|
|
95
|
+
coreStep,
|
|
96
|
+
coreStepName,
|
|
97
|
+
childEnv,
|
|
98
|
+
mode,
|
|
99
|
+
api,
|
|
100
|
+
errorMonitor,
|
|
101
|
+
deployId,
|
|
102
|
+
netlifyConfig: netlifyConfigA,
|
|
103
|
+
configMutations: configMutationsA,
|
|
104
|
+
headersPath: headersPathA,
|
|
105
|
+
redirectsPath: redirectsPathA,
|
|
106
|
+
logs,
|
|
107
|
+
debug,
|
|
108
|
+
timers: timersA,
|
|
109
|
+
durationNs,
|
|
110
|
+
testOpts,
|
|
111
|
+
systemLog,
|
|
112
|
+
quiet,
|
|
113
|
+
metrics,
|
|
114
|
+
});
|
|
115
|
+
span.end();
|
|
116
|
+
return { ...newValues, newIndex: index + 1 };
|
|
94
117
|
});
|
|
95
|
-
return { ...newValues, newIndex: index + 1 };
|
|
96
118
|
};
|
|
97
119
|
// A plugin fails _without making the build fail_ when either:
|
|
98
120
|
// - using `utils.build.failPlugin()`
|
|
@@ -125,10 +147,10 @@ export const runStep = async function ({ event, childProcess, packageName, coreS
|
|
|
125
147
|
// or available. However, one might be created by a build plugin, in which case,
|
|
126
148
|
// those core plugins should be triggered. We use a dynamic `condition()` to
|
|
127
149
|
// model this behavior.
|
|
128
|
-
const shouldRunStep = async function ({ event, packageName, error, failedPlugins, netlifyConfig, condition, constants, buildbotServerSocket, buildDir, saveConfig, }) {
|
|
150
|
+
const shouldRunStep = async function ({ event, packageName, error, failedPlugins, netlifyConfig, condition, constants, buildbotServerSocket, buildDir, saveConfig, explicitSecretKeys, }) {
|
|
129
151
|
if (failedPlugins.includes(packageName) ||
|
|
130
152
|
(condition !== undefined &&
|
|
131
|
-
!(await condition({ buildDir, constants, buildbotServerSocket, netlifyConfig, saveConfig })))) {
|
|
153
|
+
!(await condition({ buildDir, constants, buildbotServerSocket, netlifyConfig, saveConfig, explicitSecretKeys })))) {
|
|
132
154
|
return false;
|
|
133
155
|
}
|
|
134
156
|
if (error !== undefined) {
|
|
@@ -144,7 +166,7 @@ const getFireStep = function (packageName, coreStepId, event) {
|
|
|
144
166
|
const parentTag = normalizeTagName(packageName);
|
|
145
167
|
return measureDuration(tFireStep, event, { parentTag, category: 'pluginEvent' });
|
|
146
168
|
};
|
|
147
|
-
const tFireStep = function ({ event, childProcess, packageName, pluginPackageJson, loadedFrom, origin, coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, nodePath, childEnv, context, branch, envChanges, constants, steps, buildbotServerSocket, events, error, logs, debug, systemLog, verbose, saveConfig, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, userNodeVersion, }) {
|
|
169
|
+
const tFireStep = function ({ event, childProcess, packageName, pluginPackageJson, loadedFrom, origin, coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, nodePath, childEnv, context, branch, envChanges, constants, steps, buildbotServerSocket, events, error, logs, debug, systemLog, verbose, saveConfig, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, userNodeVersion, explicitSecretKeys, }) {
|
|
148
170
|
if (coreStep !== undefined) {
|
|
149
171
|
return fireCoreStep({
|
|
150
172
|
coreStep,
|
|
@@ -174,6 +196,7 @@ const tFireStep = function ({ event, childProcess, packageName, pluginPackageJso
|
|
|
174
196
|
systemLog,
|
|
175
197
|
saveConfig,
|
|
176
198
|
userNodeVersion,
|
|
199
|
+
explicitSecretKeys,
|
|
177
200
|
});
|
|
178
201
|
}
|
|
179
202
|
return firePluginStep({
|
package/lib/steps/run_steps.js
CHANGED
|
@@ -7,7 +7,7 @@ import { runStep } from './run_step.js';
|
|
|
7
7
|
// list of `failedPlugins` (that ran `utils.build.failPlugin()`).
|
|
8
8
|
// If an error arises, runs `onError` events.
|
|
9
9
|
// Runs `onEnd` events at the end, whether an error was thrown or not.
|
|
10
|
-
export const runSteps = async function ({ steps, buildbotServerSocket, events, configPath, outputConfigPath, headersPath, redirectsPath, buildDir, repositoryRoot, nodePath, childEnv, context, branch, constants, mode, api, errorMonitor, deployId, errorParams, netlifyConfig, configOpts, logs, debug, systemLog, verbose, saveConfig, timers, testOpts, featureFlags, quiet, userNodeVersion, }) {
|
|
10
|
+
export const runSteps = async function ({ steps, buildbotServerSocket, events, configPath, outputConfigPath, headersPath, redirectsPath, buildDir, repositoryRoot, nodePath, childEnv, context, branch, constants, mode, api, errorMonitor, deployId, errorParams, netlifyConfig, configOpts, logs, debug, systemLog, verbose, saveConfig, timers, testOpts, featureFlags, quiet, userNodeVersion, explicitSecretKeys, }) {
|
|
11
11
|
const { index: stepsCount, error: errorA, netlifyConfig: netlifyConfigC, statuses: statusesB, failedPlugins: failedPluginsA, timers: timersC, configMutations: configMutationsB, metrics: metricsC, } = await pReduce(steps, async ({ index, error, failedPlugins, envChanges, netlifyConfig: netlifyConfigA, configMutations, headersPath: headersPathA, redirectsPath: redirectsPathA, statuses, timers: timersA, metrics: metricsA, }, { event, childProcess, packageName, coreStep, coreStepId, coreStepName, coreStepDescription, pluginPackageJson, loadedFrom, origin, condition, }) => {
|
|
12
12
|
const { newIndex = index, newError = error, failedPlugin = [], newEnvChanges = {}, netlifyConfig: netlifyConfigB = netlifyConfigA, configMutations: configMutationsA = configMutations, headersPath: headersPathB = headersPathA, redirectsPath: redirectsPathB = redirectsPathA, newStatus, timers: timersB = timersA, metrics: metricsB = [], } = await runStep({
|
|
13
13
|
event,
|
|
@@ -57,6 +57,7 @@ export const runSteps = async function ({ steps, buildbotServerSocket, events, c
|
|
|
57
57
|
featureFlags,
|
|
58
58
|
quiet,
|
|
59
59
|
userNodeVersion,
|
|
60
|
+
explicitSecretKeys,
|
|
60
61
|
});
|
|
61
62
|
const statusesA = addStatus({ newStatus, statuses, event, packageName, pluginPackageJson });
|
|
62
63
|
return {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { HoneycombSDK } from '@honeycombio/opentelemetry-node';
|
|
2
|
+
import { context, trace, propagation, SpanStatusCode } from '@opentelemetry/api';
|
|
3
|
+
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
|
|
4
|
+
import { ROOT_PACKAGE_JSON } from '../utils/json.js';
|
|
5
|
+
let sdk;
|
|
6
|
+
/** Starts the tracing SDK, if there's already a tracing service this will be a no-op */
|
|
7
|
+
export const startTracing = function (options) {
|
|
8
|
+
if (!options.enabled)
|
|
9
|
+
return;
|
|
10
|
+
if (sdk)
|
|
11
|
+
return;
|
|
12
|
+
sdk = new HoneycombSDK({
|
|
13
|
+
serviceName: ROOT_PACKAGE_JSON.name,
|
|
14
|
+
endpoint: `http://${options.host}:${options.port}`,
|
|
15
|
+
instrumentations: [new HttpInstrumentation()],
|
|
16
|
+
});
|
|
17
|
+
sdk.start();
|
|
18
|
+
// Sets the current trace ID and span ID based on the options received
|
|
19
|
+
// this is used as a way to propagate trace context from Buildbot
|
|
20
|
+
trace.setSpanContext(context.active(), {
|
|
21
|
+
traceId: options.traceId,
|
|
22
|
+
spanId: options.parentSpanId,
|
|
23
|
+
traceFlags: options.traceFlags,
|
|
24
|
+
isRemote: true,
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
/** Stops the tracing service if there's one running. This will flush any ongoing events */
|
|
28
|
+
export const stopTracing = async function () {
|
|
29
|
+
if (!sdk)
|
|
30
|
+
return;
|
|
31
|
+
return sdk.shutdown();
|
|
32
|
+
};
|
|
33
|
+
/** Sets attributes to be propagated across child spans under the current context */
|
|
34
|
+
export const setMultiSpanAttributes = function (attributes) {
|
|
35
|
+
const currentBaggage = propagation.getBaggage(context.active());
|
|
36
|
+
let baggage = currentBaggage === undefined ? propagation.createBaggage() : currentBaggage;
|
|
37
|
+
Object.entries(attributes).forEach((entry) => {
|
|
38
|
+
baggage = baggage.setEntry(entry[0], { value: entry[1] });
|
|
39
|
+
});
|
|
40
|
+
return propagation.setBaggage(context.active(), baggage);
|
|
41
|
+
};
|
|
42
|
+
/** Add error information to the current active span (if any) */
|
|
43
|
+
export const addErrorToActiveSpan = function (error) {
|
|
44
|
+
const span = trace.getActiveSpan();
|
|
45
|
+
if (!span)
|
|
46
|
+
return;
|
|
47
|
+
span.recordException(error);
|
|
48
|
+
span.setStatus({
|
|
49
|
+
code: SpanStatusCode.ERROR,
|
|
50
|
+
message: error.message,
|
|
51
|
+
});
|
|
52
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/build",
|
|
3
|
-
"version": "29.
|
|
3
|
+
"version": "29.14.0",
|
|
4
4
|
"description": "Netlify build module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./lib/core/main.js",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"license": "MIT",
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@bugsnag/js": "^7.0.0",
|
|
67
|
+
"@honeycombio/opentelemetry-node": "^0.4.0",
|
|
67
68
|
"@netlify/cache-utils": "^5.1.5",
|
|
68
69
|
"@netlify/config": "^20.5.1",
|
|
69
70
|
"@netlify/edge-bundler": "8.16.2",
|
|
@@ -73,6 +74,8 @@
|
|
|
73
74
|
"@netlify/plugins-list": "^6.68.0",
|
|
74
75
|
"@netlify/run-utils": "^5.1.1",
|
|
75
76
|
"@netlify/zip-it-and-ship-it": "9.10.0",
|
|
77
|
+
"@opentelemetry/api": "^1.4.1",
|
|
78
|
+
"@opentelemetry/instrumentation-http": "^0.40.0",
|
|
76
79
|
"@sindresorhus/slugify": "^2.0.0",
|
|
77
80
|
"ansi-escapes": "^6.0.0",
|
|
78
81
|
"chalk": "^5.0.0",
|
|
@@ -146,5 +149,5 @@
|
|
|
146
149
|
"module": "commonjs"
|
|
147
150
|
}
|
|
148
151
|
},
|
|
149
|
-
"gitHead": "
|
|
152
|
+
"gitHead": "3ec21c7c8e5c221d96b32e739331dc44b18fe05c"
|
|
150
153
|
}
|