@netlify/build 29.28.2 → 29.29.1

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.
@@ -6,6 +6,8 @@ export type BuildCLIFlags = {
6
6
  siteId: string;
7
7
  /** Netlify API token for authentication */
8
8
  token: string;
9
+ /** Netlify Deploy ID */
10
+ deployId: string;
9
11
  /**
10
12
  * Run in dry mode, i.e. printing steps without executing them
11
13
  * @default false
@@ -41,10 +41,14 @@ const getEventMessage = function (event) {
41
41
  const getApiLocation = function ({ endpoint, parameters }) {
42
42
  return `While calling the Netlify API endpoint '${endpoint}' with:\n${JSON.stringify(parameters, null, 2)}`;
43
43
  };
44
+ const getDeployLocation = function ({ statusCode }) {
45
+ return `At deploy the stage with HTTP status code '${statusCode}'`;
46
+ };
44
47
  const LOCATIONS = {
45
48
  buildCommand: getBuildCommandLocation,
46
49
  functionsBundling: getFunctionsBundlingLocation,
47
50
  coreStep: getCoreStepLocation,
48
51
  buildFail: getBuildFailLocation,
49
52
  api: getApiLocation,
53
+ deploy: getDeployLocation,
50
54
  };
@@ -52,9 +52,6 @@ declare enum StackType {
52
52
  * not printed
53
53
  */
54
54
  none = "none",
55
- /**
56
- * printed as is
57
- */
58
55
  stack = "stack",
59
56
  /**
60
57
  * printed as is, but taken from `error.message`. Used when `error.stack` is not being correct due to the error being passed between different processes.
@@ -68,10 +65,16 @@ export type TitleFunction = ({ location }: {
68
65
  location: ErrorLocation;
69
66
  }) => string;
70
67
  export type ErrorInfo = {
71
- plugin?: any;
68
+ plugin?: PluginInfo;
72
69
  tsConfig?: any;
73
70
  location: ErrorLocation;
74
71
  };
72
+ type PluginInfo = {
73
+ packageName: string;
74
+ pluginPackageJson: {
75
+ version?: string;
76
+ };
77
+ };
75
78
  export type BuildCommandLocation = {
76
79
  buildCommand: string;
77
80
  buildCommandOrigin: string;
@@ -99,7 +102,11 @@ export type APILocation = {
99
102
  parameters?: any;
100
103
  };
101
104
  export declare const isAPILocation: (location?: ErrorLocation) => location is APILocation;
102
- export type ErrorLocation = BuildCommandLocation | FunctionsBundlingLocation | CoreStepLocation | PluginLocation | APILocation;
105
+ export type DeployLocation = {
106
+ statusCode: string;
107
+ };
108
+ export declare const isDeployLocation: (location?: ErrorLocation) => location is DeployLocation;
109
+ export type ErrorLocation = BuildCommandLocation | FunctionsBundlingLocation | CoreStepLocation | PluginLocation | APILocation | DeployLocation;
103
110
  /**
104
111
  * Given a BuildError, extract the relevant trace attributes to add to the on-going Span
105
112
  */
@@ -168,6 +175,8 @@ declare const ErrorTypeMap: {
168
175
  readonly trustedPlugin: "trustedPlugin";
169
176
  readonly coreStep: "coreStep";
170
177
  readonly api: "api";
178
+ readonly deploy: "deploy";
179
+ readonly deployInternal: "deployInternal";
171
180
  readonly exception: "exception";
172
181
  readonly telemetry: "telemetry";
173
182
  };
@@ -33,7 +33,7 @@ var StackType;
33
33
  * not printed
34
34
  */
35
35
  StackType["none"] = "none";
36
- /**
36
+ /*
37
37
  * printed as is
38
38
  */
39
39
  StackType["stack"] = "stack";
@@ -62,6 +62,9 @@ export const isPluginLocation = function (location) {
62
62
  export const isAPILocation = function (location) {
63
63
  return typeof location?.endpoint === 'string';
64
64
  };
65
+ export const isDeployLocation = function (location) {
66
+ return typeof location?.statusCode === 'string';
67
+ };
65
68
  const buildErrorAttributePrefix = 'build.error';
66
69
  const errorLocationToTracingAttributes = function (location) {
67
70
  const locationAttributePrefix = `${buildErrorAttributePrefix}.location`;
@@ -95,8 +98,22 @@ const errorLocationToTracingAttributes = function (location) {
95
98
  [`${locationAttributePrefix}.api.endpoint`]: location.endpoint,
96
99
  };
97
100
  }
101
+ if (isDeployLocation(location)) {
102
+ return {
103
+ [`${locationAttributePrefix}.deploy.status_code`]: location.statusCode,
104
+ };
105
+ }
98
106
  return {};
99
107
  };
108
+ const pluginDataToTracingAttributes = function (pluginInfo) {
109
+ const pluginAttributePrefix = `${buildErrorAttributePrefix}.plugin`;
110
+ if (typeof pluginInfo === 'undefined')
111
+ return {};
112
+ return {
113
+ [`${pluginAttributePrefix}.name`]: pluginInfo?.packageName,
114
+ [`${pluginAttributePrefix}.version`]: pluginInfo?.pluginPackageJson?.version,
115
+ };
116
+ };
100
117
  /**
101
118
  * Given a BuildError, extract the relevant trace attributes to add to the on-going Span
102
119
  */
@@ -114,6 +131,7 @@ export const buildErrorToTracingAttributes = function (error) {
114
131
  return {
115
132
  ...attributes,
116
133
  ...errorLocationToTracingAttributes(error.errorInfo?.location),
134
+ ...pluginDataToTracingAttributes(error.errorInfo?.plugin),
117
135
  };
118
136
  };
119
137
  /**
@@ -144,6 +162,8 @@ const ErrorTypeMap = {
144
162
  trustedPlugin: 'trustedPlugin',
145
163
  coreStep: 'coreStep',
146
164
  api: 'api',
165
+ deploy: 'deploy',
166
+ deployInternal: 'deployInternal',
147
167
  exception: 'exception',
148
168
  telemetry: 'telemetry',
149
169
  };
@@ -326,6 +346,24 @@ const TYPES = {
326
346
  locationType: 'api',
327
347
  severity: 'error',
328
348
  },
349
+ /**
350
+ * Non-internal errors deploying files or functions
351
+ */
352
+ deploy: {
353
+ title: 'Error deploying',
354
+ stackType: 'none',
355
+ locationType: 'deploy',
356
+ severity: 'info',
357
+ },
358
+ /**
359
+ * Internal errors deploying files or functions
360
+ */
361
+ deployInternal: {
362
+ title: 'Internal error deploying',
363
+ stackType: 'none',
364
+ locationType: 'deploy',
365
+ severity: 'error',
366
+ },
329
367
  /**
330
368
  * `@netlify/build` threw an uncaught exception
331
369
  */
@@ -1,5 +1,5 @@
1
- export const isSoftFailEvent: any;
2
- export const runsAlsoOnBuildFailure: any;
3
- export const runsOnlyOnBuildFailure: any;
4
- export const runsAfterDeploy: any;
5
- export { DEV_EVENTS, EVENTS } from "@netlify/config";
1
+ export { DEV_EVENTS, EVENTS } from '@netlify/config';
2
+ export declare const isSoftFailEvent: any;
3
+ export declare const runsAlsoOnBuildFailure: any;
4
+ export declare const runsOnlyOnBuildFailure: any;
5
+ export declare const runsAfterDeploy: any;
@@ -0,0 +1,26 @@
1
+ export declare const uploadBlobs: {
2
+ event: string;
3
+ coreStep: ({ debug, logs, deployId, buildDir, quiet, constants: { PUBLISH_DIR, SITE_ID, NETLIFY_API_TOKEN, API_URL }, }: {
4
+ debug: any;
5
+ logs: any;
6
+ deployId: any;
7
+ buildDir: any;
8
+ quiet: any;
9
+ constants: {
10
+ PUBLISH_DIR: any;
11
+ SITE_ID: any;
12
+ NETLIFY_API_TOKEN: any;
13
+ API_URL: any;
14
+ };
15
+ }) => Promise<{}>;
16
+ coreStepId: string;
17
+ coreStepName: string;
18
+ coreStepDescription: () => string;
19
+ condition: ({ deployId, buildDir, constants: { PUBLISH_DIR } }: {
20
+ deployId: any;
21
+ buildDir: any;
22
+ constants: {
23
+ PUBLISH_DIR: any;
24
+ };
25
+ }) => Promise<boolean>;
26
+ };
@@ -0,0 +1,61 @@
1
+ import { version as nodeVersion } from 'node:process';
2
+ import { getDeployStore } from '@netlify/blobs';
3
+ import pMap from 'p-map';
4
+ import semver from 'semver';
5
+ import { log, logError } from '../../log/logger.js';
6
+ import { anyBlobsToUpload, getBlobsDir } from '../../utils/blobs.js';
7
+ import { getKeysToUpload, getFileWithMetadata } from './utils.js';
8
+ const coreStep = async function ({ debug, logs, deployId, buildDir, quiet, constants: { PUBLISH_DIR, SITE_ID, NETLIFY_API_TOKEN, API_URL }, }) {
9
+ const storeOpts = {
10
+ siteID: SITE_ID,
11
+ deployID: deployId,
12
+ token: NETLIFY_API_TOKEN,
13
+ apiURL: API_URL,
14
+ };
15
+ if (semver.lt(nodeVersion, '18.0.0')) {
16
+ const nodeFetch = await import('node-fetch');
17
+ storeOpts.fetch = nodeFetch.default;
18
+ }
19
+ const blobStore = getDeployStore(storeOpts);
20
+ const blobsDir = getBlobsDir({ buildDir, publishDir: PUBLISH_DIR });
21
+ const keys = await getKeysToUpload(blobsDir);
22
+ // We checked earlier, but let's be extra safe
23
+ if (keys.length === 0) {
24
+ if (!quiet) {
25
+ log(logs, 'No blobs to upload to deploy store.');
26
+ }
27
+ return {};
28
+ }
29
+ if (!quiet) {
30
+ log(logs, `Uploading ${keys.length} blobs to deploy store...`);
31
+ }
32
+ const uploadBlob = async (key) => {
33
+ if (debug && !quiet) {
34
+ log(logs, `- Uploading blob ${key}`, { indent: true });
35
+ }
36
+ const { data, metadata } = await getFileWithMetadata(blobsDir, key);
37
+ await blobStore.set(key, data, { metadata });
38
+ };
39
+ try {
40
+ await pMap(keys, uploadBlob, { concurrency: 10 });
41
+ }
42
+ catch (err) {
43
+ logError(logs, `Error uploading blobs to deploy store: ${err.message}`);
44
+ throw new Error(`Failed while uploading blobs to deploy store`);
45
+ }
46
+ if (!quiet) {
47
+ log(logs, `Done uploading blobs to deploy store.`);
48
+ }
49
+ return {};
50
+ };
51
+ const deployAndBlobsPresent = async function ({ deployId, buildDir, constants: { PUBLISH_DIR } }) {
52
+ return deployId && (await anyBlobsToUpload({ buildDir, publishDir: PUBLISH_DIR }));
53
+ };
54
+ export const uploadBlobs = {
55
+ event: 'onPostBuild',
56
+ coreStep,
57
+ coreStepId: 'blobs_upload',
58
+ coreStepName: 'Uploading blobs',
59
+ coreStepDescription: () => 'Uploading blobs to deploy store',
60
+ condition: deployAndBlobsPresent,
61
+ };
@@ -0,0 +1,8 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /** Given output directory, find all file paths to upload excluding metadata files */
3
+ export declare function getKeysToUpload(blobsDir: string): Promise<string[]>;
4
+ /** Read a file and its metadata file from the blobs directory */
5
+ export declare function getFileWithMetadata(blobsDir: string, key: string): Promise<{
6
+ data: Buffer;
7
+ metadata: Record<string, string>;
8
+ }>;
@@ -0,0 +1,46 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fdir } from 'fdir';
4
+ const METADATA_PREFIX = '$';
5
+ const METADATA_SUFFIX = '.json';
6
+ /** Given output directory, find all file paths to upload excluding metadata files */
7
+ export async function getKeysToUpload(blobsDir) {
8
+ const files = await new fdir()
9
+ .withRelativePaths() // we want the relative path from the blobsDir
10
+ .filter((fpath) => !path.basename(fpath).startsWith(METADATA_PREFIX))
11
+ .crawl(blobsDir)
12
+ .withPromise();
13
+ // normalize the path separators to all use the forward slash
14
+ return files.map((f) => f.split(path.sep).join('/'));
15
+ }
16
+ /** Read a file and its metadata file from the blobs directory */
17
+ export async function getFileWithMetadata(blobsDir, key) {
18
+ const contentPath = path.join(blobsDir, key);
19
+ const dirname = path.dirname(key);
20
+ const basename = path.basename(key);
21
+ const metadataPath = path.join(blobsDir, dirname, `${METADATA_PREFIX}${basename}${METADATA_SUFFIX}`);
22
+ const [data, metadata] = await Promise.all([readFile(contentPath), readMetadata(metadataPath)]).catch((err) => {
23
+ throw new Error(`Failed while reading '${key}' and its metadata: ${err.message}`);
24
+ });
25
+ return { data, metadata };
26
+ }
27
+ async function readMetadata(metadataPath) {
28
+ let metadataFile;
29
+ try {
30
+ metadataFile = await readFile(metadataPath, { encoding: 'utf8' });
31
+ }
32
+ catch (err) {
33
+ if (err.code === 'ENOENT') {
34
+ // no metadata file found, that's ok
35
+ return {};
36
+ }
37
+ throw err;
38
+ }
39
+ try {
40
+ return JSON.parse(metadataFile);
41
+ }
42
+ catch {
43
+ // Normalize the error message
44
+ throw new Error(`Error parsing metadata file '${metadataPath}'`);
45
+ }
46
+ }
@@ -1,12 +1,24 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- export function createBuildbotClient(buildbotServerSocket: any): net.Socket;
3
- export const connectBuildbotClient: (...args: any[]) => Promise<any>;
4
- export function closeBuildbotClient(client: any): Promise<void>;
5
- export function deploySiteWithBuildbotClient({ client, events, buildDir, repositoryRoot, constants }: {
2
+ import net from 'net';
3
+ /**
4
+ * Creates the Buildbot IPC client we use to initiate the deploy
5
+ */
6
+ export declare const createBuildbotClient: (buildbotServerSocket: string) => net.Socket;
7
+ /**
8
+ * Emits the connect event
9
+ */
10
+ export declare const connectBuildbotClient: (...args: unknown[]) => Promise<void>;
11
+ /**
12
+ * Closes the buildbot client and its connection
13
+ */
14
+ export declare const closeBuildbotClient: (client: net.Socket) => Promise<void>;
15
+ /**
16
+ * Initates the deploy with the given buildbot client
17
+ */
18
+ export declare const deploySiteWithBuildbotClient: ({ client, events, buildDir, repositoryRoot, constants }: {
6
19
  client: any;
7
20
  events: any;
8
21
  buildDir: any;
9
22
  repositoryRoot: any;
10
23
  constants: any;
11
- }): Promise<undefined>;
12
- import net from 'net';
24
+ }) => Promise<undefined>;
@@ -5,80 +5,111 @@ import { pEvent } from 'p-event';
5
5
  import { addErrorInfo } from '../../error/info.js';
6
6
  import { runsAfterDeploy } from '../../plugins/events.js';
7
7
  import { addAsyncErrorMessage } from '../../utils/errors.js';
8
+ /**
9
+ * Actions supported by the deploy server.
10
+ */
11
+ var Action;
12
+ (function (Action) {
13
+ /**
14
+ * Initiate the deploy.
15
+ */
16
+ Action["DeploySite"] = "deploySite";
17
+ /**
18
+ * Initiate the deploy, wait and hand back control to `@netlify/build` once the site is live.
19
+ */
20
+ Action["DeploySiteAndWait"] = "deploySiteAndAwaitLive";
21
+ })(Action || (Action = {}));
22
+ /**
23
+ * Creates the Buildbot IPC client we use to initiate the deploy
24
+ */
8
25
  export const createBuildbotClient = function (buildbotServerSocket) {
9
26
  const connectionOpts = getConnectionOpts(buildbotServerSocket);
10
27
  const client = net.createConnection(connectionOpts);
11
28
  return client;
12
29
  };
13
- // Windows does not support Unix sockets well, so we also support `host:port`
30
+ /**
31
+ * Windows does not support Unix sockets well, so we also support `host:port`
32
+ */
14
33
  const getConnectionOpts = function (buildbotServerSocket) {
15
34
  if (!buildbotServerSocket.includes(':')) {
16
35
  return { path: buildbotServerSocket };
17
36
  }
18
37
  const [host, port] = buildbotServerSocket.split(':');
19
- return { host, port };
38
+ return { host, port: Number(port) };
20
39
  };
21
- const eConnectBuildbotClient = async function (client) {
40
+ /**
41
+ * Emits the connect event
42
+ */
43
+ export const connectBuildbotClient = addAsyncErrorMessage(async (client) => {
22
44
  await pEvent(client, 'connect');
23
- };
24
- export const connectBuildbotClient = addAsyncErrorMessage(eConnectBuildbotClient, 'Could not connect to buildbot');
45
+ }, 'Could not connect to buildbot');
46
+ /**
47
+ * Closes the buildbot client and its connection
48
+ */
25
49
  export const closeBuildbotClient = async function (client) {
26
50
  if (client.destroyed) {
27
51
  return;
28
52
  }
29
53
  await promisify(client.end.bind(client))();
30
54
  };
31
- const cWritePayload = async function (buildbotClient, payload) {
55
+ const writePayload = addAsyncErrorMessage(async (buildbotClient, payload) => {
32
56
  await promisify(buildbotClient.write.bind(buildbotClient))(JSON.stringify(payload));
33
- };
34
- const writePayload = addAsyncErrorMessage(cWritePayload, 'Could not send payload to buildbot');
35
- const cGetNextParsedResponsePromise = async function (buildbotClient) {
57
+ }, 'Could not send payload to buildbot');
58
+ const getNextParsedResponsePromise = addAsyncErrorMessage(async (buildbotClient) => {
36
59
  const data = await pEvent(buildbotClient, 'data');
37
60
  return JSON.parse(data);
38
- };
39
- const getNextParsedResponsePromise = addAsyncErrorMessage(cGetNextParsedResponsePromise, 'Invalid response from buildbot');
61
+ }, 'Invalid response from buildbot');
62
+ /**
63
+ * Initates the deploy with the given buildbot client
64
+ */
40
65
  export const deploySiteWithBuildbotClient = async function ({ client, events, buildDir, repositoryRoot, constants }) {
41
- const action = shouldWaitForPostProcessing(events) ? 'deploySiteAndAwaitLive' : 'deploySite';
66
+ const action = shouldWaitForPostProcessing(events) ? Action.DeploySiteAndWait : Action.DeploySite;
42
67
  const deployDir = getDeployDir({ buildDir, repositoryRoot, constants });
43
68
  const payload = { action, deployDir };
44
- const [{ succeeded, values: { error, error_type: errorType } = {} }] = await Promise.all([
45
- getNextParsedResponsePromise(client),
46
- writePayload(client, payload),
47
- ]);
48
- if (!succeeded) {
49
- return handleDeployError(error, errorType);
69
+ const [response] = await Promise.all([getNextParsedResponsePromise(client), writePayload(client, payload)]);
70
+ if (!response.succeeded) {
71
+ const { error, code, error_type } = response?.values || {};
72
+ return handleDeployError(error, code, error_type);
50
73
  }
51
74
  };
52
- // The file paths in the buildbot are relative to the repository root.
53
- // However, the file paths in Build plugins, including `constants.PUBLISH_DIR`
54
- // are relative to the build directory, which is different when there is a
55
- // base directory. This converts it.
56
- // We need to call `normalize()` in case the publish directory is the
57
- // repository root, so `deployDir` is "." not ""
75
+ /**
76
+ * The file paths in the buildbot are relative to the repository root.
77
+ * However, the file paths in Build plugins, including `constants.PUBLISH_DIR`
78
+ * are relative to the build directory, which is different when there is a
79
+ * base directory. This converts it.
80
+ * We need to call `normalize()` in case the publish directory is the
81
+ * repository root, so `deployDir` is "." not ""
82
+ */
58
83
  const getDeployDir = function ({ buildDir, repositoryRoot, constants: { PUBLISH_DIR } }) {
59
84
  const absolutePublishDir = resolve(buildDir, PUBLISH_DIR);
60
85
  const relativePublishDir = relative(repositoryRoot, absolutePublishDir);
61
86
  const deployDir = normalize(relativePublishDir);
62
87
  return deployDir;
63
88
  };
64
- // We distinguish between user errors and system errors during deploys
65
- const handleDeployError = function (error, errorType) {
66
- const errorIs422 = error !== undefined && error.code === '422';
67
- const errMsg = errorIs422
68
- ? `
69
- File upload failed because of mismatched SHAs.
70
- This can happen when files are changed after the deployment process has started but before they are uploaded.
71
-
72
- Error: ${error}
73
- `
74
- : `Deploy did not succeed: ${error}`;
75
- const errorA = new Error(errMsg);
89
+ /**
90
+ * We distinguish between user errors and system errors during deploys
91
+ */
92
+ const handleDeployError = function (error, errorCode, errorType) {
93
+ if (errorCode !== undefined) {
94
+ const err = new Error(`Deploy did not succeed with HTTP Error ${errorCode}: ${error}`);
95
+ if (errorCode.startsWith('5')) {
96
+ const errorInfo = { type: 'deployInternal', location: { statusCode: errorCode } };
97
+ addErrorInfo(err, errorInfo);
98
+ throw err;
99
+ }
100
+ const errorInfo = { type: 'deploy', location: { statusCode: errorCode } };
101
+ addErrorInfo(err, errorInfo);
102
+ throw err;
103
+ }
104
+ const err = new Error(`Deploy did not succeed: ${error}`);
76
105
  const errorInfo = errorType === 'user' ? { type: 'resolveConfig' } : { type: 'coreStep', location: { coreStepName: 'Deploy site' } };
77
- addErrorInfo(errorA, errorInfo);
78
- throw errorA;
106
+ addErrorInfo(err, errorInfo);
107
+ throw err;
79
108
  };
80
- // We only wait for post-processing (last stage before site deploy) if the build
81
- // has some plugins that do post-deploy logic
109
+ /**
110
+ * We only wait for post-processing (last stage before site deploy) if the build
111
+ * has some plugins that do post-deploy logic
112
+ */
82
113
  const shouldWaitForPostProcessing = function (events) {
83
114
  return events.some(hasPostDeployLogic);
84
115
  };
@@ -0,0 +1,18 @@
1
+ export declare const preCleanup: {
2
+ event: string;
3
+ coreStep: ({ buildDir, constants: { PUBLISH_DIR } }: {
4
+ buildDir: any;
5
+ constants: {
6
+ PUBLISH_DIR: any;
7
+ };
8
+ }) => Promise<{}>;
9
+ coreStepId: string;
10
+ coreStepName: string;
11
+ coreStepDescription: () => string;
12
+ condition: ({ buildDir, constants: { PUBLISH_DIR } }: {
13
+ buildDir: any;
14
+ constants: {
15
+ PUBLISH_DIR: any;
16
+ };
17
+ }) => Promise<boolean>;
18
+ };
@@ -0,0 +1,23 @@
1
+ import { rm } from 'node:fs/promises';
2
+ import { anyBlobsToUpload, getBlobsDir } from '../../utils/blobs.js';
3
+ const coreStep = async function ({ buildDir, constants: { PUBLISH_DIR } }) {
4
+ const blobsDir = getBlobsDir({ buildDir, publishDir: PUBLISH_DIR });
5
+ try {
6
+ await rm(blobsDir, { recursive: true, force: true });
7
+ }
8
+ catch {
9
+ // Ignore errors if it fails, we can continue anyway.
10
+ }
11
+ return {};
12
+ };
13
+ const blobsPresent = async function ({ buildDir, constants: { PUBLISH_DIR } }) {
14
+ return await anyBlobsToUpload({ buildDir, publishDir: PUBLISH_DIR });
15
+ };
16
+ export const preCleanup = {
17
+ event: 'onPreBuild',
18
+ coreStep,
19
+ coreStepId: 'pre_cleanup',
20
+ coreStepName: 'Pre cleanup',
21
+ coreStepDescription: () => 'Cleaning up leftover files from previous builds',
22
+ condition: blobsPresent,
23
+ };
@@ -1,4 +1,4 @@
1
- export declare const fireCoreStep: ({ coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, packagePath, constants, buildbotServerSocket, events, logs, nodePath, childEnv, context, branch, envChanges, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, debug, systemLog, saveConfig, userNodeVersion, explicitSecretKeys, edgeFunctionsBootstrapURL, }: {
1
+ export declare const fireCoreStep: ({ coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, packagePath, constants, buildbotServerSocket, events, logs, quiet, nodePath, childEnv, context, branch, envChanges, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, debug, systemLog, saveConfig, userNodeVersion, explicitSecretKeys, edgeFunctionsBootstrapURL, deployId, }: {
2
2
  coreStep: any;
3
3
  coreStepId: any;
4
4
  coreStepName: any;
@@ -11,6 +11,7 @@ export declare const fireCoreStep: ({ coreStep, coreStepId, coreStepName, config
11
11
  buildbotServerSocket: any;
12
12
  events: any;
13
13
  logs: any;
14
+ quiet: any;
14
15
  nodePath: any;
15
16
  childEnv: any;
16
17
  context: any;
@@ -29,6 +30,7 @@ export declare const fireCoreStep: ({ coreStep, coreStepId, coreStepName, config
29
30
  userNodeVersion: any;
30
31
  explicitSecretKeys: any;
31
32
  edgeFunctionsBootstrapURL: any;
33
+ deployId: any;
32
34
  }) => Promise<{
33
35
  newEnvChanges: any;
34
36
  netlifyConfig: any;
@@ -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, packagePath, constants, buildbotServerSocket, events, logs, nodePath, childEnv, context, branch, envChanges, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, debug, systemLog, saveConfig, userNodeVersion, explicitSecretKeys, edgeFunctionsBootstrapURL, }) {
5
+ export const fireCoreStep = async function ({ coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, packagePath, constants, buildbotServerSocket, events, logs, quiet, nodePath, childEnv, context, branch, envChanges, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, debug, systemLog, saveConfig, userNodeVersion, explicitSecretKeys, edgeFunctionsBootstrapURL, deployId, }) {
6
6
  try {
7
7
  const configSideFiles = await listConfigSideFiles([headersPath, redirectsPath]);
8
8
  const childEnvA = setEnvChanges(envChanges, { ...childEnv });
@@ -16,6 +16,7 @@ export const fireCoreStep = async function ({ coreStep, coreStepId, coreStepName
16
16
  buildbotServerSocket,
17
17
  events,
18
18
  logs,
19
+ quiet,
19
20
  context,
20
21
  branch,
21
22
  childEnv: childEnvA,
@@ -31,6 +32,7 @@ export const fireCoreStep = async function ({ coreStep, coreStepId, coreStepName
31
32
  userNodeVersion,
32
33
  explicitSecretKeys,
33
34
  edgeFunctionsBootstrapURL,
35
+ deployId,
34
36
  });
35
37
  const { netlifyConfig: netlifyConfigA, configMutations: configMutationsA, headersPath: headersPathA, redirectsPath: redirectsPathA, } = await updateNetlifyConfig({
36
38
  configOpts,
package/lib/steps/get.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { getUtils } from '../plugins/child/utils.js';
2
2
  import { DEV_EVENTS, EVENTS } from '../plugins/events.js';
3
+ import { uploadBlobs } from '../plugins_core/blobs_upload/index.js';
3
4
  import { buildCommandCore } from '../plugins_core/build_command.js';
4
5
  import { deploySite } from '../plugins_core/deploy/index.js';
5
6
  import { bundleEdgeFunctions } from '../plugins_core/edge_functions/index.js';
6
7
  import { bundleFunctions } from '../plugins_core/functions/index.js';
8
+ import { preCleanup } from '../plugins_core/pre_cleanup/index.js';
7
9
  import { saveArtifacts } from '../plugins_core/save_artifacts/index.js';
8
10
  import { scanForSecrets } from '../plugins_core/secrets_scanning/index.js';
9
11
  // Get all build steps
@@ -54,7 +56,17 @@ const getEventSteps = function (eventHandlers) {
54
56
  });
55
57
  };
56
58
  const addCoreSteps = function (steps) {
57
- return [buildCommandCore, ...steps, bundleFunctions, bundleEdgeFunctions, scanForSecrets, deploySite, saveArtifacts];
59
+ return [
60
+ preCleanup,
61
+ buildCommandCore,
62
+ ...steps,
63
+ bundleFunctions,
64
+ bundleEdgeFunctions,
65
+ scanForSecrets,
66
+ uploadBlobs,
67
+ deploySite,
68
+ saveArtifacts,
69
+ ];
58
70
  };
59
71
  // Sort plugin steps by event order.
60
72
  const sortSteps = function (steps, events) {
@@ -39,7 +39,7 @@ const getBuildSteps = function (buildSteps) {
39
39
  const allSteps = getSteps([]).steps.filter(({ coreStepId }) => buildSteps.includes(coreStepId));
40
40
  return allSteps;
41
41
  };
42
- const executeBuildStep = async function ({ config, packagePath, defaultConfig, cachedConfig, debug, nodePath, functionsDistDir, edgeFunctionsDistDir, errorMonitor, mode, logs, errorParams, featureFlags, buildSteps, repositoryRoot, systemLog, edgeFunctionsBootstrapURL, }) {
42
+ const executeBuildStep = async function ({ config, packagePath, defaultConfig, cachedConfig, debug, nodePath, functionsDistDir, edgeFunctionsDistDir, errorMonitor, mode, logs, errorParams, featureFlags, buildSteps, repositoryRoot, systemLog, edgeFunctionsBootstrapURL, deployId, token, quiet, }) {
43
43
  const configOpts = getConfigOpts({
44
44
  config,
45
45
  defaultConfig,
@@ -53,6 +53,7 @@ const executeBuildStep = async function ({ config, packagePath, defaultConfig, c
53
53
  cachedConfig,
54
54
  debug,
55
55
  logs,
56
+ quiet,
56
57
  nodePath,
57
58
  timers: [],
58
59
  });
@@ -64,6 +65,7 @@ const executeBuildStep = async function ({ config, packagePath, defaultConfig, c
64
65
  netlifyConfig,
65
66
  siteInfo,
66
67
  mode,
68
+ token,
67
69
  });
68
70
  Object.assign(errorParams, { netlifyConfig, siteInfo, childEnv, userNodeVersion });
69
71
  try {
@@ -80,6 +82,8 @@ const executeBuildStep = async function ({ config, packagePath, defaultConfig, c
80
82
  repositoryRoot: repositoryRootA,
81
83
  systemLog,
82
84
  edgeFunctionsBootstrapURL,
85
+ deployId,
86
+ quiet,
83
87
  });
84
88
  return {
85
89
  netlifyConfig: netlifyConfigA,
@@ -101,7 +105,7 @@ const executeBuildStep = async function ({ config, packagePath, defaultConfig, c
101
105
  throw error;
102
106
  }
103
107
  };
104
- const runBuildStep = async function ({ netlifyConfig, buildDir, nodePath, constants, logs, debug, featureFlags, childEnv, buildSteps, repositoryRoot, systemLog, edgeFunctionsBootstrapURL, }) {
108
+ const runBuildStep = async function ({ netlifyConfig, buildDir, nodePath, constants, logs, debug, featureFlags, childEnv, buildSteps, repositoryRoot, systemLog, edgeFunctionsBootstrapURL, deployId, quiet, }) {
105
109
  const { netlifyConfig: netlifyConfigA, configMutations } = await runSteps({
106
110
  steps: getBuildSteps(buildSteps),
107
111
  buildDir,
@@ -116,6 +120,8 @@ const runBuildStep = async function ({ netlifyConfig, buildDir, nodePath, consta
116
120
  repositoryRoot,
117
121
  systemLog,
118
122
  edgeFunctionsBootstrapURL,
123
+ deployId,
124
+ quiet,
119
125
  });
120
126
  return { netlifyConfig: netlifyConfigA, configMutations };
121
127
  };
@@ -40,6 +40,7 @@ export const runStep = async function ({ event, childProcess, packageName, coreS
40
40
  buildDir,
41
41
  saveConfig,
42
42
  explicitSecretKeys,
43
+ deployId,
43
44
  });
44
45
  span.setAttribute('build.execution.step.should_run', shouldRun);
45
46
  if (!shouldRun) {
@@ -77,6 +78,7 @@ export const runStep = async function ({ event, childProcess, packageName, coreS
77
78
  error,
78
79
  logs,
79
80
  debug,
81
+ quiet,
80
82
  systemLog,
81
83
  verbose,
82
84
  saveConfig,
@@ -91,6 +93,7 @@ export const runStep = async function ({ event, childProcess, packageName, coreS
91
93
  userNodeVersion,
92
94
  explicitSecretKeys,
93
95
  edgeFunctionsBootstrapURL,
96
+ deployId,
94
97
  });
95
98
  const newValues = await getStepReturn({
96
99
  event,
@@ -153,7 +156,7 @@ export const runStep = async function ({ event, childProcess, packageName, coreS
153
156
  // or available. However, one might be created by a build plugin, in which case,
154
157
  // those core plugins should be triggered. We use a dynamic `condition()` to
155
158
  // model this behavior.
156
- const shouldRunStep = async function ({ event, packageName, error, packagePath, failedPlugins, netlifyConfig, condition, constants, buildbotServerSocket, buildDir, saveConfig, explicitSecretKeys, }) {
159
+ const shouldRunStep = async function ({ event, packageName, error, packagePath, failedPlugins, netlifyConfig, condition, constants, buildbotServerSocket, buildDir, saveConfig, explicitSecretKeys, deployId, }) {
157
160
  if (failedPlugins.includes(packageName) ||
158
161
  (condition !== undefined &&
159
162
  !(await condition({
@@ -164,6 +167,7 @@ const shouldRunStep = async function ({ event, packageName, error, packagePath,
164
167
  netlifyConfig,
165
168
  saveConfig,
166
169
  explicitSecretKeys,
170
+ deployId,
167
171
  })))) {
168
172
  return false;
169
173
  }
@@ -180,7 +184,7 @@ const getFireStep = function (packageName, coreStepId, event) {
180
184
  const parentTag = normalizeTagName(packageName);
181
185
  return measureDuration(tFireStep, event, { parentTag, category: 'pluginEvent' });
182
186
  };
183
- const tFireStep = function ({ event, childProcess, packageName, pluginPackageJson, loadedFrom, origin, coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, packagePath, nodePath, childEnv, context, branch, envChanges, constants, steps, buildbotServerSocket, events, error, logs, debug, systemLog, verbose, saveConfig, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, userNodeVersion, explicitSecretKeys, edgeFunctionsBootstrapURL, }) {
187
+ const tFireStep = function ({ event, childProcess, packageName, pluginPackageJson, loadedFrom, origin, coreStep, coreStepId, coreStepName, configPath, outputConfigPath, buildDir, repositoryRoot, packagePath, nodePath, childEnv, context, branch, envChanges, constants, steps, buildbotServerSocket, events, error, logs, debug, quiet, systemLog, verbose, saveConfig, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, featureFlags, userNodeVersion, explicitSecretKeys, edgeFunctionsBootstrapURL, deployId, }) {
184
188
  if (coreStep !== undefined) {
185
189
  return fireCoreStep({
186
190
  coreStep,
@@ -195,6 +199,7 @@ const tFireStep = function ({ event, childProcess, packageName, pluginPackageJso
195
199
  buildbotServerSocket,
196
200
  events,
197
201
  logs,
202
+ quiet,
198
203
  nodePath,
199
204
  childEnv,
200
205
  context,
@@ -213,6 +218,7 @@ const tFireStep = function ({ event, childProcess, packageName, pluginPackageJso
213
218
  userNodeVersion,
214
219
  explicitSecretKeys,
215
220
  edgeFunctionsBootstrapURL,
221
+ deployId,
216
222
  });
217
223
  }
218
224
  return firePluginStep({
@@ -0,0 +1,8 @@
1
+ export declare const getBlobsDir: ({ buildDir, publishDir }: {
2
+ buildDir: any;
3
+ publishDir: any;
4
+ }) => string;
5
+ export declare const anyBlobsToUpload: ({ buildDir, publishDir }: {
6
+ buildDir: any;
7
+ publishDir: any;
8
+ }) => Promise<boolean>;
@@ -0,0 +1,11 @@
1
+ import path from 'node:path';
2
+ import { fdir } from 'fdir';
3
+ const BLOBS_PATH = '.netlify/blobs/deploy';
4
+ export const getBlobsDir = function ({ buildDir, publishDir }) {
5
+ return path.resolve(buildDir, publishDir, BLOBS_PATH);
6
+ };
7
+ export const anyBlobsToUpload = async function ({ buildDir, publishDir }) {
8
+ const blobsDir = getBlobsDir({ buildDir, publishDir });
9
+ const { files } = await new fdir().onlyCounts().crawl(blobsDir).withPromise();
10
+ return files > 0;
11
+ };
@@ -1 +1,7 @@
1
- export function addAsyncErrorMessage(asyncFunc: any, message: any): (...args: any[]) => Promise<any>;
1
+ type asyncFunction<T> = (...args: unknown[]) => Promise<T>;
2
+ /**
3
+ * Wrap an async function so it prepends an error message on exceptions.
4
+ * This helps locate errors.
5
+ */
6
+ export declare const addAsyncErrorMessage: <T>(asyncFunc: asyncFunction<T>, message: string) => asyncFunction<T>;
7
+ export {};
@@ -1,5 +1,7 @@
1
- // Wrap an async function so it prepends an error message on exceptions.
2
- // This helps locate errors.
1
+ /**
2
+ * Wrap an async function so it prepends an error message on exceptions.
3
+ * This helps locate errors.
4
+ */
3
5
  export const addAsyncErrorMessage = function (asyncFunc, message) {
4
6
  return async (...args) => {
5
7
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/build",
3
- "version": "29.28.2",
3
+ "version": "29.29.1",
4
4
  "description": "Netlify build module",
5
5
  "type": "module",
6
6
  "exports": "./lib/index.js",
@@ -66,6 +66,7 @@
66
66
  "dependencies": {
67
67
  "@bugsnag/js": "^7.0.0",
68
68
  "@honeycombio/opentelemetry-node": "^0.5.0",
69
+ "@netlify/blobs": "^6.3.1",
69
70
  "@netlify/cache-utils": "^5.1.5",
70
71
  "@netlify/config": "^20.10.0",
71
72
  "@netlify/edge-bundler": "10.1.3",
@@ -95,11 +96,13 @@
95
96
  "log-process-errors": "^8.0.0",
96
97
  "map-obj": "^5.0.0",
97
98
  "memoize-one": "^6.0.0",
99
+ "node-fetch": "^3.3.2",
98
100
  "os-name": "^5.0.0",
99
101
  "p-event": "^5.0.0",
100
102
  "p-every": "^2.0.0",
101
103
  "p-filter": "^3.0.0",
102
104
  "p-locate": "^6.0.0",
105
+ "p-map": "^6.0.0",
103
106
  "p-reduce": "^3.0.0",
104
107
  "path-exists": "^5.0.0",
105
108
  "path-type": "^5.0.0",
@@ -154,5 +157,5 @@
154
157
  "engines": {
155
158
  "node": "^14.16.0 || >=16.0.0"
156
159
  },
157
- "gitHead": "db2b375439203dc1c7ec1e68b24b8460c8f0ffc3"
160
+ "gitHead": "4541a6ca7ab30fb073e9f01c52aa0b52f8c63ae7"
158
161
  }