@netlify/build 29.28.1 → 29.29.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/types.d.ts +2 -0
- package/lib/error/parse/location.js +4 -0
- package/lib/error/parse/serialize_status.d.ts +10 -13
- package/lib/error/parse/serialize_status.js +1 -1
- package/lib/error/types.d.ts +9 -5
- package/lib/error/types.js +41 -1
- package/lib/plugins/events.d.ts +5 -5
- package/lib/plugins/spawn.js +3 -1
- package/lib/plugins_core/blobs_upload/index.d.ts +26 -0
- package/lib/plugins_core/blobs_upload/index.js +61 -0
- package/lib/plugins_core/blobs_upload/utils.d.ts +8 -0
- package/lib/plugins_core/blobs_upload/utils.js +46 -0
- package/lib/plugins_core/deploy/buildbot_client.d.ts +18 -6
- package/lib/plugins_core/deploy/buildbot_client.js +71 -40
- package/lib/plugins_core/pre_cleanup/index.d.ts +18 -0
- package/lib/plugins_core/pre_cleanup/index.js +23 -0
- package/lib/steps/core_step.d.ts +3 -1
- package/lib/steps/core_step.js +3 -1
- package/lib/steps/error.d.ts +24 -16
- package/lib/steps/error.js +32 -16
- package/lib/steps/get.js +13 -1
- package/lib/steps/plugin.d.ts +1 -1
- package/lib/steps/plugin.js +3 -3
- package/lib/steps/return.d.ts +7 -7
- package/lib/steps/run_core_steps.js +8 -2
- package/lib/steps/run_step.js +8 -2
- package/lib/utils/blobs.d.ts +8 -0
- package/lib/utils/blobs.js +11 -0
- package/lib/utils/errors.d.ts +7 -1
- package/lib/utils/errors.js +4 -2
- package/package.json +5 -2
package/lib/core/types.d.ts
CHANGED
|
@@ -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
|
};
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}): {
|
|
11
|
-
state: any;
|
|
12
|
-
title: any;
|
|
13
|
-
summary: any;
|
|
1
|
+
import type { BuildError } from '../types.js';
|
|
2
|
+
type ErrorState = 'failed_build' | 'failed_plugin' | 'canceled_build';
|
|
3
|
+
export declare const serializeErrorStatus: ({ fullErrorInfo: { title, message, locationInfo, errorProps, errorMetadata }, state, }: {
|
|
4
|
+
fullErrorInfo: BuildError;
|
|
5
|
+
state: ErrorState;
|
|
6
|
+
}) => {
|
|
7
|
+
state: ErrorState;
|
|
8
|
+
title: string | (import("../types.js").TitleFunction & string);
|
|
9
|
+
summary: string;
|
|
14
10
|
text: string | undefined;
|
|
15
11
|
extraData: any;
|
|
16
12
|
};
|
|
13
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/* Serialize an error object to `statuses` properties */
|
|
2
2
|
export const serializeErrorStatus = function ({ fullErrorInfo: { title, message, locationInfo, errorProps, errorMetadata }, state, }) {
|
|
3
3
|
const text = getText({ locationInfo, errorProps });
|
|
4
4
|
return { state, title, summary: message, text, extraData: errorMetadata };
|
package/lib/error/types.d.ts
CHANGED
|
@@ -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.
|
|
@@ -99,7 +96,11 @@ export type APILocation = {
|
|
|
99
96
|
parameters?: any;
|
|
100
97
|
};
|
|
101
98
|
export declare const isAPILocation: (location?: ErrorLocation) => location is APILocation;
|
|
102
|
-
export type
|
|
99
|
+
export type DeployLocation = {
|
|
100
|
+
statusCode: string;
|
|
101
|
+
};
|
|
102
|
+
export declare const isDeployLocation: (location?: ErrorLocation) => location is DeployLocation;
|
|
103
|
+
export type ErrorLocation = BuildCommandLocation | FunctionsBundlingLocation | CoreStepLocation | PluginLocation | APILocation | DeployLocation;
|
|
103
104
|
/**
|
|
104
105
|
* Given a BuildError, extract the relevant trace attributes to add to the on-going Span
|
|
105
106
|
*/
|
|
@@ -165,10 +166,13 @@ declare const ErrorTypeMap: {
|
|
|
165
166
|
readonly pluginInternal: "pluginInternal";
|
|
166
167
|
readonly ipc: "ipc";
|
|
167
168
|
readonly corePlugin: "corePlugin";
|
|
169
|
+
readonly trustedPlugin: "trustedPlugin";
|
|
168
170
|
readonly coreStep: "coreStep";
|
|
169
171
|
readonly api: "api";
|
|
172
|
+
readonly deploy: "deploy";
|
|
173
|
+
readonly deployInternal: "deployInternal";
|
|
170
174
|
readonly exception: "exception";
|
|
171
175
|
readonly telemetry: "telemetry";
|
|
172
176
|
};
|
|
173
|
-
type ErrorTypes = keyof typeof ErrorTypeMap;
|
|
177
|
+
export type ErrorTypes = keyof typeof ErrorTypeMap;
|
|
174
178
|
export {};
|
package/lib/error/types.js
CHANGED
|
@@ -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,6 +98,11 @@ 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
|
};
|
|
100
108
|
/**
|
|
@@ -141,8 +149,11 @@ const ErrorTypeMap = {
|
|
|
141
149
|
pluginInternal: 'pluginInternal',
|
|
142
150
|
ipc: 'ipc',
|
|
143
151
|
corePlugin: 'corePlugin',
|
|
152
|
+
trustedPlugin: 'trustedPlugin',
|
|
144
153
|
coreStep: 'coreStep',
|
|
145
154
|
api: 'api',
|
|
155
|
+
deploy: 'deploy',
|
|
156
|
+
deployInternal: 'deployInternal',
|
|
146
157
|
exception: 'exception',
|
|
147
158
|
telemetry: 'telemetry',
|
|
148
159
|
};
|
|
@@ -293,6 +304,17 @@ const TYPES = {
|
|
|
293
304
|
locationType: 'buildFail',
|
|
294
305
|
severity: 'error',
|
|
295
306
|
},
|
|
307
|
+
/**
|
|
308
|
+
* Trusted plugin internal error (all of our `@netlify/*` plugins).
|
|
309
|
+
*/
|
|
310
|
+
trustedPlugin: {
|
|
311
|
+
title: ({ location: { packageName } }) => `Plugin "${packageName}" internal error`,
|
|
312
|
+
stackType: 'stack',
|
|
313
|
+
showErrorProps: true,
|
|
314
|
+
rawStack: true,
|
|
315
|
+
locationType: 'buildFail',
|
|
316
|
+
severity: 'error',
|
|
317
|
+
},
|
|
296
318
|
/**
|
|
297
319
|
* Core step internal error
|
|
298
320
|
*/
|
|
@@ -314,6 +336,24 @@ const TYPES = {
|
|
|
314
336
|
locationType: 'api',
|
|
315
337
|
severity: 'error',
|
|
316
338
|
},
|
|
339
|
+
/**
|
|
340
|
+
* Non-internal errors deploying files or functions
|
|
341
|
+
*/
|
|
342
|
+
deploy: {
|
|
343
|
+
title: 'Error deploying',
|
|
344
|
+
stackType: 'none',
|
|
345
|
+
locationType: 'deploy',
|
|
346
|
+
severity: 'info',
|
|
347
|
+
},
|
|
348
|
+
/**
|
|
349
|
+
* Internal errors deploying files or functions
|
|
350
|
+
*/
|
|
351
|
+
deployInternal: {
|
|
352
|
+
title: 'Internal error deploying',
|
|
353
|
+
stackType: 'none',
|
|
354
|
+
locationType: 'deploy',
|
|
355
|
+
severity: 'error',
|
|
356
|
+
},
|
|
317
357
|
/**
|
|
318
358
|
* `@netlify/build` threw an uncaught exception
|
|
319
359
|
*/
|
package/lib/plugins/events.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export const
|
|
3
|
-
export const
|
|
4
|
-
export const
|
|
5
|
-
export
|
|
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;
|
package/lib/plugins/spawn.js
CHANGED
|
@@ -36,7 +36,9 @@ const startPlugin = async function ({ pluginDir, nodePath, buildDir, childEnv, s
|
|
|
36
36
|
execPath: nodePath,
|
|
37
37
|
env: childEnv,
|
|
38
38
|
extendEnv: false,
|
|
39
|
-
stdio: isTrustedPlugin(pluginPackageJson) && systemLogFile
|
|
39
|
+
stdio: isTrustedPlugin(pluginPackageJson?.name) && systemLogFile
|
|
40
|
+
? ['pipe', 'pipe', 'pipe', 'ipc', systemLogFile]
|
|
41
|
+
: undefined,
|
|
40
42
|
});
|
|
41
43
|
try {
|
|
42
44
|
await getEventFromChild(childProcess, 'ready');
|
|
@@ -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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
})
|
|
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
|
-
|
|
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
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Emits the connect event
|
|
42
|
+
*/
|
|
43
|
+
export const connectBuildbotClient = addAsyncErrorMessage(async (client) => {
|
|
22
44
|
await pEvent(client, 'connect');
|
|
23
|
-
};
|
|
24
|
-
|
|
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
|
|
55
|
+
const writePayload = addAsyncErrorMessage(async (buildbotClient, payload) => {
|
|
32
56
|
await promisify(buildbotClient.write.bind(buildbotClient))(JSON.stringify(payload));
|
|
33
|
-
};
|
|
34
|
-
const
|
|
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
|
-
|
|
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) ?
|
|
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 [
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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(
|
|
78
|
-
throw
|
|
106
|
+
addErrorInfo(err, errorInfo);
|
|
107
|
+
throw err;
|
|
79
108
|
};
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
};
|
package/lib/steps/core_step.d.ts
CHANGED
|
@@ -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;
|
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, 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/error.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { ErrorTypes } from '../error/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Handle build command errors and plugin errors:
|
|
4
|
+
* - usually, propagate the error to make the build stop.
|
|
5
|
+
* - `utils.build.cancelBuild()` also cancels the build by calling the API
|
|
6
|
+
* - `utils.build.failPlugin()` or post-deploy errors do not make the build
|
|
7
|
+
* stop, but are still reported, and prevent future events from the same
|
|
8
|
+
* plugin.
|
|
9
|
+
* This also computes error statuses that are sent to the API.
|
|
10
|
+
*/
|
|
11
|
+
export declare const handleStepError: ({ event, newError, childEnv, mode, api, errorMonitor, deployId, coreStep, netlifyConfig, logs, debug, testOpts, }: {
|
|
2
12
|
event: any;
|
|
3
13
|
newError: any;
|
|
4
14
|
childEnv: any;
|
|
@@ -11,38 +21,36 @@ export function handleStepError({ event, newError, childEnv, mode, api, errorMon
|
|
|
11
21
|
logs: any;
|
|
12
22
|
debug: any;
|
|
13
23
|
testOpts: any;
|
|
14
|
-
})
|
|
15
|
-
failedPlugin:
|
|
24
|
+
}) => Promise<{
|
|
25
|
+
failedPlugin: string[];
|
|
16
26
|
newStatus: {
|
|
17
|
-
state:
|
|
18
|
-
title:
|
|
19
|
-
summary:
|
|
27
|
+
state: "failed_build" | "failed_plugin" | "canceled_build";
|
|
28
|
+
title: string | (import("../error/types.js").TitleFunction & string);
|
|
29
|
+
summary: string;
|
|
20
30
|
text: string | undefined;
|
|
21
31
|
extraData: any;
|
|
22
32
|
};
|
|
23
33
|
}> | Promise<{
|
|
24
34
|
newError: any;
|
|
25
35
|
newStatus: {
|
|
26
|
-
state:
|
|
27
|
-
title:
|
|
28
|
-
summary:
|
|
36
|
+
state: "failed_build" | "failed_plugin" | "canceled_build";
|
|
37
|
+
title: string | (import("../error/types.js").TitleFunction & string);
|
|
38
|
+
summary: string;
|
|
29
39
|
text: string | undefined;
|
|
30
40
|
extraData: any;
|
|
31
41
|
};
|
|
32
42
|
}> | {
|
|
33
43
|
newError: any;
|
|
34
44
|
newStatus: {
|
|
35
|
-
state:
|
|
36
|
-
title:
|
|
37
|
-
summary:
|
|
45
|
+
state: "failed_build" | "failed_plugin" | "canceled_build";
|
|
46
|
+
title: string | (import("../error/types.js").TitleFunction & string);
|
|
47
|
+
summary: string;
|
|
38
48
|
text: string | undefined;
|
|
39
49
|
extraData: any;
|
|
40
50
|
};
|
|
41
51
|
} | {
|
|
42
52
|
newError: any;
|
|
43
53
|
};
|
|
44
|
-
export
|
|
45
|
-
type?:
|
|
46
|
-
} | {
|
|
47
|
-
type: string;
|
|
54
|
+
export declare const getPluginErrorType: (error: Error, loadedFrom: string, packageName?: string) => {
|
|
55
|
+
type?: ErrorTypes;
|
|
48
56
|
};
|
package/lib/steps/error.js
CHANGED
|
@@ -2,15 +2,19 @@ import { cancelBuild } from '../error/cancel.js';
|
|
|
2
2
|
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
|
+
import { isPluginLocation } from '../error/types.js';
|
|
5
6
|
import { isSoftFailEvent } from '../plugins/events.js';
|
|
6
7
|
import { addErrorToActiveSpan, addEventToActiveSpan } from '../tracing/main.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
import { isTrustedPlugin } from './plugin.js';
|
|
9
|
+
/**
|
|
10
|
+
* Handle build command errors and plugin errors:
|
|
11
|
+
* - usually, propagate the error to make the build stop.
|
|
12
|
+
* - `utils.build.cancelBuild()` also cancels the build by calling the API
|
|
13
|
+
* - `utils.build.failPlugin()` or post-deploy errors do not make the build
|
|
14
|
+
* stop, but are still reported, and prevent future events from the same
|
|
15
|
+
* plugin.
|
|
16
|
+
* This also computes error statuses that are sent to the API.
|
|
17
|
+
*/
|
|
14
18
|
export const handleStepError = function ({ event, newError, childEnv, mode, api, errorMonitor, deployId, coreStep, netlifyConfig, logs, debug, testOpts, }) {
|
|
15
19
|
addErrorToActiveSpan(newError);
|
|
16
20
|
// Core steps do not report error statuses
|
|
@@ -18,7 +22,7 @@ export const handleStepError = function ({ event, newError, childEnv, mode, api,
|
|
|
18
22
|
return { newError };
|
|
19
23
|
}
|
|
20
24
|
const fullErrorInfo = getFullErrorInfo({ error: newError, colors: false, debug });
|
|
21
|
-
const { errorInfo
|
|
25
|
+
const { errorInfo, message, title, type } = fullErrorInfo;
|
|
22
26
|
if (type === 'failPlugin' || isSoftFailEvent(event)) {
|
|
23
27
|
return handleFailPlugin({
|
|
24
28
|
fullErrorInfo,
|
|
@@ -36,32 +40,40 @@ export const handleStepError = function ({ event, newError, childEnv, mode, api,
|
|
|
36
40
|
const cancellationAttributes = {
|
|
37
41
|
'build.cancellation.title': title,
|
|
38
42
|
'build.cancellation.message': message,
|
|
39
|
-
'build.cancellation.packageName': packageName,
|
|
40
43
|
};
|
|
44
|
+
if (isPluginLocation(errorInfo.location)) {
|
|
45
|
+
cancellationAttributes['build.cancellation.packageName'] = errorInfo.location.packageName;
|
|
46
|
+
}
|
|
41
47
|
addEventToActiveSpan('build.cancelled', cancellationAttributes);
|
|
42
48
|
return handleCancelBuild({ fullErrorInfo, newError, api, deployId });
|
|
43
49
|
}
|
|
44
50
|
return handleFailBuild({ fullErrorInfo, newError });
|
|
45
51
|
};
|
|
46
|
-
|
|
47
|
-
const handleFailPlugin = async function ({ fullErrorInfo,
|
|
52
|
+
/* On `utils.build.failPlugin()` or during `onSuccess` or `onEnd` */
|
|
53
|
+
const handleFailPlugin = async function ({ fullErrorInfo, newError, childEnv, mode, errorMonitor, netlifyConfig, logs, debug, testOpts, }) {
|
|
48
54
|
const newStatus = serializeErrorStatus({ fullErrorInfo, state: 'failed_plugin' });
|
|
49
55
|
await handleBuildError(newError, { errorMonitor, netlifyConfig, childEnv, mode, logs, debug, testOpts });
|
|
50
|
-
|
|
56
|
+
// TODO we should probably use type guard here, but due to the way we build these errorInfo objects I'm not 100%
|
|
57
|
+
// confident we have all the properties currently required by the type
|
|
58
|
+
const location = fullErrorInfo.errorInfo.location;
|
|
59
|
+
return { failedPlugin: [location.packageName], newStatus };
|
|
51
60
|
};
|
|
52
|
-
|
|
61
|
+
/* On `utils.build.cancelBuild()` */
|
|
53
62
|
const handleCancelBuild = async function ({ fullErrorInfo, newError, api, deployId }) {
|
|
54
63
|
const newStatus = serializeErrorStatus({ fullErrorInfo, state: 'canceled_build' });
|
|
55
64
|
await cancelBuild({ api, deployId });
|
|
56
65
|
return { newError, newStatus };
|
|
57
66
|
};
|
|
58
|
-
|
|
67
|
+
/* On `utils.build.failBuild()` or uncaught exception */
|
|
59
68
|
const handleFailBuild = function ({ fullErrorInfo, newError }) {
|
|
60
69
|
const newStatus = serializeErrorStatus({ fullErrorInfo, state: 'failed_build' });
|
|
61
70
|
return { newError, newStatus };
|
|
62
71
|
};
|
|
63
|
-
|
|
64
|
-
export const getPluginErrorType = function (error, loadedFrom) {
|
|
72
|
+
/* Unlike community plugins, core plugin and trusted plugin bugs should be handled as system errors */
|
|
73
|
+
export const getPluginErrorType = function (error, loadedFrom, packageName) {
|
|
74
|
+
if (isTrustedPluginBug(error, packageName)) {
|
|
75
|
+
return { type: 'trustedPlugin' };
|
|
76
|
+
}
|
|
65
77
|
if (!isCorePluginBug(error, loadedFrom)) {
|
|
66
78
|
return {};
|
|
67
79
|
}
|
|
@@ -71,3 +83,7 @@ const isCorePluginBug = function (error, loadedFrom) {
|
|
|
71
83
|
const { severity } = parseErrorInfo(error);
|
|
72
84
|
return severity === 'warning' && loadedFrom === 'core';
|
|
73
85
|
};
|
|
86
|
+
const isTrustedPluginBug = function (error, packageName) {
|
|
87
|
+
const { severity } = parseErrorInfo(error);
|
|
88
|
+
return severity === 'warning' && isTrustedPlugin(packageName);
|
|
89
|
+
};
|
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 [
|
|
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) {
|
package/lib/steps/plugin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function isTrustedPlugin(
|
|
1
|
+
export function isTrustedPlugin(packageName: any): any;
|
|
2
2
|
export function firePluginStep({ event, childProcess, packageName, packagePath, pluginPackageJson, loadedFrom, origin, envChanges, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, constants, steps, error, logs, featureFlags, debug, verbose, }: {
|
|
3
3
|
event: any;
|
|
4
4
|
childProcess: any;
|
package/lib/steps/plugin.js
CHANGED
|
@@ -5,7 +5,7 @@ import { callChild } from '../plugins/ipc.js';
|
|
|
5
5
|
import { getSuccessStatus } from '../status/success.js';
|
|
6
6
|
import { getPluginErrorType } from './error.js';
|
|
7
7
|
import { updateNetlifyConfig, listConfigSideFiles } from './update_config.js';
|
|
8
|
-
export const isTrustedPlugin = (
|
|
8
|
+
export const isTrustedPlugin = (packageName) => packageName?.startsWith('@netlify/');
|
|
9
9
|
// Fire a plugin step
|
|
10
10
|
export const firePluginStep = async function ({ event, childProcess, packageName, packagePath, pluginPackageJson, loadedFrom, origin, envChanges, errorParams, configOpts, netlifyConfig, configMutations, headersPath, redirectsPath, constants, steps, error, logs, featureFlags, debug, verbose, }) {
|
|
11
11
|
const listeners = pipePluginOutput(childProcess, logs);
|
|
@@ -18,7 +18,7 @@ export const firePluginStep = async function ({ event, childProcess, packageName
|
|
|
18
18
|
event,
|
|
19
19
|
error,
|
|
20
20
|
envChanges,
|
|
21
|
-
featureFlags: isTrustedPlugin(pluginPackageJson) ? featureFlags : undefined,
|
|
21
|
+
featureFlags: isTrustedPlugin(pluginPackageJson?.name) ? featureFlags : undefined,
|
|
22
22
|
netlifyConfig,
|
|
23
23
|
constants,
|
|
24
24
|
},
|
|
@@ -49,7 +49,7 @@ export const firePluginStep = async function ({ event, childProcess, packageName
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
catch (newError) {
|
|
52
|
-
const errorType = getPluginErrorType(newError, loadedFrom);
|
|
52
|
+
const errorType = getPluginErrorType(newError, loadedFrom, packageName);
|
|
53
53
|
addErrorInfo(newError, {
|
|
54
54
|
...errorType,
|
|
55
55
|
plugin: { pluginPackageJson, packageName },
|
package/lib/steps/return.d.ts
CHANGED
|
@@ -24,20 +24,20 @@ export function getStepReturn({ event, packageName, newError, newEnvChanges, new
|
|
|
24
24
|
quiet: any;
|
|
25
25
|
metrics: any;
|
|
26
26
|
}): Promise<{
|
|
27
|
-
failedPlugin:
|
|
27
|
+
failedPlugin: string[];
|
|
28
28
|
newStatus: {
|
|
29
|
-
state:
|
|
30
|
-
title:
|
|
31
|
-
summary:
|
|
29
|
+
state: "failed_build" | "failed_plugin" | "canceled_build";
|
|
30
|
+
title: string | (import("../error/types.js").TitleFunction & string);
|
|
31
|
+
summary: string;
|
|
32
32
|
text: string | undefined;
|
|
33
33
|
extraData: any;
|
|
34
34
|
};
|
|
35
35
|
}> | Promise<{
|
|
36
36
|
newError: any;
|
|
37
37
|
newStatus: {
|
|
38
|
-
state:
|
|
39
|
-
title:
|
|
40
|
-
summary:
|
|
38
|
+
state: "failed_build" | "failed_plugin" | "canceled_build";
|
|
39
|
+
title: string | (import("../error/types.js").TitleFunction & string);
|
|
40
|
+
summary: string;
|
|
41
41
|
text: string | undefined;
|
|
42
42
|
extraData: any;
|
|
43
43
|
};
|
|
@@ -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
|
};
|
package/lib/steps/run_step.js
CHANGED
|
@@ -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,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
|
+
};
|
package/lib/utils/errors.d.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
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 {};
|
package/lib/utils/errors.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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.
|
|
3
|
+
"version": "29.29.0",
|
|
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": "
|
|
160
|
+
"gitHead": "e8dbd893dbc701ebbcb1df50133a7c0353bdbc8c"
|
|
158
161
|
}
|