@netlify/build 29.38.2 → 29.39.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.
- package/lib/plugins/compatibility.d.ts +3 -1
- package/lib/plugins/compatibility.js +10 -5
- package/lib/plugins/compatibility.test.js +20 -4
- package/lib/plugins/expected_version.js +12 -1
- package/lib/plugins_core/blobs_upload/index.d.ts +1 -1
- package/lib/plugins_core/blobs_upload/index.js +10 -14
- package/lib/plugins_core/dev_blobs_upload/index.d.ts +2 -0
- package/lib/plugins_core/dev_blobs_upload/index.js +75 -0
- package/lib/steps/get.js +2 -1
- package/lib/utils/blobs.d.ts +8 -0
- package/lib/utils/blobs.js +49 -5
- package/package.json +4 -4
- package/lib/plugins_core/blobs_upload/utils.d.ts +0 -8
- package/lib/plugins_core/blobs_upload/utils.js +0 -46
|
@@ -13,16 +13,18 @@ import { PluginVersion } from './list.js';
|
|
|
13
13
|
* - This is only used to print a warning message when the `compatibleVersion`
|
|
14
14
|
* is older than the currently used version.
|
|
15
15
|
*/
|
|
16
|
-
export declare const getExpectedVersion: ({ versions, nodeVersion, packageJson, packagePath, buildDir, pinnedVersion, featureFlags, systemLog, }: {
|
|
16
|
+
export declare const getExpectedVersion: ({ versions, nodeVersion, packageJson, packageName, packagePath, buildDir, pinnedVersion, featureFlags, systemLog, authoritative, }: {
|
|
17
17
|
versions: PluginVersion[];
|
|
18
18
|
/** The package.json of the repository */
|
|
19
19
|
packageJson: PackageJson;
|
|
20
|
+
packageName: string;
|
|
20
21
|
packagePath?: string | undefined;
|
|
21
22
|
buildDir: string;
|
|
22
23
|
nodeVersion: string;
|
|
23
24
|
pinnedVersion?: string | undefined;
|
|
24
25
|
featureFlags?: FeatureFlags | undefined;
|
|
25
26
|
systemLog: SystemLogger;
|
|
27
|
+
authoritative?: boolean | undefined;
|
|
26
28
|
}) => Promise<{
|
|
27
29
|
version: string;
|
|
28
30
|
compatWarning: string;
|
|
@@ -15,16 +15,17 @@ const pEvery = _pEvery;
|
|
|
15
15
|
* - This is only used to print a warning message when the `compatibleVersion`
|
|
16
16
|
* is older than the currently used version.
|
|
17
17
|
*/
|
|
18
|
-
export const getExpectedVersion = async function ({ versions, nodeVersion, packageJson, packagePath, buildDir, pinnedVersion, featureFlags, systemLog, }) {
|
|
18
|
+
export const getExpectedVersion = async function ({ versions, nodeVersion, packageJson, packageName, packagePath, buildDir, pinnedVersion, featureFlags, systemLog, authoritative, }) {
|
|
19
19
|
const { version, conditions = [] } = await getCompatibleEntry({
|
|
20
20
|
versions,
|
|
21
21
|
nodeVersion,
|
|
22
22
|
packageJson,
|
|
23
|
+
packageName,
|
|
23
24
|
packagePath,
|
|
24
25
|
buildDir,
|
|
25
26
|
pinnedVersion,
|
|
26
27
|
featureFlags,
|
|
27
|
-
systemLog,
|
|
28
|
+
systemLog: authoritative ? systemLog : undefined,
|
|
28
29
|
});
|
|
29
30
|
// Retrieve warning message shown when using an older version with `compatibility`
|
|
30
31
|
const compatWarning = conditions.map(({ type, condition }) => CONDITIONS[type].warning(condition)).join(', ');
|
|
@@ -46,7 +47,9 @@ export const getExpectedVersion = async function ({ versions, nodeVersion, packa
|
|
|
46
47
|
* - If there is a `pinnedVersion`, use it unless `latestVersion` matches it
|
|
47
48
|
* - Otherwise, use `latestVersion`
|
|
48
49
|
*/
|
|
49
|
-
const getCompatibleEntry = async function ({ versions, nodeVersion, packageJson, packagePath, buildDir, pinnedVersion, featureFlags, systemLog
|
|
50
|
+
const getCompatibleEntry = async function ({ versions, nodeVersion, packageJson, packageName, packagePath, buildDir, pinnedVersion, featureFlags, systemLog = () => {
|
|
51
|
+
// no-op
|
|
52
|
+
}, }) {
|
|
50
53
|
const compatibleEntry = await pLocate(versions, async ({ version, overridePinnedVersion, conditions }) => {
|
|
51
54
|
// When there's a `pinnedVersion`, we typically pick the first version that
|
|
52
55
|
// matches that range. The exception is if `overridePinnedVersion` is also
|
|
@@ -66,21 +69,23 @@ const getCompatibleEntry = async function ({ versions, nodeVersion, packageJson,
|
|
|
66
69
|
return await pEvery(conditions, async ({ type, condition }) => CONDITIONS[type].test(condition, { nodeVersion, packageJson, packagePath, buildDir }));
|
|
67
70
|
});
|
|
68
71
|
if (compatibleEntry) {
|
|
72
|
+
systemLog(`Used compatible version '${compatibleEntry.version}' for plugin '${packageName}' (pinned version is ${pinnedVersion})`);
|
|
69
73
|
return compatibleEntry;
|
|
70
74
|
}
|
|
71
75
|
if (pinnedVersion) {
|
|
76
|
+
systemLog(`Used pinned version '${pinnedVersion}' for plugin '${packageName}'`);
|
|
72
77
|
return { version: pinnedVersion, conditions: [] };
|
|
73
78
|
}
|
|
74
79
|
const legacyFallback = { version: versions[0].version, conditions: [] };
|
|
75
80
|
const fallback = await getFirstCompatibleEntry({ versions, nodeVersion, packageJson, packagePath, buildDir });
|
|
76
81
|
if (featureFlags?.netlify_build_updated_plugin_compatibility) {
|
|
77
82
|
if (legacyFallback.version !== fallback.version) {
|
|
78
|
-
systemLog(`Detected mismatch in selected version for plugin '${
|
|
83
|
+
systemLog(`Detected mismatch in selected version for plugin '${packageName}': used new version of '${fallback.version}' over legacy version '${legacyFallback.version}'`);
|
|
79
84
|
}
|
|
80
85
|
return fallback;
|
|
81
86
|
}
|
|
82
87
|
if (legacyFallback.version !== fallback.version) {
|
|
83
|
-
systemLog(`Detected mismatch in selected version for plugin '${
|
|
88
|
+
systemLog(`Detected mismatch in selected version for plugin '${packageName}': used legacy version '${legacyFallback.version}' over new version '${fallback.version}'`);
|
|
84
89
|
}
|
|
85
90
|
return legacyFallback;
|
|
86
91
|
};
|
|
@@ -13,6 +13,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
13
13
|
versions,
|
|
14
14
|
nodeVersion: '18.19.0',
|
|
15
15
|
packageJson: {},
|
|
16
|
+
packageName: '@netlify/cool-plugin',
|
|
16
17
|
buildDir: '/some/path',
|
|
17
18
|
pinnedVersion: '4',
|
|
18
19
|
systemLog: noopSystemLog,
|
|
@@ -29,6 +30,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
29
30
|
versions,
|
|
30
31
|
nodeVersion: '18.19.0',
|
|
31
32
|
packageJson: {},
|
|
33
|
+
packageName: '@netlify/cool-plugin',
|
|
32
34
|
buildDir: '/some/path',
|
|
33
35
|
systemLog: noopSystemLog,
|
|
34
36
|
});
|
|
@@ -36,6 +38,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
36
38
|
versions,
|
|
37
39
|
nodeVersion: '18.19.0',
|
|
38
40
|
packageJson: {},
|
|
41
|
+
packageName: '@netlify/cool-plugin',
|
|
39
42
|
buildDir: '/some/path',
|
|
40
43
|
pinnedVersion: '4',
|
|
41
44
|
systemLog: noopSystemLog,
|
|
@@ -52,6 +55,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
52
55
|
versions,
|
|
53
56
|
nodeVersion: '18.19.0',
|
|
54
57
|
packageJson: {},
|
|
58
|
+
packageName: '@netlify/cool-plugin',
|
|
55
59
|
buildDir: '/some/path',
|
|
56
60
|
pinnedVersion: '4',
|
|
57
61
|
systemLog: noopSystemLog,
|
|
@@ -70,6 +74,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
70
74
|
versions,
|
|
71
75
|
nodeVersion: '17.19.0',
|
|
72
76
|
packageJson: {},
|
|
77
|
+
packageName: '@netlify/cool-plugin',
|
|
73
78
|
buildDir: '/some/path',
|
|
74
79
|
pinnedVersion: '4',
|
|
75
80
|
systemLog: noopSystemLog,
|
|
@@ -100,6 +105,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
100
105
|
versions,
|
|
101
106
|
nodeVersion: '17.19.0',
|
|
102
107
|
packageJson: { dependencies: { next: '10.0.8' } },
|
|
108
|
+
packageName: '@netlify/cool-plugin',
|
|
103
109
|
buildDir: '/some/path',
|
|
104
110
|
pinnedVersion: '3',
|
|
105
111
|
systemLog: noopSystemLog,
|
|
@@ -109,6 +115,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
109
115
|
versions,
|
|
110
116
|
nodeVersion: '17.19.0',
|
|
111
117
|
packageJson: { dependencies: { next: '11.0.0' } },
|
|
118
|
+
packageName: '@netlify/cool-plugin',
|
|
112
119
|
buildDir: '/some/path',
|
|
113
120
|
pinnedVersion: '4',
|
|
114
121
|
systemLog: noopSystemLog,
|
|
@@ -118,6 +125,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
118
125
|
versions,
|
|
119
126
|
nodeVersion: '18.19.0',
|
|
120
127
|
packageJson: { dependencies: { next: '13.5.0' } },
|
|
128
|
+
packageName: '@netlify/cool-plugin',
|
|
121
129
|
buildDir: '/some/path',
|
|
122
130
|
pinnedVersion: '4',
|
|
123
131
|
systemLog: noopSystemLog,
|
|
@@ -148,6 +156,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
148
156
|
versions,
|
|
149
157
|
nodeVersion: '18.19.0',
|
|
150
158
|
packageJson: { dependencies: { next: '14.1.1-canary.36' } },
|
|
159
|
+
packageName: '@netlify/cool-plugin',
|
|
151
160
|
buildDir: '/some/path',
|
|
152
161
|
pinnedVersion: '4',
|
|
153
162
|
systemLog: noopSystemLog,
|
|
@@ -174,6 +183,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
174
183
|
versions,
|
|
175
184
|
nodeVersion: '20.0.0',
|
|
176
185
|
packageJson: { dependencies: { next: '14.0.0' } },
|
|
186
|
+
packageName: '@netlify/cool-plugin',
|
|
177
187
|
buildDir: '/some/path',
|
|
178
188
|
pinnedVersion: '4',
|
|
179
189
|
systemLog: noopSystemLog,
|
|
@@ -184,6 +194,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
184
194
|
versions,
|
|
185
195
|
nodeVersion: '20.0.0',
|
|
186
196
|
packageJson: { dependencies: { next: '13.0.0' } },
|
|
197
|
+
packageName: '@netlify/cool-plugin',
|
|
187
198
|
buildDir: '/some/path',
|
|
188
199
|
pinnedVersion: '4',
|
|
189
200
|
systemLog: noopSystemLog,
|
|
@@ -210,6 +221,7 @@ describe(`getExpectedVersion`, () => {
|
|
|
210
221
|
versions,
|
|
211
222
|
nodeVersion: '20.0.0',
|
|
212
223
|
packageJson: { dependencies: { next: '14.0.0' } },
|
|
224
|
+
packageName: '@netlify/cool-plugin',
|
|
213
225
|
buildDir: '/some/path',
|
|
214
226
|
pinnedVersion: '4',
|
|
215
227
|
systemLog: noopSystemLog,
|
|
@@ -236,14 +248,16 @@ describe(`getExpectedVersion`, () => {
|
|
|
236
248
|
const { version } = await getExpectedVersion({
|
|
237
249
|
versions,
|
|
238
250
|
nodeVersion: '20.0.0',
|
|
239
|
-
packageJson: {
|
|
251
|
+
packageJson: { dependencies: { next: '12.0.0' } },
|
|
252
|
+
packageName: '@netlify/cool-plugin',
|
|
240
253
|
buildDir: '/some/path',
|
|
241
254
|
systemLog: (message) => {
|
|
242
255
|
logMessages.push(message);
|
|
243
256
|
},
|
|
257
|
+
authoritative: true,
|
|
244
258
|
});
|
|
245
259
|
expect(logMessages.length).toBe(1);
|
|
246
|
-
expect(logMessages[0]).toBe(`Detected mismatch in selected version for plugin '@netlify/
|
|
260
|
+
expect(logMessages[0]).toBe(`Detected mismatch in selected version for plugin '@netlify/cool-plugin': used legacy version '5.0.0-beta.1' over new version '4.41.2'`);
|
|
247
261
|
expect(version).toBe('5.0.0-beta.1');
|
|
248
262
|
});
|
|
249
263
|
test('if no pinned version is set, it matches the first version whose requirements match the conditions', async () => {
|
|
@@ -266,7 +280,8 @@ describe(`getExpectedVersion`, () => {
|
|
|
266
280
|
const { version } = await getExpectedVersion({
|
|
267
281
|
versions,
|
|
268
282
|
nodeVersion: '20.0.0',
|
|
269
|
-
packageJson: {
|
|
283
|
+
packageJson: { dependencies: { next: '12.0.0' } },
|
|
284
|
+
packageName: '@netlify/cool-plugin',
|
|
270
285
|
buildDir: '/some/path',
|
|
271
286
|
systemLog: (message) => {
|
|
272
287
|
logMessages.push(message);
|
|
@@ -274,9 +289,10 @@ describe(`getExpectedVersion`, () => {
|
|
|
274
289
|
featureFlags: {
|
|
275
290
|
netlify_build_updated_plugin_compatibility: true,
|
|
276
291
|
},
|
|
292
|
+
authoritative: true,
|
|
277
293
|
});
|
|
278
294
|
expect(logMessages.length).toBe(1);
|
|
279
|
-
expect(logMessages[0]).toBe(`Detected mismatch in selected version for plugin '@netlify/
|
|
295
|
+
expect(logMessages[0]).toBe(`Detected mismatch in selected version for plugin '@netlify/cool-plugin': used new version of '4.41.2' over legacy version '5.0.0-beta.1'`);
|
|
280
296
|
expect(version).toBe('4.41.2');
|
|
281
297
|
});
|
|
282
298
|
});
|
|
@@ -41,13 +41,24 @@ const addExpectedVersion = async function ({ pluginsList, autoPluginsDir, packag
|
|
|
41
41
|
versions,
|
|
42
42
|
nodeVersion,
|
|
43
43
|
packageJson,
|
|
44
|
+
packageName,
|
|
44
45
|
packagePath,
|
|
45
46
|
buildDir,
|
|
46
47
|
pinnedVersion,
|
|
47
48
|
featureFlags,
|
|
48
49
|
systemLog,
|
|
50
|
+
authoritative: true,
|
|
51
|
+
}),
|
|
52
|
+
getExpectedVersion({
|
|
53
|
+
versions,
|
|
54
|
+
nodeVersion,
|
|
55
|
+
packageJson,
|
|
56
|
+
packageName,
|
|
57
|
+
packagePath,
|
|
58
|
+
buildDir,
|
|
59
|
+
featureFlags,
|
|
60
|
+
systemLog,
|
|
49
61
|
}),
|
|
50
|
-
getExpectedVersion({ versions, nodeVersion, packageJson, packagePath, buildDir, featureFlags, systemLog }),
|
|
51
62
|
]);
|
|
52
63
|
const isMissing = await isMissingVersion({ autoPluginsDir, packageName, pluginPath, loadedFrom, expectedVersion });
|
|
53
64
|
return {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CoreStep } from '../types.js';
|
|
1
|
+
import { type CoreStep } from '../types.js';
|
|
2
2
|
export declare const uploadBlobs: CoreStep;
|
|
@@ -3,8 +3,7 @@ import { getDeployStore } from '@netlify/blobs';
|
|
|
3
3
|
import pMap from 'p-map';
|
|
4
4
|
import semver from 'semver';
|
|
5
5
|
import { log, logError } from '../../log/logger.js';
|
|
6
|
-
import { scanForBlobs } from '../../utils/blobs.js';
|
|
7
|
-
import { getKeysToUpload, getFileWithMetadata } from './utils.js';
|
|
6
|
+
import { getFileWithMetadata, getKeysToUpload, scanForBlobs } from '../../utils/blobs.js';
|
|
8
7
|
const coreStep = async function ({ debug, logs, deployId, buildDir, quiet, packagePath, constants: { SITE_ID, NETLIFY_API_TOKEN, NETLIFY_API_HOST }, }) {
|
|
9
8
|
// This should never happen due to the condition check
|
|
10
9
|
if (!deployId || !NETLIFY_API_TOKEN) {
|
|
@@ -20,10 +19,8 @@ const coreStep = async function ({ debug, logs, deployId, buildDir, quiet, packa
|
|
|
20
19
|
};
|
|
21
20
|
// If we don't have native `fetch` in the global scope, add a polyfill.
|
|
22
21
|
if (semver.lt(nodeVersion, '18.0.0')) {
|
|
23
|
-
const nodeFetch = await import('node-fetch');
|
|
24
|
-
|
|
25
|
-
// are not a 100% match, even though the APIs are mostly compatible.
|
|
26
|
-
storeOpts.fetch = nodeFetch.default;
|
|
22
|
+
const nodeFetch = (await import('node-fetch')).default;
|
|
23
|
+
storeOpts.fetch = nodeFetch;
|
|
27
24
|
}
|
|
28
25
|
const blobs = await scanForBlobs(buildDir, packagePath);
|
|
29
26
|
// We checked earlier, but let's be extra safe
|
|
@@ -49,15 +46,14 @@ const coreStep = async function ({ debug, logs, deployId, buildDir, quiet, packa
|
|
|
49
46
|
if (!quiet) {
|
|
50
47
|
log(logs, `Uploading ${keys.length} blobs to deploy store...`);
|
|
51
48
|
}
|
|
52
|
-
const uploadBlob = async (key) => {
|
|
53
|
-
if (debug && !quiet) {
|
|
54
|
-
log(logs, `- Uploading blob ${key}`, { indent: true });
|
|
55
|
-
}
|
|
56
|
-
const { data, metadata } = await getFileWithMetadata(blobs.directory, key);
|
|
57
|
-
await blobStore.set(key, data, { metadata });
|
|
58
|
-
};
|
|
59
49
|
try {
|
|
60
|
-
await pMap(keys,
|
|
50
|
+
await pMap(keys, async (key) => {
|
|
51
|
+
if (debug && !quiet) {
|
|
52
|
+
log(logs, `- Uploading blob ${key}`, { indent: true });
|
|
53
|
+
}
|
|
54
|
+
const { data, metadata } = await getFileWithMetadata(blobs.directory, key);
|
|
55
|
+
await blobStore.set(key, data, { metadata });
|
|
56
|
+
}, { concurrency: 10 });
|
|
61
57
|
}
|
|
62
58
|
catch (err) {
|
|
63
59
|
logError(logs, `Error uploading blobs to deploy store: ${err.message}`);
|
|
@@ -0,0 +1,75 @@
|
|
|
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 { getFileWithMetadata, getKeysToUpload, scanForBlobs } from '../../utils/blobs.js';
|
|
7
|
+
const coreStep = async function ({ debug, logs, deployId, buildDir, quiet, packagePath, constants: { SITE_ID, NETLIFY_API_TOKEN, NETLIFY_API_HOST }, }) {
|
|
8
|
+
// This should never happen due to the condition check
|
|
9
|
+
if (!deployId || !NETLIFY_API_TOKEN) {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
// for cli deploys with `netlify deploy --build` the `NETLIFY_API_HOST` is undefined
|
|
13
|
+
const apiHost = NETLIFY_API_HOST || 'api.netlify.com';
|
|
14
|
+
const storeOpts = {
|
|
15
|
+
siteID: SITE_ID,
|
|
16
|
+
deployID: deployId,
|
|
17
|
+
token: NETLIFY_API_TOKEN,
|
|
18
|
+
apiURL: `https://${apiHost}`,
|
|
19
|
+
};
|
|
20
|
+
// If we don't have native `fetch` in the global scope, add a polyfill.
|
|
21
|
+
if (semver.lt(nodeVersion, '18.0.0')) {
|
|
22
|
+
const nodeFetch = (await import('node-fetch')).default;
|
|
23
|
+
storeOpts.fetch = nodeFetch;
|
|
24
|
+
}
|
|
25
|
+
const blobs = await scanForBlobs(buildDir, packagePath);
|
|
26
|
+
// We checked earlier, but let's be extra safe
|
|
27
|
+
if (blobs === null) {
|
|
28
|
+
if (!quiet) {
|
|
29
|
+
log(logs, 'No blobs to upload to deploy store.');
|
|
30
|
+
}
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
// If using the deploy config API, configure the store to use the region that
|
|
34
|
+
// was configured for the deploy.
|
|
35
|
+
if (!blobs.isLegacyDirectory) {
|
|
36
|
+
storeOpts.experimentalRegion = 'auto';
|
|
37
|
+
}
|
|
38
|
+
const blobStore = getDeployStore(storeOpts);
|
|
39
|
+
const keys = await getKeysToUpload(blobs.directory);
|
|
40
|
+
if (keys.length === 0) {
|
|
41
|
+
if (!quiet) {
|
|
42
|
+
log(logs, 'No blobs to upload to deploy store.');
|
|
43
|
+
}
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
if (!quiet) {
|
|
47
|
+
log(logs, `Uploading ${keys.length} blobs to deploy store...`);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
await pMap(keys, async (key) => {
|
|
51
|
+
if (debug && !quiet) {
|
|
52
|
+
log(logs, `- Uploading blob ${key}`, { indent: true });
|
|
53
|
+
}
|
|
54
|
+
const { data, metadata } = await getFileWithMetadata(blobs.directory, key);
|
|
55
|
+
await blobStore.set(key, data, { metadata });
|
|
56
|
+
}, { concurrency: 10 });
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
logError(logs, `Error uploading blobs to deploy store: ${err.message}`);
|
|
60
|
+
throw new Error(`Failed while uploading blobs to deploy store`);
|
|
61
|
+
}
|
|
62
|
+
if (!quiet) {
|
|
63
|
+
log(logs, `Done uploading blobs to deploy store.`);
|
|
64
|
+
}
|
|
65
|
+
return {};
|
|
66
|
+
};
|
|
67
|
+
const deployAndBlobsPresent = async ({ deployId, buildDir, packagePath, constants: { NETLIFY_API_TOKEN }, }) => Boolean(NETLIFY_API_TOKEN && deployId && (await scanForBlobs(buildDir, packagePath)));
|
|
68
|
+
export const devUploadBlobs = {
|
|
69
|
+
event: 'onDev',
|
|
70
|
+
coreStep,
|
|
71
|
+
coreStepId: 'dev_blobs_upload',
|
|
72
|
+
coreStepName: 'Uploading blobs',
|
|
73
|
+
coreStepDescription: () => 'Uploading blobs to development deploy store',
|
|
74
|
+
condition: deployAndBlobsPresent,
|
|
75
|
+
};
|
package/lib/steps/get.js
CHANGED
|
@@ -4,6 +4,7 @@ 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
6
|
import { applyDeployConfig } from '../plugins_core/deploy_config/index.js';
|
|
7
|
+
import { devUploadBlobs } from '../plugins_core/dev_blobs_upload/index.js';
|
|
7
8
|
import { bundleEdgeFunctions } from '../plugins_core/edge_functions/index.js';
|
|
8
9
|
import { bundleFunctions } from '../plugins_core/functions/index.js';
|
|
9
10
|
import { preCleanup } from '../plugins_core/pre_cleanup/index.js';
|
|
@@ -32,7 +33,7 @@ export const getDevSteps = function (command, steps, eventHandlers) {
|
|
|
32
33
|
coreStepDescription: () => 'Run command for local development',
|
|
33
34
|
};
|
|
34
35
|
const eventSteps = getEventSteps(eventHandlers);
|
|
35
|
-
const sortedSteps = sortSteps([preDevCleanup, ...steps, eventSteps, devCommandStep], DEV_EVENTS);
|
|
36
|
+
const sortedSteps = sortSteps([preDevCleanup, ...steps, devUploadBlobs, eventSteps, devCommandStep], DEV_EVENTS);
|
|
36
37
|
const events = getEvents(sortedSteps);
|
|
37
38
|
return { steps: sortedSteps, events };
|
|
38
39
|
};
|
package/lib/utils/blobs.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
1
2
|
/** Retrieve the absolute path of the deploy scoped internal blob directories */
|
|
2
3
|
export declare const getBlobsDirs: (buildDir: string, packagePath?: string) => string[];
|
|
3
4
|
/**
|
|
@@ -13,3 +14,10 @@ export declare const scanForBlobs: (buildDir: string, packagePath?: string) => P
|
|
|
13
14
|
directory: string;
|
|
14
15
|
isLegacyDirectory: boolean;
|
|
15
16
|
} | null>;
|
|
17
|
+
/** Given output directory, find all file paths to upload excluding metadata files */
|
|
18
|
+
export declare const getKeysToUpload: (blobsDir: string) => Promise<string[]>;
|
|
19
|
+
/** Read a file and its metadata file from the blobs directory */
|
|
20
|
+
export declare const getFileWithMetadata: (blobsDir: string, key: string) => Promise<{
|
|
21
|
+
data: Buffer;
|
|
22
|
+
metadata: Record<string, string>;
|
|
23
|
+
}>;
|
package/lib/utils/blobs.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import { fdir } from 'fdir';
|
|
3
4
|
const LEGACY_BLOBS_PATH = '.netlify/blobs/deploy';
|
|
4
5
|
const DEPLOY_CONFIG_BLOBS_PATH = '.netlify/deploy/v1/blobs/deploy';
|
|
5
6
|
/** Retrieve the absolute path of the deploy scoped internal blob directories */
|
|
6
7
|
export const getBlobsDirs = (buildDir, packagePath) => [
|
|
7
|
-
resolve(buildDir, packagePath || '', DEPLOY_CONFIG_BLOBS_PATH),
|
|
8
|
-
resolve(buildDir, packagePath || '', LEGACY_BLOBS_PATH),
|
|
8
|
+
path.resolve(buildDir, packagePath || '', DEPLOY_CONFIG_BLOBS_PATH),
|
|
9
|
+
path.resolve(buildDir, packagePath || '', LEGACY_BLOBS_PATH),
|
|
9
10
|
];
|
|
10
11
|
/**
|
|
11
12
|
* Detect if there are any blobs to upload, and if so, what directory they're
|
|
@@ -17,7 +18,7 @@ export const getBlobsDirs = (buildDir, packagePath) => [
|
|
|
17
18
|
* @returns
|
|
18
19
|
*/
|
|
19
20
|
export const scanForBlobs = async function (buildDir, packagePath) {
|
|
20
|
-
const blobsDir = resolve(buildDir, packagePath || '', DEPLOY_CONFIG_BLOBS_PATH);
|
|
21
|
+
const blobsDir = path.resolve(buildDir, packagePath || '', DEPLOY_CONFIG_BLOBS_PATH);
|
|
21
22
|
const blobsDirScan = await new fdir().onlyCounts().crawl(blobsDir).withPromise();
|
|
22
23
|
if (blobsDirScan.files > 0) {
|
|
23
24
|
return {
|
|
@@ -25,7 +26,7 @@ export const scanForBlobs = async function (buildDir, packagePath) {
|
|
|
25
26
|
isLegacyDirectory: false,
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
|
-
const legacyBlobsDir = resolve(buildDir, packagePath || '', LEGACY_BLOBS_PATH);
|
|
29
|
+
const legacyBlobsDir = path.resolve(buildDir, packagePath || '', LEGACY_BLOBS_PATH);
|
|
29
30
|
const legacyBlobsDirScan = await new fdir().onlyCounts().crawl(legacyBlobsDir).withPromise();
|
|
30
31
|
if (legacyBlobsDirScan.files > 0) {
|
|
31
32
|
return {
|
|
@@ -35,3 +36,46 @@ export const scanForBlobs = async function (buildDir, packagePath) {
|
|
|
35
36
|
}
|
|
36
37
|
return null;
|
|
37
38
|
};
|
|
39
|
+
const METADATA_PREFIX = '$';
|
|
40
|
+
const METADATA_SUFFIX = '.json';
|
|
41
|
+
/** Given output directory, find all file paths to upload excluding metadata files */
|
|
42
|
+
export const getKeysToUpload = async (blobsDir) => {
|
|
43
|
+
const files = await new fdir()
|
|
44
|
+
.withRelativePaths() // we want the relative path from the blobsDir
|
|
45
|
+
.filter((fpath) => !path.basename(fpath).startsWith(METADATA_PREFIX))
|
|
46
|
+
.crawl(blobsDir)
|
|
47
|
+
.withPromise();
|
|
48
|
+
// normalize the path separators to all use the forward slash
|
|
49
|
+
return files.map((f) => f.split(path.sep).join('/'));
|
|
50
|
+
};
|
|
51
|
+
/** Read a file and its metadata file from the blobs directory */
|
|
52
|
+
export const getFileWithMetadata = async (blobsDir, key) => {
|
|
53
|
+
const contentPath = path.join(blobsDir, key);
|
|
54
|
+
const dirname = path.dirname(key);
|
|
55
|
+
const basename = path.basename(key);
|
|
56
|
+
const metadataPath = path.join(blobsDir, dirname, `${METADATA_PREFIX}${basename}${METADATA_SUFFIX}`);
|
|
57
|
+
const [data, metadata] = await Promise.all([readFile(contentPath), readMetadata(metadataPath)]).catch((err) => {
|
|
58
|
+
throw new Error(`Failed while reading '${key}' and its metadata: ${err.message}`);
|
|
59
|
+
});
|
|
60
|
+
return { data, metadata };
|
|
61
|
+
};
|
|
62
|
+
const readMetadata = async (metadataPath) => {
|
|
63
|
+
let metadataFile;
|
|
64
|
+
try {
|
|
65
|
+
metadataFile = await readFile(metadataPath, { encoding: 'utf8' });
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (err.code === 'ENOENT') {
|
|
69
|
+
// no metadata file found, that's ok
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(metadataFile);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Normalize the error message
|
|
79
|
+
throw new Error(`Error parsing metadata file '${metadataPath}'`);
|
|
80
|
+
}
|
|
81
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/build",
|
|
3
|
-
"version": "29.
|
|
3
|
+
"version": "29.39.1",
|
|
4
4
|
"description": "Netlify build module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./lib/index.js",
|
|
@@ -73,12 +73,12 @@
|
|
|
73
73
|
"@netlify/config": "^20.12.1",
|
|
74
74
|
"@netlify/edge-bundler": "11.3.0",
|
|
75
75
|
"@netlify/framework-info": "^9.8.11",
|
|
76
|
-
"@netlify/functions-utils": "^5.2.
|
|
76
|
+
"@netlify/functions-utils": "^5.2.54",
|
|
77
77
|
"@netlify/git-utils": "^5.1.1",
|
|
78
78
|
"@netlify/opentelemetry-utils": "^1.1.0",
|
|
79
79
|
"@netlify/plugins-list": "^6.77.0",
|
|
80
80
|
"@netlify/run-utils": "^5.1.1",
|
|
81
|
-
"@netlify/zip-it-and-ship-it": "9.31.
|
|
81
|
+
"@netlify/zip-it-and-ship-it": "9.31.3",
|
|
82
82
|
"@sindresorhus/slugify": "^2.0.0",
|
|
83
83
|
"ansi-escapes": "^6.0.0",
|
|
84
84
|
"chalk": "^5.0.0",
|
|
@@ -164,5 +164,5 @@
|
|
|
164
164
|
"engines": {
|
|
165
165
|
"node": "^14.16.0 || >=16.0.0"
|
|
166
166
|
},
|
|
167
|
-
"gitHead": "
|
|
167
|
+
"gitHead": "b06aeddfb79f5d60d0fe8e66cebcb1e8eaa29f90"
|
|
168
168
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
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
|
-
}>;
|
|
@@ -1,46 +0,0 @@
|
|
|
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
|
-
}
|