netlify-cli 27.12.0 → 27.13.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/dist/commands/agents/agents-stop.js +2 -2
- package/dist/commands/base-command.js +7 -3
- package/dist/commands/deploy/deploy.js +51 -94
- package/dist/commands/sites/sites-create.js +3 -2
- package/dist/lib/spinner.d.ts +2 -2
- package/dist/lib/spinner.js +1 -1
- package/dist/utils/deploy/deploy-site.d.ts +32 -12
- package/dist/utils/deploy/deploy-site.js +18 -40
- package/dist/utils/deploy/drop-api.d.ts +7 -1
- package/dist/utils/deploy/drop-api.js +3 -4
- package/dist/utils/deploy/file.d.ts +4 -2
- package/dist/utils/deploy/hash-config.d.ts +5 -6
- package/dist/utils/deploy/hash-config.js +1 -4
- package/dist/utils/deploy/hash-files.d.ts +9 -8
- package/dist/utils/deploy/hash-fns.d.ts +45 -10
- package/dist/utils/deploy/hasher-segments.d.ts +10 -6
- package/dist/utils/deploy/hasher-segments.js +0 -1
- package/dist/utils/deploy/process-files.d.ts +1 -1
- package/dist/utils/deploy/process-files.js +1 -0
- package/dist/utils/deploy/types.d.ts +17 -0
- package/dist/utils/deploy/types.js +2 -0
- package/dist/utils/deploy/upload-files.d.ts +8 -3
- package/dist/utils/deploy/upload-files.js +4 -3
- package/dist/utils/deploy/upload-source-zip.js +4 -7
- package/dist/utils/deploy/util.d.ts +6 -3
- package/dist/utils/deploy/util.js +4 -16
- package/dist/utils/team.d.ts +1 -0
- package/dist/utils/team.js +10 -5
- package/dist/utils/telemetry/report-error.js +2 -3
- package/dist/utils/telemetry/telemetry.js +9 -5
- package/dist/utils/telemetry/utils.d.ts +1 -0
- package/dist/utils/telemetry/utils.js +4 -0
- package/functions-templates/javascript/scheduled-function/package.json +1 -1
- package/functions-templates/typescript/database/package-lock.json +8 -8
- package/functions-templates/typescript/database/package.json +1 -1
- package/functions-templates/typescript/database-drizzle/package-lock.json +8 -8
- package/functions-templates/typescript/database-drizzle/package.json +1 -1
- package/functions-templates/typescript/hello-world/package-lock.json +15 -17
- package/functions-templates/typescript/hello-world/package.json +1 -1
- package/functions-templates/typescript/scheduled-function/package.json +1 -1
- package/package.json +4 -4
|
@@ -38,8 +38,8 @@ export const agentsStop = async (id, options, command) => {
|
|
|
38
38
|
}
|
|
39
39
|
// Stop the agent task
|
|
40
40
|
const stopSpinnerInstance = startSpinner({ text: 'Stopping agent task...' });
|
|
41
|
-
const response = await fetch(`${apiOpts.scheme ?? 'https'}://${apiOpts.host ?? api.host}/api/v1/agent_runners/${id}`, {
|
|
42
|
-
method: '
|
|
41
|
+
const response = await fetch(`${apiOpts.scheme ?? 'https'}://${apiOpts.host ?? api.host}/api/v1/agent_runners/${id}/stop`, {
|
|
42
|
+
method: 'POST',
|
|
43
43
|
headers: {
|
|
44
44
|
Authorization: `Bearer ${api.accessToken ?? ''}`,
|
|
45
45
|
'User-Agent': apiOpts.userAgent,
|
|
@@ -234,6 +234,8 @@ export default class BaseCommand extends Command {
|
|
|
234
234
|
const getCommands = (command) => {
|
|
235
235
|
const parentCommand = this.name() === 'netlify' ? command : command.parent;
|
|
236
236
|
return (parentCommand?.commands
|
|
237
|
+
// subcommands nested with `.command()` rather than named `parent:child`, e.g. `database connect`
|
|
238
|
+
.concat(this.name() === 'netlify' ? [] : command.commands)
|
|
237
239
|
.filter((cmd) => {
|
|
238
240
|
if (cmd._hidden)
|
|
239
241
|
return false;
|
|
@@ -242,11 +244,13 @@ export default class BaseCommand extends Command {
|
|
|
242
244
|
// don't include subcommands on the main page
|
|
243
245
|
return !cmd.name().includes(':');
|
|
244
246
|
}
|
|
245
|
-
return cmd.name().startsWith(`${command.name()}:`);
|
|
247
|
+
return cmd.name().startsWith(`${command.name()}:`) || cmd.parent === command;
|
|
246
248
|
})
|
|
247
249
|
.sort((a, b) => a.name().localeCompare(b.name())) || []);
|
|
248
250
|
};
|
|
249
|
-
|
|
251
|
+
/** nested subcommands are shown by their full path, e.g. `database migrations apply` */
|
|
252
|
+
const getCommandTerm = (cmd) => cmd.parent && cmd.parent.name() !== 'netlify' ? `${getCommandTerm(cmd.parent)} ${cmd.name()}` : cmd.name();
|
|
253
|
+
help.longestSubcommandTermLength = (command) => getCommands(command).reduce((max, cmd) => Math.max(max, getCommandTerm(cmd).length), 0);
|
|
250
254
|
/** override the longestOptionTermLength to react on hide options flag */
|
|
251
255
|
help.longestOptionTermLength = (command, helper) => (!command.noBaseOptions &&
|
|
252
256
|
helper.visibleOptions(command).reduce((max, option) => Math.max(max, helper.optionTerm(option).length), 0)) ||
|
|
@@ -313,7 +317,7 @@ export default class BaseCommand extends Command {
|
|
|
313
317
|
'',
|
|
314
318
|
];
|
|
315
319
|
}
|
|
316
|
-
const commandList = getCommands(command).map((cmd) => formatItem(cmd
|
|
320
|
+
const commandList = getCommands(command).map((cmd) => formatItem(getCommandTerm(cmd), helper.subcommandDescription(cmd).split('\n')[0], true));
|
|
317
321
|
if (commandList.length !== 0) {
|
|
318
322
|
output = [...output, chalk.bold('COMMANDS'), formatHelpList(commandList), ''];
|
|
319
323
|
}
|
|
@@ -72,7 +72,7 @@ const getDeployFolder = async ({ command, config, options, site, siteData, }) =>
|
|
|
72
72
|
? resolve(command.jsWorkspaceRoot || site.root, options.dir)
|
|
73
73
|
: resolve(command.workingDir, options.dir);
|
|
74
74
|
}
|
|
75
|
-
else if (config
|
|
75
|
+
else if (config.build.publish) {
|
|
76
76
|
deployFolder = resolve(site.root, config.build.publish);
|
|
77
77
|
}
|
|
78
78
|
else if (siteData?.build_settings?.dir) {
|
|
@@ -166,13 +166,6 @@ const validateFolders = async ({ deployFolder, functionsFolder, }) => {
|
|
|
166
166
|
const functionsFolderStat = await validateFunctionsFolder(functionsFolder);
|
|
167
167
|
return { deployFolderStat, functionsFolderStat };
|
|
168
168
|
};
|
|
169
|
-
/**
|
|
170
|
-
* @param {object} config
|
|
171
|
-
* @param {string} config.deployFolder
|
|
172
|
-
* @param {*} config.site
|
|
173
|
-
* @returns
|
|
174
|
-
*/
|
|
175
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'deployFolder' implicitly has an '... Remove this comment to see the full error message
|
|
176
169
|
const getDeployFilesFilter = ({ deployFolder, site }) => {
|
|
177
170
|
// site.root === deployFolder can happen when users run `netlify deploy --dir .`
|
|
178
171
|
// in that specific case we don't want to publish the repo node_modules
|
|
@@ -258,11 +251,8 @@ const generateDeployCommand = (options, availableTeams, command) => {
|
|
|
258
251
|
}
|
|
259
252
|
return parts.join(' ');
|
|
260
253
|
};
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (typeof siteData.published_deploy === 'object' &&
|
|
264
|
-
siteData.published_deploy !== null &&
|
|
265
|
-
siteData.published_deploy.locked) {
|
|
254
|
+
const prepareProductionDeploy = async ({ api, siteData, options, command, }) => {
|
|
255
|
+
if (siteData.published_deploy?.locked) {
|
|
266
256
|
log(`\n${NETLIFYDEVERR} Deployments are "locked" for production context of this project\n`);
|
|
267
257
|
const overrideCommand = generateDeployCommand({ ...options, prodIfUnlocked: true, prod: false }, [], command);
|
|
268
258
|
if (!isInteractive()) {
|
|
@@ -282,6 +272,19 @@ const prepareProductionDeploy = async ({ api, siteData, options, command }) => {
|
|
|
282
272
|
log(`\n${NETLIFYDEVLOG} "Auto publishing" has been enabled for production context\n`);
|
|
283
273
|
}
|
|
284
274
|
};
|
|
275
|
+
const createDeploy = async ({ alias, api, command, deployToProduction, options, siteData, siteId, }) => {
|
|
276
|
+
if (deployToProduction) {
|
|
277
|
+
await prepareProductionDeploy({ siteData, api, options, command });
|
|
278
|
+
}
|
|
279
|
+
const draft = options.draft || (!deployToProduction && !alias);
|
|
280
|
+
const createDeployBody = {
|
|
281
|
+
draft,
|
|
282
|
+
branch: alias,
|
|
283
|
+
include_upload_url: options.uploadSourceZip,
|
|
284
|
+
...getDeploySourceFields(),
|
|
285
|
+
};
|
|
286
|
+
return (await api.createSiteDeploy({ siteId, title: options.message, body: createDeployBody }));
|
|
287
|
+
};
|
|
285
288
|
const hasErrorMessage = (actual, expected) => {
|
|
286
289
|
if (typeof actual === 'string') {
|
|
287
290
|
return actual.includes(expected);
|
|
@@ -337,6 +340,7 @@ const deployProgressCb = function () {
|
|
|
337
340
|
return;
|
|
338
341
|
case 'stop':
|
|
339
342
|
default: {
|
|
343
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- FIXME: a `stop` event is assumed to follow a `start` event of the same type
|
|
340
344
|
spinnersByType[event.type].success(event.msg);
|
|
341
345
|
delete spinnersByType[event.type];
|
|
342
346
|
}
|
|
@@ -357,7 +361,7 @@ const uploadDeployBlobs = async ({ cachedConfig, deployId, options, packagePath,
|
|
|
357
361
|
// We log our own progress so we don't want this as well. Plus, this logs much of the same
|
|
358
362
|
// information as the build that (likely) came before this as part of the deploy build.
|
|
359
363
|
quiet: options.debug ?? true,
|
|
360
|
-
// @ts-expect-error(
|
|
364
|
+
// @ts-expect-error FIXME(@netlify/build): the `cachedConfig` flag is typed `Record<string, unknown>`, rejecting `CachedConfig`
|
|
361
365
|
cachedConfig,
|
|
362
366
|
packagePath,
|
|
363
367
|
deployId,
|
|
@@ -378,37 +382,7 @@ const uploadDeployBlobs = async ({ cachedConfig, deployId, options, packagePath,
|
|
|
378
382
|
phase: 'stop',
|
|
379
383
|
});
|
|
380
384
|
};
|
|
381
|
-
const runDeploy = async ({
|
|
382
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'alias' implicitly has an 'any' ty... Remove this comment to see the full error message
|
|
383
|
-
alias,
|
|
384
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message
|
|
385
|
-
api, command,
|
|
386
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message
|
|
387
|
-
config,
|
|
388
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'deployFolder' implicitly has an '... Remove this comment to see the full error message
|
|
389
|
-
deployFolder,
|
|
390
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'deployTimeout' implicitly has an ... Remove this comment to see the full error message
|
|
391
|
-
deployTimeout,
|
|
392
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'deployToProduction' implicitly ha... Remove this comment to see the full error message
|
|
393
|
-
deployToProduction,
|
|
394
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'functionsConfig' implicitly has a... Remove this comment to see the full error message
|
|
395
|
-
functionsConfig, functionsFolder,
|
|
396
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'options' implicitly has an 'a... Remove this comment to see the full error message
|
|
397
|
-
options,
|
|
398
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'packagePath' implicitly has an 'a... Remove this comment to see the full error message
|
|
399
|
-
packagePath,
|
|
400
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'silent' implicitly has an 'any' t... Remove this comment to see the full error message
|
|
401
|
-
silent,
|
|
402
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'site' implicitly has an 'any' typ... Remove this comment to see the full error message
|
|
403
|
-
site,
|
|
404
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'siteData' implicitly has an 'any'... Remove this comment to see the full error message
|
|
405
|
-
siteData,
|
|
406
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'siteId' implicitly has an 'any' t... Remove this comment to see the full error message
|
|
407
|
-
siteId,
|
|
408
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'skipFunctionsCache' implicitly ha... Remove this comment to see the full error message
|
|
409
|
-
skipFunctionsCache,
|
|
410
|
-
// @ts-expect-error TS(7031) FIXME: Binding element 'title' implicitly has an 'any' ty... Remove this comment to see the full error message
|
|
411
|
-
title, deployId: existingDeployId, }) => {
|
|
385
|
+
const runDeploy = async ({ alias, api, command, config, deployFolder, deployTimeout, deployToProduction, functionsConfig, functionsFolder, options, packagePath, silent, site, siteData, siteId, skipFunctionsCache, deployId: existingDeployId, }) => {
|
|
412
386
|
let results;
|
|
413
387
|
let deployId = existingDeployId;
|
|
414
388
|
let uploadSourceZipResult;
|
|
@@ -416,17 +390,15 @@ title, deployId: existingDeployId, }) => {
|
|
|
416
390
|
// We won't have a deploy ID if we run the command with `--no-build`.
|
|
417
391
|
// In this case, we must create the deploy.
|
|
418
392
|
if (!deployId) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
};
|
|
429
|
-
const createDeployResponse = await api.createSiteDeploy({ siteId, title, body: createDeployBody });
|
|
393
|
+
const createDeployResponse = await createDeploy({
|
|
394
|
+
alias,
|
|
395
|
+
api,
|
|
396
|
+
command,
|
|
397
|
+
deployToProduction,
|
|
398
|
+
options,
|
|
399
|
+
siteData,
|
|
400
|
+
siteId,
|
|
401
|
+
});
|
|
430
402
|
deployId = createDeployResponse.id;
|
|
431
403
|
if (options.uploadSourceZip &&
|
|
432
404
|
createDeployResponse.source_zip_upload_url &&
|
|
@@ -449,17 +421,21 @@ title, deployId: existingDeployId, }) => {
|
|
|
449
421
|
command.netlify.frameworksAPIPaths.functions.path,
|
|
450
422
|
functionsFolder,
|
|
451
423
|
].filter((folder) => Boolean(folder));
|
|
452
|
-
const manifestPath = skipFunctionsCache
|
|
424
|
+
const manifestPath = skipFunctionsCache
|
|
425
|
+
? undefined
|
|
426
|
+
: await getFunctionsManifestPath({ base: site.root, packagePath });
|
|
453
427
|
const serverManifestPath = await getServerManifestPath({ base: site.root, packagePath });
|
|
454
428
|
const redirectsPath = `${deployFolder}/_redirects`;
|
|
455
429
|
const headersPath = `${deployFolder}/_headers`;
|
|
456
430
|
const { redirects } = await parseAllRedirects({
|
|
431
|
+
// @ts-expect-error FIXME(@netlify/redirect-parser): `configRedirects` is typed `string[]` but takes redirect objects
|
|
457
432
|
configRedirects: config.redirects,
|
|
458
433
|
redirectsFiles: [redirectsPath],
|
|
459
434
|
minimal: true,
|
|
460
435
|
});
|
|
461
436
|
config.redirects = redirects;
|
|
462
437
|
const { headers } = await parseAllHeaders({
|
|
438
|
+
// @ts-expect-error FIXME(@netlify/headers-parser): `MinimalHeader` rejects the array values `NetlifyConfig['headers']` allows
|
|
463
439
|
configHeaders: config.headers,
|
|
464
440
|
headersFiles: [headersPath],
|
|
465
441
|
minimal: true,
|
|
@@ -474,7 +450,6 @@ title, deployId: existingDeployId, }) => {
|
|
|
474
450
|
packagePath: command.workspacePackage,
|
|
475
451
|
});
|
|
476
452
|
results = await deploySite(command, api, siteId, deployFolder, {
|
|
477
|
-
// @ts-expect-error FIXME
|
|
478
453
|
config,
|
|
479
454
|
fnDir: functionDirectories,
|
|
480
455
|
functionsConfig,
|
|
@@ -485,9 +460,9 @@ title, deployId: existingDeployId, }) => {
|
|
|
485
460
|
deployId,
|
|
486
461
|
filter: getDeployFilesFilter({ site, deployFolder }),
|
|
487
462
|
workingDir: command.workingDir,
|
|
488
|
-
manifestPath,
|
|
463
|
+
manifestPath: manifestPath ?? undefined,
|
|
489
464
|
packagePath,
|
|
490
|
-
serverEnabled: Boolean(siteData
|
|
465
|
+
serverEnabled: Boolean(siteData.feature_flags?.netlify_build_server_standalone),
|
|
491
466
|
serverManifestPath: serverManifestPath ?? undefined,
|
|
492
467
|
skipFunctionsCache,
|
|
493
468
|
siteRoot: site.root,
|
|
@@ -581,10 +556,8 @@ const bundleEdgeFunctions = async (options, command) => {
|
|
|
581
556
|
// We log our own progress so we don't want this as well. Plus, this logs much of the same
|
|
582
557
|
// information as the build that (likely) came before this as part of the deploy build.
|
|
583
558
|
quiet: options.debug ?? true,
|
|
584
|
-
// (cachedConfig type error hides this one, but it still is valid) @ts-expect-error FIXME(serhalp): This is missing from the `runCoreSteps` type in @netlify/build
|
|
585
559
|
edgeFunctionsBootstrapURL: await getBootstrapURL(),
|
|
586
|
-
// @ts-expect-error
|
|
587
|
-
// Index signature for type 'string' is missing in type 'CachedConfig'.
|
|
560
|
+
// @ts-expect-error FIXME(@netlify/build): `cachedConfig` is typed `Record<string, unknown>` and `edgeFunctionsBootstrapURL` is missing from the flags
|
|
588
561
|
cachedConfig: command.netlify.cachedConfig,
|
|
589
562
|
});
|
|
590
563
|
if (!success) {
|
|
@@ -722,7 +695,6 @@ const prepAndRunDeploy = async ({ api, command, config, deployToProduction, opti
|
|
|
722
695
|
siteEnv,
|
|
723
696
|
});
|
|
724
697
|
const results = await runDeploy({
|
|
725
|
-
// @ts-expect-error FIXME
|
|
726
698
|
alias,
|
|
727
699
|
api,
|
|
728
700
|
command,
|
|
@@ -735,12 +707,11 @@ const prepAndRunDeploy = async ({ api, command, config, deployToProduction, opti
|
|
|
735
707
|
functionsFolder: functionsFolderStat && functionsFolder,
|
|
736
708
|
options,
|
|
737
709
|
packagePath: command.workspacePackage,
|
|
738
|
-
silent: options.json || options.silent,
|
|
710
|
+
silent: options.json || Boolean(options.silent),
|
|
739
711
|
site,
|
|
740
712
|
siteData,
|
|
741
713
|
siteId,
|
|
742
714
|
skipFunctionsCache: options.skipFunctionsCache,
|
|
743
|
-
title: options.message,
|
|
744
715
|
deployId,
|
|
745
716
|
});
|
|
746
717
|
return results;
|
|
@@ -768,10 +739,11 @@ const createSiteWithFlags = async (options, command, site) => {
|
|
|
768
739
|
throw new Error('Team must be specified to create a site');
|
|
769
740
|
}
|
|
770
741
|
try {
|
|
771
|
-
|
|
742
|
+
// FIXME(@netlify/api): the `site` response type makes every field optional, unlike `SiteInfo`
|
|
743
|
+
const siteData = (await api.createSiteInTeam({
|
|
772
744
|
accountSlug: options.team,
|
|
773
745
|
body,
|
|
774
|
-
});
|
|
746
|
+
}));
|
|
775
747
|
site.id = siteData.id;
|
|
776
748
|
return siteData;
|
|
777
749
|
}
|
|
@@ -781,10 +753,11 @@ const createSiteWithFlags = async (options, command, site) => {
|
|
|
781
753
|
const suffixedName = `${siteName.trim()}-${suffix}`;
|
|
782
754
|
log(`Site name "${siteName}" is taken. Retrying with "${suffixedName}"...`);
|
|
783
755
|
try {
|
|
784
|
-
|
|
756
|
+
// FIXME(@netlify/api): the `site` response type makes every field optional, unlike `SiteInfo`
|
|
757
|
+
const siteData = (await api.createSiteInTeam({
|
|
785
758
|
accountSlug: options.team,
|
|
786
759
|
body: { name: suffixedName },
|
|
787
|
-
});
|
|
760
|
+
}));
|
|
788
761
|
site.id = siteData.id;
|
|
789
762
|
return siteData;
|
|
790
763
|
}
|
|
@@ -861,7 +834,7 @@ const anonymousDeploy = async (options, command) => {
|
|
|
861
834
|
}
|
|
862
835
|
};
|
|
863
836
|
const checkForFunctions = async () => {
|
|
864
|
-
const functionsFolder = getFunctionsFolder({ config, options, site,
|
|
837
|
+
const functionsFolder = getFunctionsFolder({ config, options, site, workingDir });
|
|
865
838
|
const internalFunctionsDir = await getInternalFunctionsDir({ base: site.root });
|
|
866
839
|
const frameworksFunctionsDir = command.netlify.frameworksAPIPaths.functions.path;
|
|
867
840
|
const hasFunctions = (await dirHasFiles(functionsFolder)) ||
|
|
@@ -891,17 +864,16 @@ const anonymousDeploy = async (options, command) => {
|
|
|
891
864
|
}
|
|
892
865
|
const deployFolder = await getDeployFolder({
|
|
893
866
|
command,
|
|
894
|
-
config
|
|
867
|
+
config,
|
|
895
868
|
options,
|
|
896
869
|
site,
|
|
897
|
-
siteData: {},
|
|
898
870
|
});
|
|
899
871
|
await validateDeployFolder(deployFolder);
|
|
900
872
|
const edgeFunctionsDistPath = await getEdgeFunctionsDistPathIfExists(workingDir);
|
|
901
873
|
const filter = getDeployFilesFilter({ site, deployFolder });
|
|
902
874
|
const { files, filesShaMap } = await hashFiles({
|
|
903
875
|
concurrentHash: DEFAULT_CONCURRENT_HASH,
|
|
904
|
-
directories: [deployFolder, edgeFunctionsDistPath].filter(Boolean),
|
|
876
|
+
directories: [deployFolder, edgeFunctionsDistPath].filter((dir) => Boolean(dir)),
|
|
905
877
|
filter,
|
|
906
878
|
normalizer: deployFileNormalizer.bind(null, workingDir),
|
|
907
879
|
statusCb: options.json ? () => { } : deployProgressCb(),
|
|
@@ -941,7 +913,7 @@ const anonymousDeploy = async (options, command) => {
|
|
|
941
913
|
}
|
|
942
914
|
const deploy = await waitForDropDeploy(dropApiOptions, deployInfo.id, deployInfo.deploy_id, options.timeout ? options.timeout * 1000 : DEFAULT_DEPLOY_TIMEOUT);
|
|
943
915
|
site.id = deployInfo.id;
|
|
944
|
-
const siteUrl =
|
|
916
|
+
const siteUrl = deploy.ssl_url || deploy.url || `https://${deployInfo.subdomain}.netlify.app`;
|
|
945
917
|
const isPasswordProtected = !options.createdVia || options.createdVia === 'drop';
|
|
946
918
|
const claimUrl = `https://app.netlify.com/drop/${deployInfo.subdomain}#drop_token=${dropToken}`;
|
|
947
919
|
if (options.json) {
|
|
@@ -1013,23 +985,8 @@ export const deploy = async (options, command) => {
|
|
|
1013
985
|
const deployToProduction = !options.draft && (options.prod || (options.prodIfUnlocked && !(siteData.published_deploy?.locked ?? false)));
|
|
1014
986
|
let results = {};
|
|
1015
987
|
if (options.build) {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
}
|
|
1019
|
-
const draft = options.draft || (!deployToProduction && !alias);
|
|
1020
|
-
const createDeployBody = {
|
|
1021
|
-
draft,
|
|
1022
|
-
branch: alias,
|
|
1023
|
-
include_upload_url: options.uploadSourceZip,
|
|
1024
|
-
...getDeploySourceFields(),
|
|
1025
|
-
};
|
|
1026
|
-
// TODO: Type this properly in `@netlify/api`.
|
|
1027
|
-
const deployMetadata = (await api.createSiteDeploy({
|
|
1028
|
-
siteId,
|
|
1029
|
-
title: options.message,
|
|
1030
|
-
body: createDeployBody,
|
|
1031
|
-
}));
|
|
1032
|
-
const deployId = deployMetadata.id || '';
|
|
988
|
+
const deployMetadata = await createDeploy({ alias, api, command, deployToProduction, options, siteData, siteId });
|
|
989
|
+
const deployId = deployMetadata.id;
|
|
1033
990
|
const skewProtectionToken = deployMetadata.skew_protection_token;
|
|
1034
991
|
let sourceZipFileName;
|
|
1035
992
|
if (options.uploadSourceZip &&
|
|
@@ -1058,7 +1015,7 @@ export const deploy = async (options, command) => {
|
|
|
1058
1015
|
options,
|
|
1059
1016
|
workingDir,
|
|
1060
1017
|
api,
|
|
1061
|
-
site,
|
|
1018
|
+
site: site,
|
|
1062
1019
|
config: netlifyConfig,
|
|
1063
1020
|
siteData,
|
|
1064
1021
|
siteId,
|
|
@@ -1087,7 +1044,7 @@ export const deploy = async (options, command) => {
|
|
|
1087
1044
|
options,
|
|
1088
1045
|
workingDir,
|
|
1089
1046
|
api,
|
|
1090
|
-
site,
|
|
1047
|
+
site: site,
|
|
1091
1048
|
config: command.netlify.config,
|
|
1092
1049
|
siteData,
|
|
1093
1050
|
siteId,
|
|
@@ -5,7 +5,7 @@ import { chalk, logAndThrowError, log, logJson, warn } from '../../utils/command
|
|
|
5
5
|
import getRepoData from '../../utils/get-repo-data.js';
|
|
6
6
|
import { configureRepo } from '../../utils/init/config.js';
|
|
7
7
|
import { isInteractive } from '../../utils/scripted-commands.js';
|
|
8
|
-
import { resolveTeamForNonInteractive } from '../../utils/team.js';
|
|
8
|
+
import { requireTeams, resolveTeamForNonInteractive } from '../../utils/team.js';
|
|
9
9
|
import { track } from '../../utils/telemetry/index.js';
|
|
10
10
|
import { MAX_SITE_NAME_LENGTH } from '../../utils/validation.js';
|
|
11
11
|
import { link } from '../link/link.js';
|
|
@@ -29,9 +29,10 @@ export const sitesCreate = async (options, command) => {
|
|
|
29
29
|
log(`Using team: ${team.name}`);
|
|
30
30
|
}
|
|
31
31
|
else {
|
|
32
|
+
const teams = requireTeams(accounts);
|
|
32
33
|
accountSlug = await select({
|
|
33
34
|
message: 'Team:',
|
|
34
|
-
choices:
|
|
35
|
+
choices: teams.map((account) => ({
|
|
35
36
|
value: account.slug,
|
|
36
37
|
name: account.name,
|
|
37
38
|
})),
|
package/dist/lib/spinner.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export declare const startSpinner: ({ text }: {
|
|
|
8
8
|
/**
|
|
9
9
|
* Stops the spinner with the following text
|
|
10
10
|
*/
|
|
11
|
-
export declare const stopSpinner: ({ error, spinner, text }: {
|
|
11
|
+
export declare const stopSpinner: ({ error, spinner, text, }: {
|
|
12
12
|
error?: boolean;
|
|
13
|
-
spinner: Spinner;
|
|
13
|
+
spinner: Spinner | undefined;
|
|
14
14
|
text?: string;
|
|
15
15
|
}) => void;
|
|
16
16
|
/**
|
package/dist/lib/spinner.js
CHANGED
|
@@ -33,7 +33,7 @@ export const startSpinner = ({ text }) => {
|
|
|
33
33
|
/**
|
|
34
34
|
* Stops the spinner with the following text
|
|
35
35
|
*/
|
|
36
|
-
export const stopSpinner = ({ error, spinner, text }) => {
|
|
36
|
+
export const stopSpinner = ({ error, spinner, text, }) => {
|
|
37
37
|
if (!spinner) {
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
@@ -1,26 +1,46 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import type
|
|
1
|
+
import type { NetlifyAPI } from '@netlify/api';
|
|
2
|
+
import type { Config as FunctionsConfig } from '@netlify/zip-it-and-ship-it';
|
|
3
|
+
import type BaseCommand from '../../commands/base-command.js';
|
|
4
|
+
import type { File } from './file.js';
|
|
5
|
+
import type { DeployEvent, StatusCallback } from './status-cb.js';
|
|
6
|
+
import type { DeployConfig } from './types.js';
|
|
4
7
|
import type { DeployEnvironmentVariable } from '../env/deploy-env-vars.js';
|
|
5
8
|
export type { DeployEvent };
|
|
6
|
-
export
|
|
9
|
+
export interface DeploySiteOptions {
|
|
10
|
+
assetType?: 'file' | undefined;
|
|
11
|
+
branch?: string;
|
|
7
12
|
concurrentHash?: number;
|
|
8
13
|
concurrentUpload?: number;
|
|
14
|
+
/** The site configuration, uploaded as the deploy's `netlify.toml` */
|
|
15
|
+
config: DeployConfig;
|
|
16
|
+
deployId: string;
|
|
9
17
|
deployTimeout?: number;
|
|
10
18
|
draft?: boolean;
|
|
11
19
|
environment?: DeployEnvironmentVariable[];
|
|
20
|
+
filter: (filename: string) => boolean;
|
|
21
|
+
fnDir?: string[];
|
|
22
|
+
functionsConfig?: FunctionsConfig | undefined;
|
|
23
|
+
hashAlgorithm?: string;
|
|
24
|
+
manifestPath?: string | undefined;
|
|
12
25
|
maxRetry?: number;
|
|
13
|
-
packagePath?: string;
|
|
26
|
+
packagePath?: string | undefined;
|
|
14
27
|
serverEnabled?: boolean;
|
|
15
|
-
serverManifestPath?: string;
|
|
16
|
-
|
|
28
|
+
serverManifestPath?: string | undefined;
|
|
29
|
+
siteRoot?: string | undefined;
|
|
30
|
+
skipFunctionsCache?: boolean | undefined;
|
|
31
|
+
statusCb?: StatusCallback;
|
|
17
32
|
syncFileLimit?: number;
|
|
18
33
|
tmpDir?: string;
|
|
19
|
-
fnDir?: string[];
|
|
20
34
|
workingDir: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
}
|
|
36
|
+
export declare const deploySite: (command: BaseCommand, api: NetlifyAPI, siteId: string, dir: string, { assetType, branch, concurrentHash, concurrentUpload, config, deployId, deployTimeout, draft, environment, filter, fnDir, functionsConfig, hashAlgorithm, manifestPath, maxRetry, packagePath, serverEnabled, serverManifestPath, siteRoot, skipFunctionsCache, statusCb, syncFileLimit, tmpDir, workingDir, }: DeploySiteOptions) => Promise<{
|
|
37
|
+
deployId: string;
|
|
38
|
+
deploy: import("./util.js").ReadyDeploy;
|
|
39
|
+
uploadList: (import("./upload-files.js").EdgeFunctionUploadFile | import("./upload-files.js").ServerUploadFile | File | (import("./upload-files.js").FunctionUploadFile & {
|
|
40
|
+
hash: string;
|
|
41
|
+
relname: string;
|
|
42
|
+
}) | (import("./upload-files.js").InlineUploadFile & {
|
|
43
|
+
hash: string;
|
|
44
|
+
}))[];
|
|
25
45
|
}>;
|
|
26
46
|
//# sourceMappingURL=deploy-site.d.ts.map
|
|
@@ -18,31 +18,7 @@ const buildStatsString = (possibleParts) => {
|
|
|
18
18
|
}
|
|
19
19
|
return `${parts.slice(0, -1).join(', ')} and ${parts[parts.length - 1]}`;
|
|
20
20
|
};
|
|
21
|
-
export const deploySite = async (command, api,
|
|
22
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'siteId' implicitly has an 'any' type.
|
|
23
|
-
siteId,
|
|
24
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'dir' implicitly has an 'any' type.
|
|
25
|
-
dir, {
|
|
26
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
27
|
-
assetType,
|
|
28
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
29
|
-
branch, concurrentHash = DEFAULT_CONCURRENT_HASH, concurrentUpload = DEFAULT_CONCURRENT_UPLOAD,
|
|
30
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
31
|
-
config,
|
|
32
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
33
|
-
deployId, deployTimeout = DEFAULT_DEPLOY_TIMEOUT, draft = false, environment,
|
|
34
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
35
|
-
filter, fnDir = [],
|
|
36
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
37
|
-
functionsConfig,
|
|
38
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
39
|
-
hashAlgorithm,
|
|
40
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
41
|
-
manifestPath, maxRetry = DEFAULT_MAX_RETRY, packagePath, serverEnabled, serverManifestPath,
|
|
42
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
43
|
-
siteRoot,
|
|
44
|
-
// @ts-expect-error TS(2525) FIXME: Initializer provides no value for this binding ele... Remove this comment to see the full error message
|
|
45
|
-
skipFunctionsCache, statusCb = () => {
|
|
21
|
+
export const deploySite = async (command, api, siteId, dir, { assetType, branch, concurrentHash = DEFAULT_CONCURRENT_HASH, concurrentUpload = DEFAULT_CONCURRENT_UPLOAD, config, deployId, deployTimeout = DEFAULT_DEPLOY_TIMEOUT, draft = false, environment, filter, fnDir = [], functionsConfig, hashAlgorithm, manifestPath, maxRetry = DEFAULT_MAX_RETRY, packagePath, serverEnabled, serverManifestPath, siteRoot, skipFunctionsCache, statusCb = () => {
|
|
46
22
|
/* default to noop */
|
|
47
23
|
}, syncFileLimit = DEFAULT_SYNC_LIMIT, tmpDir = temporaryDirectory(), workingDir, }) => {
|
|
48
24
|
statusCb({
|
|
@@ -57,7 +33,7 @@ skipFunctionsCache, statusCb = () => {
|
|
|
57
33
|
hashFiles({
|
|
58
34
|
assetType,
|
|
59
35
|
concurrentHash,
|
|
60
|
-
directories: [dir, edgeFunctionsDistPath, deployConfigPath, dbMigrationsDistPath].filter(Boolean),
|
|
36
|
+
directories: [dir, edgeFunctionsDistPath, deployConfigPath, dbMigrationsDistPath].filter((d) => Boolean(d)),
|
|
61
37
|
filter,
|
|
62
38
|
hashAlgorithm,
|
|
63
39
|
normalizer: deployFileNormalizer.bind(null, workingDir),
|
|
@@ -99,9 +75,7 @@ skipFunctionsCache, statusCb = () => {
|
|
|
99
75
|
throw new Error('Nothing to deploy');
|
|
100
76
|
}
|
|
101
77
|
if (functionsWithNativeModules.length !== 0) {
|
|
102
|
-
const functionsWithNativeModulesMessage = functionsWithNativeModules
|
|
103
|
-
.map(({ name }) => `- ${name}`)
|
|
104
|
-
.join('\n');
|
|
78
|
+
const functionsWithNativeModulesMessage = functionsWithNativeModules.map(({ name }) => `- ${name}`).join('\n');
|
|
105
79
|
warn(`Modules with native dependencies\n
|
|
106
80
|
${functionsWithNativeModulesMessage}
|
|
107
81
|
|
|
@@ -120,8 +94,7 @@ For more information, visit https://ntl.fyi/cli-native-modules.`);
|
|
|
120
94
|
});
|
|
121
95
|
const packageFrameworks = command.project.frameworks.get(command.workspacePackage ?? '');
|
|
122
96
|
const primaryFramework = packageFrameworks?.[0];
|
|
123
|
-
|
|
124
|
-
const cleanedParams = cleanDeep({
|
|
97
|
+
const params = {
|
|
125
98
|
siteId,
|
|
126
99
|
deploy_id: deployId,
|
|
127
100
|
body: {
|
|
@@ -138,21 +111,27 @@ For more information, visit https://ntl.fyi/cli-native-modules.`);
|
|
|
138
111
|
framework_version: primaryFramework?.detected.package?.version?.toString() ?? 'unknown',
|
|
139
112
|
build_version: getNetlifyBuildVersion(),
|
|
140
113
|
},
|
|
141
|
-
}
|
|
114
|
+
};
|
|
115
|
+
const cleanedParams =
|
|
116
|
+
// @ts-expect-error FIXME(clean-deep): typings declare an ES `export default` but the package is CommonJS
|
|
117
|
+
cleanDeep(params);
|
|
142
118
|
// cleanDeep deeply strips keys with empty strings, but empty strings are valid environment
|
|
143
119
|
// variable values--a user can use an empty string to e.g. unset a variable only for a deploy.
|
|
144
120
|
// This would result in payloads with a missing `value` key, which the API would reject.
|
|
145
121
|
const deployParams = environment?.length
|
|
146
122
|
? { ...cleanedParams, body: { ...cleanedParams.body, environment } }
|
|
147
123
|
: cleanedParams;
|
|
148
|
-
|
|
124
|
+
// FIXME(@netlify/api): `id` and `required` are optional on the updated deploy
|
|
125
|
+
let deploy = (await api.updateSiteDeploy(
|
|
126
|
+
// @ts-expect-error FIXME(@netlify/api): `functions_config` rejects zip-it-and-ship-it's `BuildData`, route `methods` and `traffic_rules` strings
|
|
127
|
+
deployParams));
|
|
149
128
|
if (deployParams.body.async)
|
|
150
|
-
deploy = await waitForDiff(api, deploy.id, siteId, deployTimeout);
|
|
129
|
+
deploy = (await waitForDiff(api, deploy.id, siteId, deployTimeout));
|
|
151
130
|
const { required: requiredFiles, required_functions: requiredFns, required_edge_functions: requiredEdgeFns, required_server: requiredServer, } = deploy;
|
|
152
131
|
const newStats = buildStatsString([
|
|
153
132
|
requiredFiles.length > 0 && pluralize(requiredFiles.length, 'file'),
|
|
154
|
-
|
|
155
|
-
|
|
133
|
+
requiredFns != null && requiredFns.length > 0 && pluralize(requiredFns.length, 'function'),
|
|
134
|
+
requiredEdgeFns != null && requiredEdgeFns.length > 0 && pluralize(requiredEdgeFns.length, 'edge function'),
|
|
156
135
|
(requiredServer?.length ?? 0) > 0 && 'a server',
|
|
157
136
|
]);
|
|
158
137
|
statusCb({
|
|
@@ -171,18 +150,17 @@ For more information, visit https://ntl.fyi/cli-native-modules.`);
|
|
|
171
150
|
msg: 'Waiting for deploy to go live...',
|
|
172
151
|
phase: 'start',
|
|
173
152
|
});
|
|
174
|
-
|
|
153
|
+
const readyDeploy = await waitForDeploy(api, deployId, siteId, deployTimeout);
|
|
175
154
|
statusCb({
|
|
176
155
|
type: 'wait-for-deploy',
|
|
177
156
|
msg: draft ? 'Draft deploy is live!' : 'Deploy is live!',
|
|
178
157
|
phase: 'stop',
|
|
179
158
|
});
|
|
180
159
|
await rm(tmpDir, { force: true, recursive: true });
|
|
181
|
-
|
|
160
|
+
return {
|
|
182
161
|
deployId,
|
|
183
|
-
deploy,
|
|
162
|
+
deploy: readyDeploy,
|
|
184
163
|
uploadList,
|
|
185
164
|
};
|
|
186
|
-
return deployManifest;
|
|
187
165
|
};
|
|
188
166
|
//# sourceMappingURL=deploy-site.js.map
|
|
@@ -18,7 +18,13 @@ export interface DropApiError extends Error {
|
|
|
18
18
|
export declare const getDropToken: ({ apiBase, userAgent }: DropApiOptions) => Promise<string>;
|
|
19
19
|
export declare const createDropDeploy: ({ apiBase, userAgent }: DropApiOptions, files: Record<string, string>, token: string, createdVia?: string) => Promise<DropDeployInfo>;
|
|
20
20
|
export declare const uploadDropFile: ({ apiBase, userAgent }: DropApiOptions, deployId: string, filePath: string, body: fs.ReadStream | Buffer, token: string) => Promise<void>;
|
|
21
|
-
|
|
21
|
+
interface DropSiteDeploy {
|
|
22
|
+
state?: string;
|
|
23
|
+
error_message?: string;
|
|
24
|
+
ssl_url?: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare const waitForDropDeploy: ({ apiBase, userAgent }: DropApiOptions, siteId: string, deployId: string, timeout?: number) => Promise<DropSiteDeploy>;
|
|
22
28
|
export declare const claimDropSite: ({ apiBase, userAgent }: DropApiOptions, siteId: string, dropToken: string, authToken: string) => Promise<void>;
|
|
23
29
|
export interface UploadListItem {
|
|
24
30
|
normalizedPath: string;
|
|
@@ -44,8 +44,6 @@ export const createDropDeploy = async ({ apiBase, userAgent }, files, token, cre
|
|
|
44
44
|
};
|
|
45
45
|
// TODO: Migrate to @netlify/api when Drop endpoints are in the OpenAPI spec.
|
|
46
46
|
export const uploadDropFile = async ({ apiBase, userAgent }, deployId, filePath, body, token) => {
|
|
47
|
-
// Node.js fetch needs `duplex: 'half'` for streaming bodies which isn't in standard RequestInit
|
|
48
|
-
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any */
|
|
49
47
|
const normalizedFilePath = filePath.startsWith('/') ? filePath : `/${filePath}`;
|
|
50
48
|
const response = await fetch(`${apiBase}/deploys/${deployId}/files${encodeURI(normalizedFilePath)}`, {
|
|
51
49
|
method: 'PUT',
|
|
@@ -53,10 +51,10 @@ export const uploadDropFile = async ({ apiBase, userAgent }, deployId, filePath,
|
|
|
53
51
|
'Content-Type': 'application/octet-stream',
|
|
54
52
|
Authorization: `Bearer ${token}`,
|
|
55
53
|
}),
|
|
56
|
-
body
|
|
54
|
+
body,
|
|
55
|
+
// Node.js fetch requires this for streaming bodies
|
|
57
56
|
duplex: 'half',
|
|
58
57
|
});
|
|
59
|
-
/* eslint-enable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any */
|
|
60
58
|
if (!response.ok) {
|
|
61
59
|
const error = new Error(`Failed to upload file ${filePath}: ${String(response.status)} ${response.statusText}`);
|
|
62
60
|
error.status = response.status;
|
|
@@ -79,6 +77,7 @@ export const waitForDropDeploy = async ({ apiBase, userAgent }, siteId, deployId
|
|
|
79
77
|
return true;
|
|
80
78
|
}
|
|
81
79
|
if (data.state === 'error') {
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- an empty error message falls back to the generic one
|
|
82
81
|
throw new Error(data.error_message || `Deploy ${deployId} had an error`);
|
|
83
82
|
}
|
|
84
83
|
return false;
|