@netlify/build 29.33.7 → 29.34.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/dry.js CHANGED
@@ -8,6 +8,9 @@ export const doDryRun = async function ({ buildDir, steps, netlifyConfig, consta
8
8
  const stepsCount = successSteps.length;
9
9
  logDryRunStart({ logs, eventWidth, stepsCount });
10
10
  successSteps.forEach((step, index) => {
11
+ if (step.quiet) {
12
+ return;
13
+ }
11
14
  logDryRunStep({ logs, step, index, netlifyConfig, eventWidth, stepsCount });
12
15
  });
13
16
  logDryRunEnd(logs);
@@ -0,0 +1,2 @@
1
+ import { CoreStep } from '../types.js';
2
+ export declare const applyDeployConfig: CoreStep;
@@ -0,0 +1,70 @@
1
+ import { promises as fs } from 'fs';
2
+ import { resolve } from 'path';
3
+ import { mergeConfigs } from '@netlify/config';
4
+ import { filterConfig } from './util.js';
5
+ // The properties that can be set using this API. Each element represents a
6
+ // path using dot-notation — e.g. `["build", "functions"]` represents the
7
+ // `build.functions` property.
8
+ const ALLOWED_PROPERTIES = [
9
+ ['build', 'edge_functions'],
10
+ ['build', 'functions'],
11
+ ['build', 'publish'],
12
+ ['functions', '*'],
13
+ ['functions', '*', '*'],
14
+ ['headers'],
15
+ ['images', 'remote_images'],
16
+ ['redirects'],
17
+ ];
18
+ // For array properties, any values set in this API will be merged with the
19
+ // main configuration file in such a way that user-defined values always take
20
+ // precedence. The exception are these properties that let frameworks set
21
+ // values that should be evaluated before any user-defined values. They use
22
+ // a special notation where `headers!` represents "forced headers", etc.
23
+ const OVERRIDE_PROPERTIES = new Set(['headers!', 'redirects!']);
24
+ const coreStep = async function ({ buildDir, netlifyConfig, systemLog = () => {
25
+ // no-op
26
+ }, }) {
27
+ const configPath = resolve(buildDir, '.netlify/deploy/v1/config.json');
28
+ let config = {};
29
+ try {
30
+ const data = await fs.readFile(configPath, 'utf8');
31
+ config = JSON.parse(data);
32
+ }
33
+ catch (err) {
34
+ // If the file doesn't exist, this is a non-error.
35
+ if (err.code === 'ENOENT') {
36
+ return {};
37
+ }
38
+ systemLog(`Failed to read Deploy Configuration API: ${err.message}`);
39
+ throw new Error('An error occured while processing the platform configurarion defined by your framework');
40
+ }
41
+ const configOverrides = {};
42
+ for (const key in config) {
43
+ // If the key uses the special notation for defining mutations that should
44
+ // take precedence over user-defined properties, extract the canonical
45
+ // property, set it on a different object, and delete it from the main one.
46
+ if (OVERRIDE_PROPERTIES.has(key)) {
47
+ const canonicalKey = key.slice(0, -1);
48
+ configOverrides[canonicalKey] = config[key];
49
+ delete config[key];
50
+ }
51
+ }
52
+ // Filtering out any properties that can't be mutated using this API.
53
+ config = filterConfig(config, [], ALLOWED_PROPERTIES, systemLog);
54
+ // Merging the different configuration sources. The order here is important.
55
+ // Leftmost elements of the array take precedence.
56
+ const newConfig = mergeConfigs([config, netlifyConfig, configOverrides], { concatenateArrays: true });
57
+ for (const key in newConfig) {
58
+ netlifyConfig[key] = newConfig[key];
59
+ }
60
+ return {};
61
+ };
62
+ export const applyDeployConfig = {
63
+ event: 'onBuild',
64
+ coreStep,
65
+ coreStepId: 'deploy_config',
66
+ coreStepName: 'Applying Deploy Configuration',
67
+ coreStepDescription: () => '',
68
+ condition: ({ featureFlags }) => featureFlags?.netlify_build_deploy_configuration_api,
69
+ quiet: true,
70
+ };
@@ -0,0 +1,12 @@
1
+ import { SystemLogger } from '../types.js';
2
+ /**
3
+ * Checks whether a property can be defined using the Deploy Configuration API.
4
+ */
5
+ export declare const isAllowedProperty: (property: string[], allowedProperties: string[][]) => boolean;
6
+ /**
7
+ * Takes a candidate configuration object and returns a normalized version that
8
+ * includes only the properties that are present in an allow-list. It does so
9
+ * recursively, so `allowedProperties` can contain the full list of properties
10
+ * that the Deploy Configuration API can interact with.
11
+ */
12
+ export declare const filterConfig: (obj: Record<string, unknown>, path: string[], allowedProperties: string[][], systemLog: SystemLogger) => Record<string, unknown>;
@@ -0,0 +1,32 @@
1
+ import isPlainObject from 'is-plain-obj';
2
+ import mapObject, { mapObjectSkip } from 'map-obj';
3
+ /**
4
+ * Checks whether a property matches a template that may contain wildcards.
5
+ * Both the property and the template use a dot-notation represented as an
6
+ * array of strings.
7
+ * For example, the property `["build", "command"]` matches the templates
8
+ * `["build", "command"]` and `["build", "*"]`, but not `["functions"]`.
9
+ */
10
+ const propertyMatchesTemplate = (property, template) => property.every((node, index) => template[index] === node || template[index] === '*');
11
+ /**
12
+ * Checks whether a property can be defined using the Deploy Configuration API.
13
+ */
14
+ export const isAllowedProperty = (property, allowedProperties) => allowedProperties.some((template) => propertyMatchesTemplate(property, template));
15
+ /**
16
+ * Takes a candidate configuration object and returns a normalized version that
17
+ * includes only the properties that are present in an allow-list. It does so
18
+ * recursively, so `allowedProperties` can contain the full list of properties
19
+ * that the Deploy Configuration API can interact with.
20
+ */
21
+ export const filterConfig = (obj, path, allowedProperties, systemLog) => mapObject(obj, (key, value) => {
22
+ const keyPath = [...path, key];
23
+ if (!isAllowedProperty(keyPath, allowedProperties)) {
24
+ systemLog(`Discarding property that is not supported by the Deploy Configuration API: ${keyPath.join('.')}`);
25
+ return mapObjectSkip;
26
+ }
27
+ if (!isPlainObject(value)) {
28
+ systemLog(`Loading property from Deploy Configuration API: ${keyPath.join('.')}`);
29
+ return [key, value];
30
+ }
31
+ return [key, filterConfig(value, keyPath, allowedProperties, systemLog)];
32
+ });
@@ -28,13 +28,15 @@ export type CoreStepFunctionArgs = {
28
28
  };
29
29
  export type CoreStepFunction = (args: CoreStepFunctionArgs) => Promise<object>;
30
30
  export type CoreStepCondition = (args: CoreStepFunctionArgs) => Promise<boolean> | boolean;
31
- type Event = 'onPreBuild' | 'onBuild' | 'onPostBuild' | 'onPreDev' | 'onDev' | 'onPostDev';
31
+ export type Event = 'onPreBuild' | 'onBuild' | 'onPostBuild' | 'onPreDev' | 'onDev' | 'onPostDev';
32
32
  export type CoreStep = {
33
33
  event: Event;
34
34
  coreStep: CoreStepFunction;
35
35
  coreStepId: string;
36
36
  coreStepName: string;
37
37
  coreStepDescription: () => string;
38
- condition: CoreStepCondition;
38
+ condition?: CoreStepCondition;
39
+ quiet?: boolean;
39
40
  };
41
+ export type SystemLogger = (...args: any[]) => void;
40
42
  export {};
@@ -1,8 +1,9 @@
1
+ import { CoreStep } from '../plugins_core/types.js';
1
2
  export declare const getSteps: (steps: any, eventHandlers?: any[]) => {
2
- steps: any;
3
+ steps: CoreStep[];
3
4
  events: unknown[];
4
5
  };
5
6
  export declare const getDevSteps: (command: any, steps: any, eventHandlers?: any[]) => {
6
- steps: any;
7
+ steps: CoreStep[];
7
8
  events: unknown[];
8
9
  };
package/lib/steps/get.js CHANGED
@@ -3,6 +3,7 @@ import { DEV_EVENTS, EVENTS } from '../plugins/events.js';
3
3
  import { uploadBlobs } from '../plugins_core/blobs_upload/index.js';
4
4
  import { buildCommandCore } from '../plugins_core/build_command.js';
5
5
  import { deploySite } from '../plugins_core/deploy/index.js';
6
+ import { applyDeployConfig } from '../plugins_core/deploy_config/index.js';
6
7
  import { bundleEdgeFunctions } from '../plugins_core/edge_functions/index.js';
7
8
  import { bundleFunctions } from '../plugins_core/functions/index.js';
8
9
  import { preCleanup } from '../plugins_core/pre_cleanup/index.js';
@@ -44,7 +45,7 @@ const getEventSteps = function (eventHandlers) {
44
45
  handler = eventHandler.handler;
45
46
  }
46
47
  return {
47
- event,
48
+ event: event,
48
49
  coreStep: (args) => {
49
50
  const { constants, event } = args;
50
51
  const utils = getUtils({ event, constants, runState: {} });
@@ -60,6 +61,7 @@ const addCoreSteps = function (steps) {
60
61
  return [
61
62
  preCleanup,
62
63
  buildCommandCore,
64
+ applyDeployConfig,
63
65
  ...steps,
64
66
  bundleFunctions,
65
67
  bundleEdgeFunctions,
@@ -1,4 +1,4 @@
1
- export declare const runStep: ({ event, childProcess, packageName, coreStep, coreStepId, coreStepName, coreStepDescription, pluginPackageJson, loadedFrom, origin, condition, configPath, outputConfigPath, buildDir, packagePath, 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, edgeFunctionsBootstrapURL, }: {
1
+ export declare const runStep: ({ event, childProcess, packageName, coreStep, coreStepId, coreStepName, coreStepDescription, coreStepQuiet, pluginPackageJson, loadedFrom, origin, condition, configPath, outputConfigPath, buildDir, packagePath, 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, edgeFunctionsBootstrapURL, }: {
2
2
  event: any;
3
3
  childProcess: any;
4
4
  packageName: any;
@@ -6,6 +6,7 @@ export declare const runStep: ({ event, childProcess, packageName, coreStep, cor
6
6
  coreStepId: any;
7
7
  coreStepName: any;
8
8
  coreStepDescription: any;
9
+ coreStepQuiet: any;
9
10
  pluginPackageJson: any;
10
11
  loadedFrom: any;
11
12
  origin: any;
@@ -10,7 +10,7 @@ import { firePluginStep } from './plugin.js';
10
10
  import { getStepReturn } from './return.js';
11
11
  const tracer = trace.getTracer('steps');
12
12
  // Run a step (core, build command or plugin)
13
- export const runStep = async function ({ event, childProcess, packageName, coreStep, coreStepId, coreStepName, coreStepDescription, pluginPackageJson, loadedFrom, origin, condition, configPath, outputConfigPath, buildDir, packagePath, 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, edgeFunctionsBootstrapURL, }) {
13
+ export const runStep = async function ({ event, childProcess, packageName, coreStep, coreStepId, coreStepName, coreStepDescription, coreStepQuiet, pluginPackageJson, loadedFrom, origin, condition, configPath, outputConfigPath, buildDir, packagePath, 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, edgeFunctionsBootstrapURL, }) {
14
14
  // Add relevant attributes to the upcoming span context
15
15
  const attributes = {
16
16
  'build.execution.step.name': coreStepName,
@@ -41,13 +41,14 @@ export const runStep = async function ({ event, childProcess, packageName, coreS
41
41
  saveConfig,
42
42
  explicitSecretKeys,
43
43
  deployId,
44
+ featureFlags,
44
45
  });
45
46
  span.setAttribute('build.execution.step.should_run', shouldRun);
46
47
  if (!shouldRun) {
47
48
  span.end();
48
49
  return {};
49
50
  }
50
- if (!quiet) {
51
+ if (!quiet && !coreStepQuiet) {
51
52
  logStepStart({ logs, event, packageName, coreStepDescription, error, netlifyConfig });
52
53
  }
53
54
  const fireStep = getFireStep(packageName, coreStepId, event);
@@ -118,7 +119,7 @@ export const runStep = async function ({ event, childProcess, packageName, coreS
118
119
  durationNs,
119
120
  testOpts,
120
121
  systemLog,
121
- quiet,
122
+ quiet: quiet || coreStepQuiet,
122
123
  metrics,
123
124
  });
124
125
  span.end();
@@ -156,7 +157,7 @@ export const runStep = async function ({ event, childProcess, packageName, coreS
156
157
  // or available. However, one might be created by a build plugin, in which case,
157
158
  // those core plugins should be triggered. We use a dynamic `condition()` to
158
159
  // model this behavior.
159
- const shouldRunStep = async function ({ event, packageName, error, packagePath, failedPlugins, netlifyConfig, condition, constants, buildbotServerSocket, buildDir, saveConfig, explicitSecretKeys, deployId, }) {
160
+ const shouldRunStep = async function ({ event, packageName, error, packagePath, failedPlugins, netlifyConfig, condition, constants, buildbotServerSocket, buildDir, saveConfig, explicitSecretKeys, deployId, featureFlags = {}, }) {
160
161
  if (failedPlugins.includes(packageName) ||
161
162
  (condition !== undefined &&
162
163
  !(await condition({
@@ -168,6 +169,7 @@ const shouldRunStep = async function ({ event, packageName, error, packagePath,
168
169
  saveConfig,
169
170
  explicitSecretKeys,
170
171
  deployId,
172
+ featureFlags,
171
173
  })))) {
172
174
  return false;
173
175
  }
@@ -8,7 +8,7 @@ import { runStep } from './run_step.js';
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
10
  export const runSteps = async function ({ steps, buildbotServerSocket, events, configPath, outputConfigPath, headersPath, redirectsPath, buildDir, packagePath, repositoryRoot, nodePath, childEnv, context, branch, constants, mode, api, errorMonitor, deployId, errorParams, netlifyConfig, configOpts, logs, debug, systemLog, verbose, saveConfig, timers, testOpts, featureFlags, quiet, userNodeVersion, explicitSecretKeys, edgeFunctionsBootstrapURL, }) {
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, }) => {
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, quiet: coreStepQuiet, }) => {
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,
14
14
  childProcess,
@@ -17,6 +17,7 @@ export const runSteps = async function ({ steps, buildbotServerSocket, events, c
17
17
  coreStepId,
18
18
  coreStepName,
19
19
  coreStepDescription,
20
+ coreStepQuiet,
20
21
  pluginPackageJson,
21
22
  loadedFrom,
22
23
  origin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/build",
3
- "version": "29.33.7",
3
+ "version": "29.34.0",
4
4
  "description": "Netlify build module",
5
5
  "type": "module",
6
6
  "exports": "./lib/index.js",
@@ -70,7 +70,7 @@
70
70
  "@bugsnag/js": "^7.0.0",
71
71
  "@netlify/blobs": "^6.5.0",
72
72
  "@netlify/cache-utils": "^5.1.5",
73
- "@netlify/config": "^20.11.1",
73
+ "@netlify/config": "^20.12.0",
74
74
  "@netlify/edge-bundler": "11.2.2",
75
75
  "@netlify/framework-info": "^9.8.10",
76
76
  "@netlify/functions-utils": "^5.2.50",
@@ -163,5 +163,5 @@
163
163
  "engines": {
164
164
  "node": "^14.16.0 || >=16.0.0"
165
165
  },
166
- "gitHead": "178e6b05e2befca86cbd0ecc73814f9006a129dc"
166
+ "gitHead": "24f130e0d2e27091cd5998cb23f19718e3785824"
167
167
  }