@netlify/build 29.4.4-rc → 29.4.4
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/bin.js +4 -4
- package/lib/core/build.js +1 -15
- package/lib/core/main.js +2 -3
- package/lib/error/report.js +14 -12
- package/lib/report/statsd.js +48 -23
- package/lib/time/report.js +19 -13
- package/package.json +7 -7
package/lib/core/bin.js
CHANGED
|
@@ -3,10 +3,10 @@ import process from 'process';
|
|
|
3
3
|
import filterObj from 'filter-obj';
|
|
4
4
|
import yargs from 'yargs';
|
|
5
5
|
import { hideBin } from 'yargs/helpers';
|
|
6
|
-
import { normalizeCliFeatureFlags } from '
|
|
7
|
-
import { FLAGS } from '
|
|
8
|
-
import build from '
|
|
9
|
-
import { FALLBACK_SEVERITY_ENTRY } from '
|
|
6
|
+
import { normalizeCliFeatureFlags } from './feature_flags.js';
|
|
7
|
+
import { FLAGS } from './flags.js';
|
|
8
|
+
import build from './main.js';
|
|
9
|
+
import { FALLBACK_SEVERITY_ENTRY } from './severity.js';
|
|
10
10
|
// CLI entry point.
|
|
11
11
|
// Before adding logic to this file, please consider adding it to the main
|
|
12
12
|
// programmatic command instead, so that the new logic is available when run
|
package/lib/core/build.js
CHANGED
|
@@ -30,7 +30,7 @@ export const startBuild = function (flags) {
|
|
|
30
30
|
const errorMonitor = startErrorMonitor({ flags: flagsA, logs, bugsnagKey });
|
|
31
31
|
return { ...flagsA, errorMonitor, logs, timers };
|
|
32
32
|
};
|
|
33
|
-
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,
|
|
33
|
+
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, }) {
|
|
34
34
|
const configOpts = getConfigOpts({
|
|
35
35
|
config,
|
|
36
36
|
defaultConfig,
|
|
@@ -61,16 +61,6 @@ const tExecBuild = async function ({ config, defaultConfig, cachedConfig, cached
|
|
|
61
61
|
timers,
|
|
62
62
|
quiet,
|
|
63
63
|
});
|
|
64
|
-
if (framework) {
|
|
65
|
-
const runtime = supportedRuntimes[framework];
|
|
66
|
-
if (runtime !== undefined) {
|
|
67
|
-
const dontSkip = childEnv[runtime.skipFlag] !== 'true';
|
|
68
|
-
const installed = netlifyConfig.plugins.map((plugin) => plugin.package).includes(runtime.package);
|
|
69
|
-
if (!installed && dontSkip) {
|
|
70
|
-
netlifyConfig.plugins.push({ package: runtime.package });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
64
|
const constants = await getConstants({
|
|
75
65
|
configPath,
|
|
76
66
|
buildDir,
|
|
@@ -376,7 +366,3 @@ const runBuild = async function ({ childProcesses, pluginsOptions, netlifyConfig
|
|
|
376
366
|
});
|
|
377
367
|
return { stepsCount, netlifyConfig: netlifyConfigA, statuses, failedPlugins, timers: timersB, configMutations };
|
|
378
368
|
};
|
|
379
|
-
const supportedRuntimes = {
|
|
380
|
-
next: { package: '@netlify/plugin-nextjs', skipFlag: 'NETLIFY_NEXT_PLUGIN_SKIP' },
|
|
381
|
-
gatsby: { package: '@netlify/plugin-gatsby', skipFlag: 'NETLIFY_GATSBY_PLUGIN_SKIP' },
|
|
382
|
-
};
|
package/lib/core/main.js
CHANGED
|
@@ -31,7 +31,6 @@ export default async function buildSite(flags = {}) {
|
|
|
31
31
|
debug,
|
|
32
32
|
testOpts,
|
|
33
33
|
errorParams,
|
|
34
|
-
framework,
|
|
35
34
|
});
|
|
36
35
|
await handleBuildSuccess({
|
|
37
36
|
framework,
|
|
@@ -75,7 +74,7 @@ export default async function buildSite(flags = {}) {
|
|
|
75
74
|
testOpts,
|
|
76
75
|
errorParams,
|
|
77
76
|
});
|
|
78
|
-
await reportError(
|
|
77
|
+
await reportError(error, statsdOpts, framework);
|
|
79
78
|
return { success, severityCode, logs };
|
|
80
79
|
}
|
|
81
80
|
}
|
|
@@ -86,7 +85,7 @@ const handleBuildSuccess = async function ({ framework, dry, logs, timers, durat
|
|
|
86
85
|
}
|
|
87
86
|
logBuildSuccess(logs);
|
|
88
87
|
logTimer(logs, durationNs, 'Netlify Build', systemLog);
|
|
89
|
-
await reportTimers(
|
|
88
|
+
await reportTimers(timers, statsdOpts, framework);
|
|
90
89
|
};
|
|
91
90
|
// Handles the calls and errors of telemetry reports
|
|
92
91
|
const telemetryReport = async function ({ deployId, buildId, status, stepsCount, pluginsOptions, durationNs, siteInfo, telemetry, userNodeVersion, framework, testOpts, errorParams, }) {
|
package/lib/error/report.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { isNetlifyMaintainedPlugin } from '../plugins/internal.js';
|
|
2
|
-
import { closeClient, normalizeTagName, startClient } from '../report/statsd.js';
|
|
2
|
+
import { closeClient, formatTags, normalizeTagName, startClient, validateStatsDOptions, } from '../report/statsd.js';
|
|
3
3
|
import { getErrorInfo } from './info.js';
|
|
4
4
|
const TOP_PARENT_TAG = 'run_netlify_build';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Record error rates of the build phase for monitoring.
|
|
7
|
+
* Sends to statsd daemon.
|
|
8
|
+
*/
|
|
9
|
+
export const reportError = async function (error, statsdOpts, framework) {
|
|
10
|
+
if (!validateStatsDOptions(statsdOpts)) {
|
|
9
11
|
return;
|
|
10
12
|
}
|
|
11
13
|
const [errorInfo] = getErrorInfo(error);
|
|
@@ -16,12 +18,12 @@ export const reportError = async function ({ error, statsdOpts: { host, port },
|
|
|
16
18
|
}
|
|
17
19
|
const parent = pluginName ? pluginName : TOP_PARENT_TAG;
|
|
18
20
|
const stage = pluginName ? errorInfo.location?.event : errorInfo.stage;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const statsDTags = { stage: stage ?? 'system', parent };
|
|
22
|
+
// Do not add a framework tag if empty string or null/undefined
|
|
23
|
+
if (framework) {
|
|
24
|
+
statsDTags.framework = framework;
|
|
25
|
+
}
|
|
26
|
+
const client = await startClient(statsdOpts);
|
|
27
|
+
client.increment('buildbot.build.stage.error', 1, formatTags(statsDTags));
|
|
26
28
|
await closeClient(client);
|
|
27
29
|
};
|
package/lib/report/statsd.js
CHANGED
|
@@ -1,31 +1,56 @@
|
|
|
1
1
|
import { promisify } from 'util';
|
|
2
2
|
import slugify from '@sindresorhus/slugify';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// See https://github.com/msiebuhr/node-statsd-client/blob/45a93ee4c94ca72f244a40b06cb542d4bd7c3766/lib/EphemeralSocket.js#L81
|
|
7
|
-
const CLOSE_TIMEOUT = 11;
|
|
8
|
-
// The socket creation is delayed until the first packet is sent. In our
|
|
9
|
-
// case, this happens just before `client.close()` is called, which is too
|
|
10
|
-
// late and make it not send anything. We need to manually create it using
|
|
11
|
-
// the internal API.
|
|
12
|
-
export const startClient = async function (host, port) {
|
|
13
|
-
const client = new StatsdClient({ host, port, socketTimeout: 0 });
|
|
14
|
-
// @ts-expect-error using internals :D
|
|
15
|
-
await promisify(client._socket._createSocket.bind(client._socket))();
|
|
16
|
-
return client;
|
|
3
|
+
import { StatsD } from 'hot-shots';
|
|
4
|
+
export const validateStatsDOptions = function (statsdOpts) {
|
|
5
|
+
return !!(statsdOpts && statsdOpts.host && statsdOpts.port);
|
|
17
6
|
};
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Start a new StatsD Client and a new UDP socket
|
|
9
|
+
*/
|
|
10
|
+
export const startClient = async function (statsdOpts) {
|
|
11
|
+
const { host, port } = statsdOpts;
|
|
12
|
+
return new StatsD({
|
|
13
|
+
host,
|
|
14
|
+
port,
|
|
15
|
+
// This caches the dns resolution for subsequent sends of metrics for this instance
|
|
16
|
+
// Because we only try to send the metrics on close, this comes only into effect if `bufferFlushInterval` time is exceeded
|
|
17
|
+
cacheDns: true,
|
|
18
|
+
// set the maxBufferSize to infinite and the bufferFlushInterval very high, so that we only
|
|
19
|
+
// send the metrics on close or if more than 10 seconds past by
|
|
20
|
+
maxBufferSize: Infinity,
|
|
21
|
+
bufferFlushInterval: 10_000,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Close the StatsD Client
|
|
26
|
+
*
|
|
27
|
+
* UDP packets are buffered and flushed every 10 seconds and
|
|
28
|
+
* closing the client forces all buffered metrics to be flushed.
|
|
29
|
+
*/
|
|
20
30
|
export const closeClient = async function (client) {
|
|
21
|
-
client.close();
|
|
22
|
-
// statsd-client does not provide a way of knowing when the socket is done
|
|
23
|
-
// closing, so we need to use the following hack.
|
|
24
|
-
await pSetTimeout(CLOSE_TIMEOUT);
|
|
25
|
-
await pSetTimeout(CLOSE_TIMEOUT);
|
|
31
|
+
await promisify(client.close.bind(client))();
|
|
26
32
|
};
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Make sure the timer name does not include special characters.
|
|
35
|
+
* For example, the `packageName` of local plugins includes dots.
|
|
36
|
+
*/
|
|
29
37
|
export const normalizeTagName = function (name) {
|
|
30
38
|
return slugify(name, { separator: '_' });
|
|
31
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* Formats and flattens the tags as array
|
|
42
|
+
* We do this because we might send the same tag with different values `{ tag: ['a', 'b'] }`
|
|
43
|
+
* which cannot be represented in an flattened object and `hot-shots` also supports tags as array in the format `['tag:a', 'tag:b']`
|
|
44
|
+
*/
|
|
45
|
+
export const formatTags = function (tags) {
|
|
46
|
+
return Object.entries(tags)
|
|
47
|
+
.map(([key, value]) => {
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
return value.map((subValue) => `${key}:${subValue}`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
return `${key}:${value}`;
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
.flat();
|
|
56
|
+
};
|
package/lib/time/report.js
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
|
-
import { closeClient, startClient } from '../report/statsd.js';
|
|
1
|
+
import { closeClient, formatTags, startClient, validateStatsDOptions, } from '../report/statsd.js';
|
|
2
2
|
import { addAggregatedTimers } from './aggregate.js';
|
|
3
3
|
import { roundTimerToMillisecs } from './measure.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Record the duration of a build phase, for monitoring.
|
|
6
|
+
* Sends to statsd daemon.
|
|
7
|
+
*/
|
|
8
|
+
export const reportTimers = async function (timers, statsdOpts, framework) {
|
|
9
|
+
if (!validateStatsDOptions(statsdOpts)) {
|
|
8
10
|
return;
|
|
9
11
|
}
|
|
10
|
-
|
|
11
|
-
await sendTimers({ timers: timersA, host, port, framework });
|
|
12
|
+
await sendTimers(addAggregatedTimers(timers), statsdOpts, framework);
|
|
12
13
|
};
|
|
13
|
-
const sendTimers = async function (
|
|
14
|
-
const client = await startClient(
|
|
14
|
+
const sendTimers = async function (timers, statsdOpts, framework) {
|
|
15
|
+
const client = await startClient(statsdOpts);
|
|
15
16
|
timers.forEach((timer) => {
|
|
16
|
-
sendTimer(
|
|
17
|
+
sendTimer(timer, client, framework);
|
|
17
18
|
});
|
|
18
19
|
await closeClient(client);
|
|
19
20
|
};
|
|
20
|
-
const sendTimer = function (
|
|
21
|
+
const sendTimer = function (timer, client, framework) {
|
|
22
|
+
const { metricName, stageTag, parentTag, durationNs, tags } = timer;
|
|
21
23
|
const durationMs = roundTimerToMillisecs(durationNs);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
+
const statsDTags = { stage: stageTag, parent: parentTag, ...tags };
|
|
25
|
+
// Do not add a framework tag if empty string or null/undefined
|
|
26
|
+
if (framework) {
|
|
27
|
+
statsDTags.framework = framework;
|
|
28
|
+
}
|
|
29
|
+
client.distribution(metricName, durationMs, formatTags(statsDTags));
|
|
24
30
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/build",
|
|
3
|
-
"version": "29.4.4
|
|
3
|
+
"version": "29.4.4",
|
|
4
4
|
"description": "Netlify build module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./lib/core/main.js",
|
|
@@ -66,10 +66,10 @@
|
|
|
66
66
|
"@bugsnag/js": "^7.0.0",
|
|
67
67
|
"@netlify/cache-utils": "^5.1.0",
|
|
68
68
|
"@netlify/config": "^20.3.0",
|
|
69
|
-
"@netlify/edge-bundler": "8.
|
|
70
|
-
"@netlify/functions-utils": "^5.1.
|
|
69
|
+
"@netlify/edge-bundler": "8.1.2",
|
|
70
|
+
"@netlify/functions-utils": "^5.1.3",
|
|
71
71
|
"@netlify/git-utils": "^5.1.0",
|
|
72
|
-
"@netlify/plugins-list": "^6.
|
|
72
|
+
"@netlify/plugins-list": "^6.61.0",
|
|
73
73
|
"@netlify/run-utils": "^5.1.0",
|
|
74
74
|
"@netlify/zip-it-and-ship-it": "^8.2.0",
|
|
75
75
|
"@sindresorhus/slugify": "^2.0.0",
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"figures": "^4.0.0",
|
|
81
81
|
"filter-obj": "^3.0.0",
|
|
82
82
|
"got": "^10.0.0",
|
|
83
|
+
"hot-shots": "9.3.0",
|
|
83
84
|
"indent-string": "^5.0.0",
|
|
84
85
|
"is-plain-obj": "^4.0.0",
|
|
85
86
|
"js-yaml": "^4.0.0",
|
|
@@ -105,7 +106,6 @@
|
|
|
105
106
|
"rfdc": "^1.3.0",
|
|
106
107
|
"safe-json-stringify": "^1.2.0",
|
|
107
108
|
"semver": "^7.0.0",
|
|
108
|
-
"statsd-client": "0.4.7",
|
|
109
109
|
"string-width": "^5.0.0",
|
|
110
110
|
"strip-ansi": "^7.0.0",
|
|
111
111
|
"supports-color": "^9.0.0",
|
|
@@ -120,7 +120,6 @@
|
|
|
120
120
|
"devDependencies": {
|
|
121
121
|
"@netlify/nock-udp": "^3.1.0",
|
|
122
122
|
"@types/node": "^14.18.31",
|
|
123
|
-
"@types/statsd-client": "^0.4.3",
|
|
124
123
|
"atob": "^2.1.2",
|
|
125
124
|
"ava": "^4.0.0",
|
|
126
125
|
"c8": "^7.12.0",
|
|
@@ -148,5 +147,6 @@
|
|
|
148
147
|
"compilerOptions": {
|
|
149
148
|
"module": "commonjs"
|
|
150
149
|
}
|
|
151
|
-
}
|
|
150
|
+
},
|
|
151
|
+
"gitHead": "147214c85a80173f3a19380e9a78ac5e387265ba"
|
|
152
152
|
}
|