netlify-cli 27.15.0 → 27.16.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/deploy/deploy.js +31 -6
- package/dist/commands/deploy/index.js +15 -2
- package/dist/commands/deploy/option_values.d.ts +1 -0
- package/dist/commands/deploy/remote/follow.d.ts +8 -0
- package/dist/commands/deploy/remote/follow.js +17 -0
- package/dist/commands/deploy/remote/git.d.ts +27 -0
- package/dist/commands/deploy/remote/git.js +61 -0
- package/dist/commands/deploy/remote/source.d.ts +25 -0
- package/dist/commands/deploy/remote/source.js +88 -0
- package/dist/commands/deploy/remote-deploy.d.ts +19 -0
- package/dist/commands/deploy/remote-deploy.js +126 -0
- package/dist/lib/edge-functions/proxy.js +1 -1
- package/dist/utils/create-stream-promise.d.ts +6 -0
- package/dist/utils/create-stream-promise.js +14 -2
- package/dist/utils/deploy/deploy-source.d.ts +1 -0
- package/dist/utils/deploy/deploy-source.js +2 -1
- package/dist/utils/netlify-git.d.ts +1 -0
- package/dist/utils/netlify-git.js +27 -0
- package/dist/utils/proxy.js +44 -7
- package/dist/utils/socketeer.js +8 -1
- package/package.json +7 -7
|
@@ -35,6 +35,7 @@ import hashFiles from '../../utils/deploy/hash-files.js';
|
|
|
35
35
|
import { deployFileNormalizer, getEdgeFunctionsDistPathIfExists } from '../../utils/deploy/process-files.js';
|
|
36
36
|
import { link } from '../link/link.js';
|
|
37
37
|
import { sitesCreate } from '../sites/sites-create.js';
|
|
38
|
+
import { remoteDeploy } from './remote-deploy.js';
|
|
38
39
|
import boxen from 'boxen';
|
|
39
40
|
import terminalLink from 'terminal-link';
|
|
40
41
|
import { anyEdgeFunctionsDirectoryExists } from '../../lib/edge-functions/get-directories.js';
|
|
@@ -559,7 +560,7 @@ const bundleEdgeFunctions = async (options, command) => {
|
|
|
559
560
|
phase: 'stop',
|
|
560
561
|
});
|
|
561
562
|
};
|
|
562
|
-
const printResults = ({ deployToProduction, uploadSourceZip, json, results,
|
|
563
|
+
const printResults = ({ deployToProduction, uploadSourceZip, json, results, prodCommand, }) => {
|
|
563
564
|
const msgData = {
|
|
564
565
|
'Build logs': terminalLink(results.logsUrl, results.logsUrl, { fallback: false }),
|
|
565
566
|
'Function logs': terminalLink(results.functionLogsUrl, results.functionLogsUrl, { fallback: false }),
|
|
@@ -585,6 +586,9 @@ const printResults = ({ deployToProduction, uploadSourceZip, json, results, runB
|
|
|
585
586
|
if (uploadSourceZip) {
|
|
586
587
|
jsonData.source_zip_filename = results.sourceZipFileName;
|
|
587
588
|
}
|
|
589
|
+
if (results.commitSha) {
|
|
590
|
+
jsonData.commit_sha = results.commitSha;
|
|
591
|
+
}
|
|
588
592
|
logJson(jsonData);
|
|
589
593
|
exit(0);
|
|
590
594
|
}
|
|
@@ -602,7 +606,7 @@ const printResults = ({ deployToProduction, uploadSourceZip, json, results, runB
|
|
|
602
606
|
if (!deployToProduction) {
|
|
603
607
|
log();
|
|
604
608
|
log('If everything looks good on your draft URL, deploy it to your main project URL with the --prod flag:');
|
|
605
|
-
log(
|
|
609
|
+
log(prodCommand);
|
|
606
610
|
log();
|
|
607
611
|
}
|
|
608
612
|
}
|
|
@@ -626,7 +630,7 @@ const printResults = ({ deployToProduction, uploadSourceZip, json, results, runB
|
|
|
626
630
|
if (!deployToProduction) {
|
|
627
631
|
log();
|
|
628
632
|
log('If everything looks good on your draft URL, deploy it to your main project URL with the --prod flag:');
|
|
629
|
-
log(chalk.cyanBright.bold(
|
|
633
|
+
log(chalk.cyanBright.bold(prodCommand));
|
|
630
634
|
log();
|
|
631
635
|
}
|
|
632
636
|
}
|
|
@@ -787,6 +791,9 @@ const ensureSiteExists = async (options, command, site, siteInfo) => {
|
|
|
787
791
|
if (hasSiteData) {
|
|
788
792
|
return siteInfo;
|
|
789
793
|
}
|
|
794
|
+
if (options.remote) {
|
|
795
|
+
return logAndThrowError('--remote needs an existing Netlify project connected to a git repository. Run netlify link to link this directory to one, or pass --site.');
|
|
796
|
+
}
|
|
790
797
|
if (options.createSite) {
|
|
791
798
|
return createSiteWithFlags(options, command, site);
|
|
792
799
|
}
|
|
@@ -940,7 +947,13 @@ export const deploy = async (options, command) => {
|
|
|
940
947
|
const { workingDir } = command;
|
|
941
948
|
const { api, site, siteInfo } = command.netlify;
|
|
942
949
|
const alias = options.alias || options.branch;
|
|
943
|
-
command.setAnalyticsPayload({
|
|
950
|
+
command.setAnalyticsPayload({
|
|
951
|
+
open: options.open,
|
|
952
|
+
prod: options.prod,
|
|
953
|
+
json: options.json,
|
|
954
|
+
alias: Boolean(alias),
|
|
955
|
+
remote: Boolean(options.remote),
|
|
956
|
+
});
|
|
944
957
|
if (options.allowAnonymous) {
|
|
945
958
|
const [token] = await getToken(options.auth);
|
|
946
959
|
if (token) {
|
|
@@ -969,7 +982,19 @@ export const deploy = async (options, command) => {
|
|
|
969
982
|
}
|
|
970
983
|
const deployToProduction = !options.draft && (options.prod || (options.prodIfUnlocked && !(siteData.published_deploy?.locked ?? false)));
|
|
971
984
|
let results = {};
|
|
972
|
-
|
|
985
|
+
let prodCommand = `netlify deploy${options.build ? '' : ' --no-build'} --prod`;
|
|
986
|
+
if (options.remote) {
|
|
987
|
+
results = await remoteDeploy({
|
|
988
|
+
api,
|
|
989
|
+
workingDir,
|
|
990
|
+
options,
|
|
991
|
+
siteData,
|
|
992
|
+
deployToProduction,
|
|
993
|
+
prepareProductionDeploy: () => prepareProductionDeploy({ siteData, api, options, command }),
|
|
994
|
+
});
|
|
995
|
+
prodCommand = 'netlify deploy --remote --prod';
|
|
996
|
+
}
|
|
997
|
+
else if (options.build) {
|
|
973
998
|
const deployMetadata = await createDeploy({ alias, api, command, deployToProduction, options, siteData, siteId });
|
|
974
999
|
const deployId = deployMetadata.id;
|
|
975
1000
|
const skewProtectionToken = deployMetadata.skew_protection_token;
|
|
@@ -1037,7 +1062,7 @@ export const deploy = async (options, command) => {
|
|
|
1037
1062
|
});
|
|
1038
1063
|
}
|
|
1039
1064
|
printResults({
|
|
1040
|
-
|
|
1065
|
+
prodCommand,
|
|
1041
1066
|
json: options.json,
|
|
1042
1067
|
results,
|
|
1043
1068
|
deployToProduction,
|
|
@@ -4,15 +4,25 @@ import terminalLink from 'terminal-link';
|
|
|
4
4
|
import { normalizeContext } from '../../utils/env/index.js';
|
|
5
5
|
import { findDuplicateKey, mergeDeployEnvVars, parseDeployEnvVar } from '../../utils/env/deploy-env-vars.js';
|
|
6
6
|
import { chalk, logAndThrowError, warn } from '../../utils/command-helpers.js';
|
|
7
|
+
const BUILD_RELATED_OPTIONS = [
|
|
8
|
+
'build',
|
|
9
|
+
'context',
|
|
10
|
+
'dir',
|
|
11
|
+
'env',
|
|
12
|
+
'functions',
|
|
13
|
+
'secretEnv',
|
|
14
|
+
'skipFunctionsCache',
|
|
15
|
+
'uploadSourceZip',
|
|
16
|
+
];
|
|
7
17
|
export const createDeployCommand = (program) => program
|
|
8
18
|
.command('deploy')
|
|
9
19
|
.description(`Deploy your project to Netlify
|
|
10
20
|
|
|
11
|
-
Builds and deploys
|
|
21
|
+
Builds your project locally, or on Netlify with --remote, and deploys it to Netlify. Creates a draft deploy by default.
|
|
12
22
|
Use --prod to deploy directly to your live site.
|
|
13
23
|
|
|
14
24
|
The deploy command will:
|
|
15
|
-
- Build your project (unless --no-build is specified)
|
|
25
|
+
- Build your project locally (unless --no-build or --remote is specified)
|
|
16
26
|
- Upload static files, functions, and edge functions
|
|
17
27
|
- Process redirects and headers from netlify.toml or _redirects/_headers files
|
|
18
28
|
- Provide deploy and function logs URLs
|
|
@@ -38,6 +48,7 @@ For detailed configuration options, see the Netlify documentation.`)
|
|
|
38
48
|
.option('--json', 'Output deployment data as JSON')
|
|
39
49
|
.option('--timeout <number>', 'Timeout to wait for deployment to finish', (value) => Number.parseInt(value))
|
|
40
50
|
.addOption(new Option('--trigger', 'Trigger a new build of your project on Netlify without uploading local files').conflicts('build'))
|
|
51
|
+
.addOption(new Option('--remote', 'Build your project on Netlify instead of locally. Useful when building with native packages, on devices with limited resources or missing toolchains. Build logs stream in real time and are available on Netlify.').conflicts([...BUILD_RELATED_OPTIONS, 'alias', 'allowAnonymous', 'branch', 'createSite', 'siteName', 'trigger']))
|
|
41
52
|
.addOption(new Option('--build', 'Do not use - this is now the default. Will be removed in future versions.')
|
|
42
53
|
.default(true)
|
|
43
54
|
.hideHelp(true))
|
|
@@ -66,6 +77,8 @@ For detailed configuration options, see the Netlify documentation.`)
|
|
|
66
77
|
'netlify deploy --message "A message with an $ENV_VAR"',
|
|
67
78
|
'netlify deploy --auth $NETLIFY_AUTH_TOKEN',
|
|
68
79
|
'netlify deploy --trigger',
|
|
80
|
+
'netlify deploy --remote # Build on Netlify and deploy a draft',
|
|
81
|
+
'netlify deploy --remote --prod',
|
|
69
82
|
'netlify deploy --context deploy-preview',
|
|
70
83
|
'netlify deploy --env "NODE_ENV=production" --env "API_URL=https://api.example.com"',
|
|
71
84
|
'netlify deploy --env "NODE_ENV=production" --secret-env "DATABASE_PASSWORD=$DB_PASSWORD"',
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NetlifyAPI } from '@netlify/api';
|
|
2
|
+
export declare const followDeploy: (api: NetlifyAPI, { siteId, deployId, timeoutMs, onLogLine, }: {
|
|
3
|
+
siteId: string;
|
|
4
|
+
deployId: string;
|
|
5
|
+
timeoutMs: number;
|
|
6
|
+
onLogLine?: (line: string) => void;
|
|
7
|
+
}) => Promise<import("../../../utils/deploy/util.js").ReadyDeploy>;
|
|
8
|
+
//# sourceMappingURL=follow.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { streamDeploy } from '../../logs/sources/deploy.js';
|
|
2
|
+
import { waitForDeploy } from '../../../utils/deploy/util.js';
|
|
3
|
+
// The log stream is best-effort; the deploy's state decides success.
|
|
4
|
+
export const followDeploy = async (api, { siteId, deployId, timeoutMs, onLogLine, }) => {
|
|
5
|
+
const stopStreaming = onLogLine
|
|
6
|
+
? streamDeploy(siteId, deployId, api.accessToken, (entry) => {
|
|
7
|
+
onLogLine(entry.message);
|
|
8
|
+
}, () => undefined)
|
|
9
|
+
: undefined;
|
|
10
|
+
try {
|
|
11
|
+
return await waitForDeploy(api, deployId, siteId, timeoutMs);
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
stopStreaming?.();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=follow.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface GitCommand {
|
|
2
|
+
args: string[];
|
|
3
|
+
cwd: string;
|
|
4
|
+
exitCode: number;
|
|
5
|
+
stderr: string;
|
|
6
|
+
}
|
|
7
|
+
export type GitCommandObserver = (command: GitCommand) => void;
|
|
8
|
+
export interface GitContext {
|
|
9
|
+
cwd: string;
|
|
10
|
+
onCommand?: GitCommandObserver;
|
|
11
|
+
}
|
|
12
|
+
interface Remote {
|
|
13
|
+
name: string;
|
|
14
|
+
url: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const getRemotes: (ctx: GitContext) => Promise<Remote[]>;
|
|
17
|
+
export declare const getHead: (ctx: GitContext) => Promise<string>;
|
|
18
|
+
export declare const getGitVersion: (ctx: GitContext) => Promise<string>;
|
|
19
|
+
export declare const getCurrentBranch: (ctx: GitContext) => Promise<string>;
|
|
20
|
+
export declare const getUpstream: (ctx: GitContext) => Promise<string>;
|
|
21
|
+
export declare const getWorkTreeStatus: (ctx: GitContext) => Promise<{
|
|
22
|
+
hasChanges: boolean;
|
|
23
|
+
untracked: string[];
|
|
24
|
+
}>;
|
|
25
|
+
export declare const getRemoteBranchesContaining: (ctx: GitContext, sha: string) => Promise<string[]>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import semver from 'semver';
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
// `code` is the exit code once git ran, or a string like ENOENT when it couldn't start.
|
|
6
|
+
const isFailedCommand = (error) => error instanceof Error && 'code' in error && typeof error.code === 'number';
|
|
7
|
+
const run = async (ctx, args) => {
|
|
8
|
+
let result;
|
|
9
|
+
try {
|
|
10
|
+
// GIT_OPTIONAL_LOCKS keeps read-only commands like `git status` from refreshing the user's index.
|
|
11
|
+
const env = { ...process.env, GIT_OPTIONAL_LOCKS: '0' };
|
|
12
|
+
const { stdout, stderr } = await execFileAsync('git', args, { cwd: ctx.cwd, env });
|
|
13
|
+
result = { exitCode: 0, stdout, stderr };
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (!isFailedCommand(error)) {
|
|
17
|
+
throw new Error(`Couldn't run git: ${String(error)}`, { cause: error });
|
|
18
|
+
}
|
|
19
|
+
result = { exitCode: error.code, stdout: error.stdout ?? '', stderr: error.stderr ?? '' };
|
|
20
|
+
}
|
|
21
|
+
ctx.onCommand?.({ args, cwd: ctx.cwd, exitCode: result.exitCode, stderr: result.stderr.trimEnd() });
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
24
|
+
const git = async (ctx, args) => {
|
|
25
|
+
const { exitCode, stdout, stderr } = await run(ctx, args);
|
|
26
|
+
if (exitCode !== 0) {
|
|
27
|
+
throw new Error(`git ${args[0]} failed: ${stderr.trimEnd() || stdout}`);
|
|
28
|
+
}
|
|
29
|
+
return stdout.trim();
|
|
30
|
+
};
|
|
31
|
+
const tryGit = async (ctx, args) => {
|
|
32
|
+
const { exitCode, stdout } = await run(ctx, args);
|
|
33
|
+
return exitCode === 0 ? stdout.trim() : '';
|
|
34
|
+
};
|
|
35
|
+
export const getRemotes = async (ctx) => {
|
|
36
|
+
const names = (await tryGit(ctx, ['remote'])).split('\n').filter(Boolean);
|
|
37
|
+
return Promise.all(names.map(async (name) => ({ name, url: await git(ctx, ['remote', 'get-url', name]) })));
|
|
38
|
+
};
|
|
39
|
+
export const getHead = (ctx) => tryGit(ctx, ['rev-parse', '--verify', 'HEAD']);
|
|
40
|
+
export const getGitVersion = async (ctx) => {
|
|
41
|
+
const output = await tryGit(ctx, ['version']).catch(() => '');
|
|
42
|
+
return semver.coerce(output)?.version ?? '';
|
|
43
|
+
};
|
|
44
|
+
// Full ref names, since short ones become ambiguous when a local branch is named like a remote one
|
|
45
|
+
// (e.g. `origin/main`).
|
|
46
|
+
const stripRefPrefix = (ref, prefix) => (ref.startsWith(prefix) ? ref.slice(prefix.length) : '');
|
|
47
|
+
export const getCurrentBranch = async (ctx) => stripRefPrefix(await tryGit(ctx, ['symbolic-ref', '--quiet', 'HEAD']), 'refs/heads/');
|
|
48
|
+
export const getUpstream = async (ctx) => stripRefPrefix(await tryGit(ctx, ['rev-parse', '--symbolic-full-name', '@{upstream}']), 'refs/remotes/');
|
|
49
|
+
export const getWorkTreeStatus = async (ctx) => {
|
|
50
|
+
const lines = (await git(ctx, ['status', '--porcelain', '--untracked-files=normal'])).split('\n').filter(Boolean);
|
|
51
|
+
return {
|
|
52
|
+
hasChanges: lines.some((line) => !line.startsWith('?? ')),
|
|
53
|
+
untracked: lines.filter((line) => line.startsWith('?? ')).map((line) => line.slice(3)),
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const listRemoteBranches = async (ctx, filter) => {
|
|
57
|
+
const output = await tryGit(ctx, ['branch', '--remotes', ...filter, '--format=%(refname:lstrip=2)']);
|
|
58
|
+
return output.split('\n').filter((name) => name !== '' && !name.endsWith('/HEAD'));
|
|
59
|
+
};
|
|
60
|
+
export const getRemoteBranchesContaining = (ctx, sha) => listRemoteBranches(ctx, ['--contains', sha]);
|
|
61
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SiteInfo } from '../../../utils/types.js';
|
|
2
|
+
import { type GitCommandObserver } from './git.js';
|
|
3
|
+
export type RemoteSource = {
|
|
4
|
+
kind: 'external';
|
|
5
|
+
repoUrl: string;
|
|
6
|
+
head: string;
|
|
7
|
+
branch: string;
|
|
8
|
+
untracked: string[];
|
|
9
|
+
};
|
|
10
|
+
export interface SourceCommit {
|
|
11
|
+
sha: string;
|
|
12
|
+
branch?: string;
|
|
13
|
+
}
|
|
14
|
+
/** A problem the user can fix, as opposed to an unexpected failure. */
|
|
15
|
+
export declare class RemoteBuildError extends Error {
|
|
16
|
+
readonly name = "RemoteBuildError";
|
|
17
|
+
}
|
|
18
|
+
export declare const resolveRemoteSource: ({ siteInfo, workingDir, onCommand, production, }: {
|
|
19
|
+
siteInfo: SiteInfo;
|
|
20
|
+
workingDir: string;
|
|
21
|
+
onCommand?: GitCommandObserver;
|
|
22
|
+
production?: boolean;
|
|
23
|
+
}) => Promise<RemoteSource>;
|
|
24
|
+
export declare const prepareSourceCommit: (source: RemoteSource) => Promise<SourceCommit>;
|
|
25
|
+
//# sourceMappingURL=source.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
import { isNetlifyGitUrl, isSameRepository } from '../../../utils/netlify-git.js';
|
|
3
|
+
import { getCurrentBranch, getGitVersion, getHead, getRemotes, getUpstream, getRemoteBranchesContaining, getWorkTreeStatus, } from './git.js';
|
|
4
|
+
/** A problem the user can fix, as opposed to an unexpected failure. */
|
|
5
|
+
export class RemoteBuildError extends Error {
|
|
6
|
+
name = 'RemoteBuildError';
|
|
7
|
+
}
|
|
8
|
+
const describeGitProblem = (version) => {
|
|
9
|
+
if (!version) {
|
|
10
|
+
return "--remote uses git, which isn't installed or isn't on your PATH. Install git, then deploy again.";
|
|
11
|
+
}
|
|
12
|
+
// 2.13 added `git branch --format`.
|
|
13
|
+
if (semver.lt(version, '2.13.0')) {
|
|
14
|
+
return `--remote needs git 2.13 or later, but you have ${version}. Update git, then deploy again.`;
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
};
|
|
18
|
+
const inspectClone = async (ctx, repoUrl, productionBranch) => {
|
|
19
|
+
const head = await getHead(ctx);
|
|
20
|
+
if (!head) {
|
|
21
|
+
return {
|
|
22
|
+
pushed: false,
|
|
23
|
+
reason: `This directory isn't a clone of ${repoUrl}, which Netlify builds this project from. Run this from a clone of it.`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// Another remote having the commit doesn't mean the provider can build it.
|
|
27
|
+
const providerRemotes = (await getRemotes(ctx))
|
|
28
|
+
.filter(({ url }) => isSameRepository(url, repoUrl))
|
|
29
|
+
.map(({ name }) => name);
|
|
30
|
+
if (providerRemotes.length === 0) {
|
|
31
|
+
return {
|
|
32
|
+
pushed: false,
|
|
33
|
+
reason: `This repository has no remote for ${repoUrl}, which Netlify builds this project from. Run this from a clone of it, or add a remote for it.`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const { hasChanges, untracked } = await getWorkTreeStatus(ctx);
|
|
37
|
+
if (hasChanges) {
|
|
38
|
+
return {
|
|
39
|
+
pushed: false,
|
|
40
|
+
reason: `You have uncommitted changes. Netlify builds this project from ${repoUrl}, so commit them and push them to ${productionBranch} or another branch, then deploy again.`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const remoteOf = (ref) => providerRemotes.find((remote) => ref.startsWith(`${remote}/`));
|
|
44
|
+
const pushedTo = (await getRemoteBranchesContaining(ctx, head)).filter((ref) => remoteOf(ref) !== undefined);
|
|
45
|
+
if (pushedTo.length === 0) {
|
|
46
|
+
return {
|
|
47
|
+
pushed: false,
|
|
48
|
+
reason: `Your checked-out commit isn't on ${repoUrl} as of your last fetch. Push it to ${productionBranch} or another branch (or fetch, if you already pushed it), then deploy again.`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Only a branch the user evidently pushed this commit to, never a guess among other branches that
|
|
52
|
+
// happen to contain it.
|
|
53
|
+
const currentBranch = await getCurrentBranch(ctx);
|
|
54
|
+
const candidates = [
|
|
55
|
+
await getUpstream(ctx),
|
|
56
|
+
...(currentBranch ? providerRemotes.map((remote) => `${remote}/${currentBranch}`) : []),
|
|
57
|
+
];
|
|
58
|
+
const ref = candidates.find((candidate) => pushedTo.includes(candidate));
|
|
59
|
+
return { pushed: true, head, branch: ref?.slice((remoteOf(ref) ?? '').length + 1), untracked };
|
|
60
|
+
};
|
|
61
|
+
export const resolveRemoteSource = async ({ siteInfo, workingDir, onCommand, production = false, }) => {
|
|
62
|
+
const settings = siteInfo.build_settings;
|
|
63
|
+
if (!settings?.repo_url) {
|
|
64
|
+
throw new RemoteBuildError("This project isn't connected to a git repository, so Netlify can't build it. Connect one in the project's build settings, or run netlify deploy without --remote to build locally instead.");
|
|
65
|
+
}
|
|
66
|
+
const productionBranch = settings.repo_branch;
|
|
67
|
+
const ctx = { cwd: workingDir, onCommand };
|
|
68
|
+
if (settings.provider !== 'netlify-git' && !isNetlifyGitUrl(settings.repo_url)) {
|
|
69
|
+
const gitProblem = describeGitProblem(await getGitVersion(ctx));
|
|
70
|
+
if (gitProblem) {
|
|
71
|
+
throw new RemoteBuildError(gitProblem);
|
|
72
|
+
}
|
|
73
|
+
const repoUrl = settings.repo_url;
|
|
74
|
+
const clone = await inspectClone(ctx, repoUrl, productionBranch);
|
|
75
|
+
if (clone.pushed) {
|
|
76
|
+
// Any pushed commit can go to production: the build fetches it by SHA, not from the branch.
|
|
77
|
+
const branch = production ? productionBranch : clone.branch;
|
|
78
|
+
if (!branch) {
|
|
79
|
+
throw new RemoteBuildError(`Couldn't tell which branch to build ${clone.head.slice(0, 7)} as: it isn't on your checked-out branch's upstream or a branch of the same name on ${repoUrl}. Check out the branch you pushed it to, then deploy again.`);
|
|
80
|
+
}
|
|
81
|
+
return { kind: 'external', repoUrl, head: clone.head, branch, untracked: clone.untracked };
|
|
82
|
+
}
|
|
83
|
+
throw new RemoteBuildError(clone.reason);
|
|
84
|
+
}
|
|
85
|
+
throw new RemoteBuildError("Remote builds aren't supported yet for projects on Netlify Git. Run netlify deploy without --remote to build locally instead.");
|
|
86
|
+
};
|
|
87
|
+
export const prepareSourceCommit = (source) => Promise.resolve({ sha: source.head, branch: source.branch });
|
|
88
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NetlifyAPI } from '@netlify/api';
|
|
2
|
+
import { type DeployUrls } from '../../utils/deploy/deploy-urls.js';
|
|
3
|
+
import type { SiteInfo } from '../../utils/types.js';
|
|
4
|
+
import type { DeployOptionValues } from './option_values.js';
|
|
5
|
+
export interface RemoteDeployResults extends DeployUrls {
|
|
6
|
+
siteId: string;
|
|
7
|
+
siteName: string;
|
|
8
|
+
deployId: string;
|
|
9
|
+
commitSha: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const remoteDeploy: ({ api, workingDir, options, siteData, deployToProduction, prepareProductionDeploy, }: {
|
|
12
|
+
api: NetlifyAPI;
|
|
13
|
+
workingDir: string;
|
|
14
|
+
options: DeployOptionValues;
|
|
15
|
+
siteData: SiteInfo;
|
|
16
|
+
deployToProduction: boolean;
|
|
17
|
+
prepareProductionDeploy: () => Promise<void>;
|
|
18
|
+
}) => Promise<RemoteDeployResults>;
|
|
19
|
+
//# sourceMappingURL=remote-deploy.d.ts.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import { TimeoutError } from 'p-wait-for';
|
|
3
|
+
import terminalLink from 'terminal-link';
|
|
4
|
+
import { startSpinner } from '../../lib/spinner.js';
|
|
5
|
+
import { chalk, exit, log, logAndThrowError, NETLIFYDEVLOG, warn } from '../../utils/command-helpers.js';
|
|
6
|
+
import { DEFAULT_DEPLOY_TIMEOUT } from '../../utils/deploy/constants.js';
|
|
7
|
+
import { getDeploySource } from '../../utils/deploy/deploy-source.js';
|
|
8
|
+
import { getDeployUrls } from '../../utils/deploy/deploy-urls.js';
|
|
9
|
+
import { prepareSourceCommit, RemoteBuildError, resolveRemoteSource, } from './remote/source.js';
|
|
10
|
+
import { followDeploy } from './remote/follow.js';
|
|
11
|
+
import { DeployStateError } from '../../utils/deploy/util.js';
|
|
12
|
+
import { isInteractive } from '../../utils/scripted-commands.js';
|
|
13
|
+
const describeSource = (source) => `Checking your commit is on ${source.repoUrl}`;
|
|
14
|
+
const describeMode = (source) => `external (${source.repoUrl}), nothing is pushed`;
|
|
15
|
+
const describeUntracked = (files, sha, repoUrl) => {
|
|
16
|
+
const shown = files.slice(0, 5).join(', ');
|
|
17
|
+
const more = files.length > 5 ? ` and ${String(files.length - 5)} more` : '';
|
|
18
|
+
return `${String(files.length)} untracked ${files.length === 1 ? 'file' : 'files'} won't be in this deploy, since Netlify builds ${sha} from ${repoUrl}: ${shown}${more}`;
|
|
19
|
+
};
|
|
20
|
+
const logGitCommand = (debugLog) => ({ args, cwd, exitCode, stderr }) => {
|
|
21
|
+
debugLog(`git ${args.join(' ')} (in ${cwd}) exited ${String(exitCode)}`);
|
|
22
|
+
if (exitCode !== 0 && stderr) {
|
|
23
|
+
debugLog(stderr);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export const remoteDeploy = async ({ api, workingDir, options, siteData, deployToProduction, prepareProductionDeploy, }) => {
|
|
27
|
+
const quiet = Boolean(options.json || options.silent);
|
|
28
|
+
const siteId = siteData.id;
|
|
29
|
+
const timeoutMs = options.timeout ? options.timeout * 1000 : DEFAULT_DEPLOY_TIMEOUT;
|
|
30
|
+
const debugLog = options.debug
|
|
31
|
+
? (message) => {
|
|
32
|
+
log(`${NETLIFYDEVLOG} ${chalk.dim(message)}`);
|
|
33
|
+
}
|
|
34
|
+
: undefined;
|
|
35
|
+
const onCommand = debugLog && logGitCommand(debugLog);
|
|
36
|
+
let source;
|
|
37
|
+
let commit;
|
|
38
|
+
// Debug output would interleave with the spinner's redraws.
|
|
39
|
+
const spinner = debugLog ? undefined : startSpinner({ text: 'Preparing your source for a remote build' });
|
|
40
|
+
try {
|
|
41
|
+
const { provider, repo_url: repoUrl, repo_branch: productionBranch } = siteData.build_settings ?? {};
|
|
42
|
+
debugLog?.(`Project repository: ${repoUrl ?? '(none)'} (${provider ?? 'no provider'}), production branch ${productionBranch ?? '(none)'}`);
|
|
43
|
+
source = await resolveRemoteSource({
|
|
44
|
+
siteInfo: siteData,
|
|
45
|
+
workingDir,
|
|
46
|
+
onCommand,
|
|
47
|
+
production: deployToProduction,
|
|
48
|
+
});
|
|
49
|
+
debugLog?.(`Mode: ${describeMode(source)}`);
|
|
50
|
+
spinner?.update({ text: describeSource(source) });
|
|
51
|
+
commit = await prepareSourceCommit(source);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
spinner?.error();
|
|
55
|
+
if (error instanceof RemoteBuildError) {
|
|
56
|
+
return logAndThrowError(error.message);
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
const shortSha = commit.sha.slice(0, 7);
|
|
61
|
+
const foundText = `Found ${shortSha} on ${source.repoUrl}`;
|
|
62
|
+
if (spinner) {
|
|
63
|
+
spinner.success({ text: foundText });
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
log(`${chalk.green('✔')} ${foundText}`);
|
|
67
|
+
}
|
|
68
|
+
if (source.untracked.length !== 0) {
|
|
69
|
+
warn(describeUntracked(source.untracked, shortSha, source.repoUrl));
|
|
70
|
+
if (isInteractive() && !quiet && !(await confirm({ message: 'Deploy anyway?', default: true }))) {
|
|
71
|
+
exit(0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// After the checks above, so we never unlock production and then bail.
|
|
75
|
+
if (deployToProduction) {
|
|
76
|
+
await prepareProductionDeploy();
|
|
77
|
+
}
|
|
78
|
+
debugLog?.(`Requesting a ${deployToProduction ? 'production' : 'draft'} build of ${commit.sha} on branch ${commit.branch ?? '(none)'}`);
|
|
79
|
+
let build;
|
|
80
|
+
try {
|
|
81
|
+
build = (await api.createSiteBuild({
|
|
82
|
+
siteId,
|
|
83
|
+
branch: commit.branch,
|
|
84
|
+
title: options.message,
|
|
85
|
+
// @ts-expect-error FIXME(@netlify/api): `createSiteBuild` doesn't declare `commit_sha`, `draft` or `deploy_source`; the client drops undeclared query params, so they go in the body
|
|
86
|
+
body: { commit_sha: commit.sha, draft: !deployToProduction, deploy_source: getDeploySource() },
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
const { json, message } = error;
|
|
91
|
+
return logAndThrowError(`Couldn't start a build of ${commit.sha}: ${json?.message ?? json?.error ?? message}`);
|
|
92
|
+
}
|
|
93
|
+
debugLog?.(`Build ${build.id}, deploy ${build.deploy_id}`);
|
|
94
|
+
const { logsUrl } = getDeployUrls({ id: build.deploy_id, admin_url: siteData.admin_url }, { deployToProduction });
|
|
95
|
+
log(`${NETLIFYDEVLOG} Building ${shortSha} on Netlify (${terminalLink('↗ View deploy logs', logsUrl)})`);
|
|
96
|
+
log('');
|
|
97
|
+
try {
|
|
98
|
+
const deploy = await followDeploy(api, {
|
|
99
|
+
siteId,
|
|
100
|
+
deployId: build.deploy_id,
|
|
101
|
+
timeoutMs,
|
|
102
|
+
onLogLine: quiet
|
|
103
|
+
? undefined
|
|
104
|
+
: (line) => {
|
|
105
|
+
log(`${chalk.dim('│')} ${line.trimEnd()}`);
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
siteId,
|
|
110
|
+
siteName: siteData.name,
|
|
111
|
+
deployId: build.deploy_id,
|
|
112
|
+
commitSha: commit.sha,
|
|
113
|
+
...getDeployUrls(deploy, { deployToProduction }),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error instanceof DeployStateError) {
|
|
118
|
+
return logAndThrowError(`Deploy failed: ${error.message}\n\nDeploy logs: ${logsUrl}`);
|
|
119
|
+
}
|
|
120
|
+
if (error instanceof TimeoutError) {
|
|
121
|
+
return logAndThrowError(`Timed out after ${String(timeoutMs / 1000)} seconds waiting for the deploy to finish. It may still be running on Netlify; to wait longer, pass a larger --timeout.\n\nDeploy logs: ${logsUrl}`);
|
|
122
|
+
}
|
|
123
|
+
return logAndThrowError(`${error.message}\n\nThe build may still be running on Netlify. Deploy logs: ${logsUrl}`);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=remote-deploy.js.map
|
|
@@ -125,7 +125,7 @@ const prepareServer = async ({ aiGatewayContext, command, config, configPath, de
|
|
|
125
125
|
debug,
|
|
126
126
|
distImportMapPath: join(projectDir, distImportMapPath),
|
|
127
127
|
featureFlags,
|
|
128
|
-
formatExportTypeError: (name) => `${NETLIFYDEVERR} ${chalk.red('Failed')} to load Edge Function ${chalk.yellow(name)}. The file does not seem to have a function as the default export.`,
|
|
128
|
+
formatExportTypeError: (name) => `${NETLIFYDEVERR} ${chalk.red('Failed')} to load Edge Function ${chalk.yellow(name)}. The file does not seem to have a function, or an object with a \`fetch\` method, as the default export.`,
|
|
129
129
|
formatImportError: (name) => `${NETLIFYDEVERR} ${chalk.red('Failed')} to run Edge Function ${chalk.yellow(name)}:`,
|
|
130
130
|
inspectSettings,
|
|
131
131
|
port,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import type { Readable } from 'stream';
|
|
3
|
+
export declare class StreamBodyTooBigError extends Error {
|
|
4
|
+
constructor();
|
|
5
|
+
}
|
|
6
|
+
export declare class StreamTimeoutError extends Error {
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
3
9
|
declare const createStreamPromise: (stream: Readable, timeoutSeconds: number, bytesLimit?: number) => Promise<Buffer>;
|
|
4
10
|
export default createStreamPromise;
|
|
5
11
|
//# sourceMappingURL=create-stream-promise.d.ts.map
|
|
@@ -2,6 +2,18 @@ import { Buffer } from 'buffer';
|
|
|
2
2
|
const SEC_TO_MILLISEC = 1e3;
|
|
3
3
|
// 6 MiB
|
|
4
4
|
const DEFAULT_BYTES_LIMIT = 6e6;
|
|
5
|
+
export class StreamBodyTooBigError extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super('Stream body too big');
|
|
8
|
+
this.name = 'StreamBodyTooBigError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class StreamTimeoutError extends Error {
|
|
12
|
+
constructor() {
|
|
13
|
+
super('Request timed out waiting for body');
|
|
14
|
+
this.name = 'StreamTimeoutError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
5
17
|
const createStreamPromise = function (stream, timeoutSeconds, bytesLimit = DEFAULT_BYTES_LIMIT) {
|
|
6
18
|
return new Promise(function streamPromiseFunc(resolve, reject) {
|
|
7
19
|
let data = [];
|
|
@@ -10,7 +22,7 @@ const createStreamPromise = function (stream, timeoutSeconds, bytesLimit = DEFAU
|
|
|
10
22
|
if (timeoutSeconds != null && Number.isFinite(timeoutSeconds)) {
|
|
11
23
|
timeoutId = setTimeout(() => {
|
|
12
24
|
data = null;
|
|
13
|
-
reject(new
|
|
25
|
+
reject(new StreamTimeoutError());
|
|
14
26
|
}, timeoutSeconds * SEC_TO_MILLISEC);
|
|
15
27
|
}
|
|
16
28
|
stream.on('data', function onData(chunk) {
|
|
@@ -21,7 +33,7 @@ const createStreamPromise = function (stream, timeoutSeconds, bytesLimit = DEFAU
|
|
|
21
33
|
dataLength += chunk.length;
|
|
22
34
|
if (dataLength > bytesLimit) {
|
|
23
35
|
data = null;
|
|
24
|
-
reject(new
|
|
36
|
+
reject(new StreamBodyTooBigError());
|
|
25
37
|
}
|
|
26
38
|
else {
|
|
27
39
|
data.push(chunk);
|
|
@@ -3,5 +3,6 @@ export interface DeploySourceFields {
|
|
|
3
3
|
agent_runner_id?: string;
|
|
4
4
|
agent_runner_session_id?: string;
|
|
5
5
|
}
|
|
6
|
+
export declare const getDeploySource: () => string;
|
|
6
7
|
export declare const getDeploySourceFields: () => DeploySourceFields;
|
|
7
8
|
//# sourceMappingURL=deploy-source.d.ts.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
export const getDeploySource = () => process.env.NETLIFY_DEPLOY_SOURCE || 'cli';
|
|
1
2
|
export const getDeploySourceFields = () => {
|
|
2
3
|
const agentRunnerId = process.env.NETLIFY_AGENT_RUNNER_ID;
|
|
3
4
|
const agentRunnerSessionId = process.env.NETLIFY_AGENT_RUNNER_SESSION_ID;
|
|
4
5
|
return {
|
|
5
|
-
deploy_source:
|
|
6
|
+
deploy_source: getDeploySource(),
|
|
6
7
|
...(agentRunnerId ? { agent_runner_id: agentRunnerId } : {}),
|
|
7
8
|
...(agentRunnerSessionId ? { agent_runner_session_id: agentRunnerSessionId } : {}),
|
|
8
9
|
};
|
|
@@ -9,4 +9,31 @@ export const isNetlifyGitUrl = (repoUrl) => {
|
|
|
9
9
|
return false;
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
|
+
// Reduces `https://host/owner/repo.git`, `ssh://git@host/owner/repo` and `git@host:owner/repo.git` to
|
|
13
|
+
// `host/owner/repo`, ignoring credentials, case and a trailing `.git`. Local paths have no host.
|
|
14
|
+
const getRepositoryKey = (repoUrl) => {
|
|
15
|
+
const parse = () => {
|
|
16
|
+
if (repoUrl.startsWith('/')) {
|
|
17
|
+
return { host: '', path: repoUrl };
|
|
18
|
+
}
|
|
19
|
+
const scpLike = /^(?:[^@/]+@)?([^:/]+):(?!\/\/)(.+)$/.exec(repoUrl);
|
|
20
|
+
if (scpLike) {
|
|
21
|
+
return { host: scpLike[1], path: scpLike[2] };
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const { hostname, pathname } = new URL(repoUrl);
|
|
25
|
+
return { host: hostname, path: pathname };
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const parsed = parse();
|
|
32
|
+
const path = parsed?.path.replace(/^\/+|\/+$/g, '').replace(/\.git$/, '');
|
|
33
|
+
return parsed && path ? `${parsed.host}/${path}`.toLowerCase() : undefined;
|
|
34
|
+
};
|
|
35
|
+
export const isSameRepository = (a, b) => {
|
|
36
|
+
const keyA = getRepositoryKey(a);
|
|
37
|
+
return keyA !== undefined && keyA === getRepositoryKey(b);
|
|
38
|
+
};
|
|
12
39
|
//# sourceMappingURL=netlify-git.js.map
|
package/dist/utils/proxy.js
CHANGED
|
@@ -28,8 +28,8 @@ import { fileExistsAsync, isFileAsync } from '../lib/fs.js';
|
|
|
28
28
|
import { getFormHandler } from '../lib/functions/form-submissions-handler.js';
|
|
29
29
|
import { DEFAULT_FUNCTION_URL_EXPRESSION } from '../lib/functions/registry.js';
|
|
30
30
|
import { initializeProxy as initializeImageProxy, isImageRequest } from '../lib/images/proxy.js';
|
|
31
|
-
import { NETLIFYDEVLOG, NETLIFYDEVWARN, chalk, log, logError, warn, } from './command-helpers.js';
|
|
32
|
-
import createStreamPromise from './create-stream-promise.js';
|
|
31
|
+
import { NETLIFYDEVERR, NETLIFYDEVLOG, NETLIFYDEVWARN, chalk, log, logError, warn, } from './command-helpers.js';
|
|
32
|
+
import createStreamPromise, { StreamBodyTooBigError, StreamTimeoutError } from './create-stream-promise.js';
|
|
33
33
|
import { NFFunctionName, NFFunctionRoute, NFRequestID, headersForPath, parseHeaders } from './headers.js';
|
|
34
34
|
import { generateRequestID } from './request-id.js';
|
|
35
35
|
import { createRewriter, onChanges } from './rules-proxy.js';
|
|
@@ -350,6 +350,13 @@ const initializeProxy = async function ({ config, configPath, distDir, env, host
|
|
|
350
350
|
return;
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
|
+
if (res instanceof http.ServerResponse && res.headersSent) {
|
|
354
|
+
if (!res.writableEnded) {
|
|
355
|
+
log(NETLIFYDEVWARN, `Could not finish proxying ${req.url ?? ''}: ${err.message}`);
|
|
356
|
+
res.destroy();
|
|
357
|
+
}
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
353
360
|
if (res instanceof http.ServerResponse) {
|
|
354
361
|
res.writeHead(500, {
|
|
355
362
|
'Content-Type': 'text/plain',
|
|
@@ -547,9 +554,27 @@ const initializeProxy = async function ({ config, configPath, distDir, env, host
|
|
|
547
554
|
};
|
|
548
555
|
return handlers;
|
|
549
556
|
};
|
|
557
|
+
const rejectRequestBody = (req, res, error) => {
|
|
558
|
+
if (!(error instanceof StreamBodyTooBigError || error instanceof StreamTimeoutError)) {
|
|
559
|
+
res.destroy();
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const status = error instanceof StreamBodyTooBigError ? 413 : 408;
|
|
563
|
+
log(NETLIFYDEVWARN, `Responded to ${req.method ?? ''} ${req.url ?? ''} with ${status.toString()}: ${error.message}`);
|
|
564
|
+
res.writeHead(status);
|
|
565
|
+
res.end(http.STATUS_CODES[status]);
|
|
566
|
+
};
|
|
550
567
|
const onRequest = async ({ api, edgeFunctionsProxy, env, functionsRegistry, functionsServer, imageProxy, proxy, rewriter, serverHandler, settings, siteInfo, }, req, res) => {
|
|
551
|
-
|
|
552
|
-
req.
|
|
568
|
+
try {
|
|
569
|
+
req.originalBody =
|
|
570
|
+
req.method && ['GET', 'OPTIONS', 'HEAD'].includes(req.method)
|
|
571
|
+
? null
|
|
572
|
+
: await createStreamPromise(req, REQUEST_BODY_TIMEOUT_SECONDS);
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
rejectRequestBody(req, res, error);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
553
578
|
if (isImageRequest(req)) {
|
|
554
579
|
return imageProxy(req, res);
|
|
555
580
|
}
|
|
@@ -740,7 +765,7 @@ export const startProxy = async function ({ accountId, aiGatewayContext, api, bl
|
|
|
740
765
|
jwtRoleClaim: settings.jwtRolePath,
|
|
741
766
|
projectDir,
|
|
742
767
|
});
|
|
743
|
-
const
|
|
768
|
+
const requestOptions = {
|
|
744
769
|
proxy,
|
|
745
770
|
rewriter,
|
|
746
771
|
serverHandler,
|
|
@@ -752,7 +777,19 @@ export const startProxy = async function ({ accountId, aiGatewayContext, api, bl
|
|
|
752
777
|
siteInfo,
|
|
753
778
|
env,
|
|
754
779
|
api,
|
|
755
|
-
}
|
|
780
|
+
};
|
|
781
|
+
const onRequestWithOptions = (req, res) => {
|
|
782
|
+
onRequest(requestOptions, req, res).catch((error) => {
|
|
783
|
+
console.error(NETLIFYDEVERR, 'Failed to handle request to', req.url, error);
|
|
784
|
+
if (res.headersSent) {
|
|
785
|
+
res.destroy();
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
res.writeHead(500);
|
|
789
|
+
res.end('Could not proxy request.');
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
};
|
|
756
793
|
const primaryServer = settings.https
|
|
757
794
|
? https.createServer({ cert: settings.https.cert, key: settings.https.key }, onRequestWithOptions)
|
|
758
795
|
: http.createServer(onRequestWithOptions);
|
|
@@ -797,5 +834,5 @@ export const startProxy = async function ({ accountId, aiGatewayContext, api, bl
|
|
|
797
834
|
await Promise.all(eventQueue);
|
|
798
835
|
return getProxyUrl(settings);
|
|
799
836
|
};
|
|
800
|
-
const
|
|
837
|
+
const REQUEST_BODY_TIMEOUT_SECONDS = 30;
|
|
801
838
|
//# sourceMappingURL=proxy.js.map
|
package/dist/utils/socketeer.js
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
const getSocketeerUrl = () => {
|
|
2
|
+
// An empty value means unset, as with NETLIFY_API_URL.
|
|
3
|
+
if (process.env.NETLIFY_SOCKETEER_URL) {
|
|
4
|
+
return process.env.NETLIFY_SOCKETEER_URL;
|
|
5
|
+
}
|
|
6
|
+
return 'wss://socketeer.services.netlify.com';
|
|
7
|
+
};
|
|
8
|
+
export const SOCKETEER_URL = getSocketeerUrl();
|
|
2
9
|
//# sourceMappingURL=socketeer.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
|
-
"version": "27.
|
|
3
|
+
"version": "27.16.0",
|
|
4
4
|
"description": "Netlify command line tool",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -55,20 +55,20 @@
|
|
|
55
55
|
"@fastify/static": "^10.0.0",
|
|
56
56
|
"@inquirer/prompts": "^8.7.2",
|
|
57
57
|
"@netlify/ai": "^1.0.2",
|
|
58
|
-
"@netlify/api": "^15.1.
|
|
58
|
+
"@netlify/api": "^15.1.4",
|
|
59
59
|
"@netlify/blobs": "^11.1.6",
|
|
60
|
-
"@netlify/build": "^37.7.
|
|
61
|
-
"@netlify/build-info": "^11.3.
|
|
60
|
+
"@netlify/build": "^37.7.1",
|
|
61
|
+
"@netlify/build-info": "^11.3.1",
|
|
62
62
|
"@netlify/config": "^25.5.0",
|
|
63
63
|
"@netlify/dev": "^5.1.10",
|
|
64
64
|
"@netlify/dev-utils": "^6.0.4",
|
|
65
|
-
"@netlify/edge-bundler": "^16.
|
|
65
|
+
"@netlify/edge-bundler": "^16.2.0",
|
|
66
66
|
"@netlify/edge-functions": "^4.0.3",
|
|
67
67
|
"@netlify/edge-functions-bootstrap": "^3.2.0",
|
|
68
68
|
"@netlify/headers-parser": "^10.1.1",
|
|
69
69
|
"@netlify/images": "^2.0.5",
|
|
70
70
|
"@netlify/local-functions-proxy": "^2.0.3",
|
|
71
|
-
"@netlify/redirect-matcher": "^0.
|
|
71
|
+
"@netlify/redirect-matcher": "^0.7.0",
|
|
72
72
|
"@netlify/redirect-parser": "^16.1.1",
|
|
73
73
|
"@netlify/server-dev": "^0.1.4",
|
|
74
74
|
"@netlify/zip-it-and-ship-it": "^16.6.0",
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
},
|
|
150
150
|
"devDependencies": {
|
|
151
151
|
"@babel/preset-react": "^7.27.1",
|
|
152
|
-
"@bugsnag/js": "^
|
|
152
|
+
"@bugsnag/js": "^9.0.0",
|
|
153
153
|
"@eslint/compat": "^1.4.1",
|
|
154
154
|
"@eslint/js": "^9.36.0",
|
|
155
155
|
"@netlify/functions": "^6.0.3",
|