@netlify/build 29.4.89-test → 29.5.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 CHANGED
@@ -1,3 +1,4 @@
1
+ import { supportedRuntimes } from '@netlify/framework-info';
1
2
  import { getErrorInfo } from '../error/info.js';
2
3
  import { startErrorMonitor } from '../error/monitor/start.js';
3
4
  import { getBufferLogs, getSystemLogger } from '../log/logger.js';
@@ -30,7 +31,7 @@ export const startBuild = function (flags) {
30
31
  const errorMonitor = startErrorMonitor({ flags: flagsA, logs, bugsnagKey });
31
32
  return { ...flagsA, errorMonitor, logs, timers };
32
33
  };
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
+ 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, }) {
34
35
  const configOpts = getConfigOpts({
35
36
  config,
36
37
  defaultConfig,
@@ -61,6 +62,16 @@ const tExecBuild = async function ({ config, defaultConfig, cachedConfig, cached
61
62
  timers,
62
63
  quiet,
63
64
  });
65
+ if (featureFlags.build_automatic_runtime && framework) {
66
+ const runtime = supportedRuntimes[framework];
67
+ if (runtime !== undefined) {
68
+ const skip = childEnv[runtime.skipFlag] === 'true';
69
+ const installed = netlifyConfig.plugins.some((plugin) => plugin.package === runtime.package);
70
+ if (!installed && !skip) {
71
+ netlifyConfig.plugins.push({ package: runtime.package });
72
+ }
73
+ }
74
+ }
64
75
  const constants = await getConstants({
65
76
  configPath,
66
77
  buildDir,
package/lib/core/main.js CHANGED
@@ -31,6 +31,7 @@ export default async function buildSite(flags = {}) {
31
31
  debug,
32
32
  testOpts,
33
33
  errorParams,
34
+ framework,
34
35
  });
35
36
  await handleBuildSuccess({
36
37
  framework,
@@ -74,7 +75,7 @@ export default async function buildSite(flags = {}) {
74
75
  testOpts,
75
76
  errorParams,
76
77
  });
77
- await reportError({ error, framework, statsdOpts });
78
+ await reportError(error, statsdOpts, framework);
78
79
  return { success, severityCode, logs };
79
80
  }
80
81
  }
@@ -85,7 +86,7 @@ const handleBuildSuccess = async function ({ framework, dry, logs, timers, durat
85
86
  }
86
87
  logBuildSuccess(logs);
87
88
  logTimer(logs, durationNs, 'Netlify Build', systemLog);
88
- await reportTimers({ timers, statsdOpts, framework });
89
+ await reportTimers(timers, statsdOpts, framework);
89
90
  };
90
91
  // Handles the calls and errors of telemetry reports
91
92
  const telemetryReport = async function ({ deployId, buildId, status, stepsCount, pluginsOptions, durationNs, siteInfo, telemetry, userNodeVersion, framework, testOpts, errorParams, }) {
@@ -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
- // Record error rates of the build phase for monitoring.
6
- // Sends to statsd daemon.
7
- export const reportError = async function ({ error, statsdOpts: { host, port }, framework, }) {
8
- if (host === undefined) {
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 client = await startClient(host, port);
20
- const frameworkTag = framework === undefined ? {} : { framework };
21
- client.increment('buildbot.build.stage.error', 1, {
22
- stage: stage ?? 'system',
23
- parent,
24
- ...frameworkTag,
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
  };
@@ -7,9 +7,11 @@ import { getZipError } from './error.js';
7
7
  import { getUserAndInternalFunctions, validateFunctionsSrc } from './utils.js';
8
8
  import { getZisiParameters } from './zisi.js';
9
9
  // Get a list of all unique bundlers in this run
10
- const getBundlers = (results = []) => Array.from(new Set(results
10
+ const getBundlers = (results = []) =>
11
+ // using a Set to filter duplicates
12
+ new Set(results
11
13
  .map((bundle) => (bundle.runtime === "js" /* RuntimeType.JAVASCRIPT */ ? bundle.bundler : null))
12
- .filter(Boolean)));
14
+ .filter(Boolean));
13
15
  const zipFunctionsAndLogResults = async ({ buildDir, childEnv, featureFlags, functionsConfig, functionsDist, functionsSrc, internalFunctionsSrc, isRunningLocally, logs, repositoryRoot, }) => {
14
16
  const zisiParameters = getZisiParameters({
15
17
  buildDir,
@@ -26,7 +28,7 @@ const zipFunctionsAndLogResults = async ({ buildDir, childEnv, featureFlags, fun
26
28
  log(logs, '');
27
29
  const sourceDirectories = [internalFunctionsSrc, functionsSrc].filter(Boolean);
28
30
  const results = await zipItAndShipIt.zipFunctions(sourceDirectories, functionsDist, zisiParameters);
29
- const bundlers = getBundlers(results);
31
+ const bundlers = Array.from(getBundlers(results));
30
32
  logBundleResults({ logs, results });
31
33
  return { bundlers };
32
34
  }
@@ -11,7 +11,15 @@ export const getZisiParameters = ({ buildDir, childEnv, featureFlags, functionsC
11
11
  const zisiFeatureFlags = getZisiFeatureFlags(featureFlags);
12
12
  // Only internal functions are allowed to have a json config file
13
13
  const configFileDirectories = [resolve(internalFunctionsSrc)];
14
- return { basePath: buildDir, config, manifest, featureFlags: zisiFeatureFlags, repositoryRoot, configFileDirectories };
14
+ return {
15
+ basePath: buildDir,
16
+ config,
17
+ manifest,
18
+ featureFlags: zisiFeatureFlags,
19
+ repositoryRoot,
20
+ configFileDirectories,
21
+ internalSrcFolder: internalFunctionsSrc,
22
+ };
15
23
  };
16
24
  // The function configuration keys returned by @netlify/config are not an exact
17
25
  // match to the properties that @netlify/zip-it-and-ship-it expects. We do that
@@ -19,6 +27,7 @@ export const getZisiParameters = ({ buildDir, childEnv, featureFlags, functionsC
19
27
  export const normalizeFunctionConfig = ({ buildDir, functionConfig = {}, isRunningLocally, nodeVersion }) => ({
20
28
  externalNodeModules: functionConfig.external_node_modules,
21
29
  includedFiles: functionConfig.included_files,
30
+ name: functionConfig.name,
22
31
  includedFilesBasePath: buildDir,
23
32
  ignoredNodeModules: functionConfig.ignored_node_modules,
24
33
  nodeVersion,
@@ -1,17 +1,47 @@
1
1
  import { promisify } from 'util';
2
2
  import slugify from '@sindresorhus/slugify';
3
3
  import { StatsD } from 'hot-shots';
4
- export const startClient = async function (host, port) {
5
- return new StatsD({ host, port, cacheDns: true });
4
+ export const validateStatsDOptions = function (statsdOpts) {
5
+ return !!(statsdOpts && statsdOpts.host && statsdOpts.port);
6
6
  };
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
+ */
7
30
  export const closeClient = async function (client) {
8
31
  await promisify(client.close.bind(client))();
9
32
  };
10
- // Make sure the timer name does not include special characters.
11
- // For example, the `packageName` of local plugins includes dots.
33
+ /**
34
+ * Make sure the timer name does not include special characters.
35
+ * For example, the `packageName` of local plugins includes dots.
36
+ */
12
37
  export const normalizeTagName = function (name) {
13
38
  return slugify(name, { separator: '_' });
14
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
+ */
15
45
  export const formatTags = function (tags) {
16
46
  return Object.entries(tags)
17
47
  .map(([key, value]) => {
@@ -1,27 +1,30 @@
1
- import { closeClient, formatTags, 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
- // Record the duration of a build phase, for monitoring.
5
- // Sends to statsd daemon.
6
- export const reportTimers = async function ({ timers, statsdOpts: { host, port }, framework, }) {
7
- if (host === undefined) {
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
- const timersA = addAggregatedTimers(timers);
11
- await sendTimers({ timers: timersA, statsdOpts: { host, port }, framework });
12
+ await sendTimers(addAggregatedTimers(timers), statsdOpts, framework);
12
13
  };
13
- const sendTimers = async function ({ timers, statsdOpts: { host, port }, framework, }) {
14
- const client = await startClient(host, port);
14
+ const sendTimers = async function (timers, statsdOpts, framework) {
15
+ const client = await startClient(statsdOpts);
15
16
  timers.forEach((timer) => {
16
- sendTimer({ timer, client, framework });
17
+ sendTimer(timer, client, framework);
17
18
  });
18
19
  await closeClient(client);
19
20
  };
20
- const sendTimer = function ({ timer: { metricName, stageTag, parentTag, durationNs, tags }, client, framework, }) {
21
+ const sendTimer = function (timer, client, framework) {
22
+ const { metricName, stageTag, parentTag, durationNs, tags } = timer;
21
23
  const durationMs = roundTimerToMillisecs(durationNs);
22
- const statsdTags = { stage: stageTag, parent: parentTag, ...tags };
23
- if (framework != undefined) {
24
- statsdTags.framework = framework;
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;
25
28
  }
26
- client.distribution(metricName, durationMs, formatTags(statsdTags));
29
+ client.distribution(metricName, durationMs, formatTags(statsDTags));
27
30
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/build",
3
- "version": "29.4.89-test",
3
+ "version": "29.5.0",
4
4
  "description": "Netlify build module",
5
5
  "type": "module",
6
6
  "exports": "./lib/core/main.js",
@@ -66,12 +66,12 @@
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.0.0",
70
- "@netlify/functions-utils": "^5.1.1",
69
+ "@netlify/edge-bundler": "8.1.2",
70
+ "@netlify/functions-utils": "^5.1.4",
71
71
  "@netlify/git-utils": "^5.1.0",
72
72
  "@netlify/plugins-list": "^6.61.0",
73
73
  "@netlify/run-utils": "^5.1.0",
74
- "@netlify/zip-it-and-ship-it": "^8.2.0",
74
+ "@netlify/zip-it-and-ship-it": "^8.4.0",
75
75
  "@sindresorhus/slugify": "^2.0.0",
76
76
  "ansi-escapes": "^5.0.0",
77
77
  "chalk": "^5.0.0",
@@ -147,5 +147,6 @@
147
147
  "compilerOptions": {
148
148
  "module": "commonjs"
149
149
  }
150
- }
150
+ },
151
+ "gitHead": "fb15bd56d47418bc03b2e94fc87de89d3e18a7a9"
151
152
  }