netlify 27.16.2 → 27.16.3
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.
|
@@ -66,7 +66,7 @@ export default class BaseCommand extends Command {
|
|
|
66
66
|
/** Will be called on the end of an action to track the metrics */
|
|
67
67
|
onEnd(error_?: unknown): Promise<undefined>;
|
|
68
68
|
authenticate(tokenFromFlag?: string): Promise<string>;
|
|
69
|
-
|
|
69
|
+
refreshAccounts(): Promise<void>;
|
|
70
70
|
expensivelyAuthenticate(): Promise<string>;
|
|
71
71
|
/** Adds some data to the analytics payload */
|
|
72
72
|
setAnalyticsPayload(payload: Record<string, unknown>): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { confirm, select } from '@inquirer/prompts';
|
|
2
2
|
import { isEmpty } from '../../utils/object-utilities.js';
|
|
3
|
-
import { chalk, exit, log, netlifyCommand } from '../../utils/command-helpers.js';
|
|
3
|
+
import { chalk, exit, log, logAndThrowError, netlifyCommand, warn } from '../../utils/command-helpers.js';
|
|
4
4
|
import getRepoData from '../../utils/get-repo-data.js';
|
|
5
5
|
import { ensureNetlifyIgnore } from '../../utils/gitignore.js';
|
|
6
6
|
import { configureRepo } from '../../utils/init/config.js';
|
|
@@ -30,6 +30,71 @@ const logExistingAndExit = ({ siteInfo }) => {
|
|
|
30
30
|
log(`2. Then run ${chalk.cyanBright.bold(`${netlifyCommand()} init`)} again`);
|
|
31
31
|
return exit();
|
|
32
32
|
};
|
|
33
|
+
const logLinkedWithoutGitAndExit = ({ siteInfo, repoError, }) => {
|
|
34
|
+
if (repoError.reason !== 'no-remote') {
|
|
35
|
+
warn(`Could not read the git remote: ${repoError.error}`);
|
|
36
|
+
log(`Fix the git remote, then run ${chalk.cyanBright.bold(`${netlifyCommand()} init`)} again to connect it.`);
|
|
37
|
+
}
|
|
38
|
+
log();
|
|
39
|
+
log(`This folder is already linked to the Netlify project ${chalk.cyan(siteInfo.name)}`);
|
|
40
|
+
log();
|
|
41
|
+
log(`Project Url: ${chalk.cyan(siteInfo.ssl_url || siteInfo.url)}`);
|
|
42
|
+
log();
|
|
43
|
+
log(`To deploy to this project, run ${chalk.cyanBright.bold(`${netlifyCommand()} deploy`)}.`);
|
|
44
|
+
log(`To link this folder to a different project, run ${chalk.cyanBright.bold(`${netlifyCommand()} unlink`)}, then ${chalk.cyanBright.bold(`${netlifyCommand()} init`)}.`);
|
|
45
|
+
return exit();
|
|
46
|
+
};
|
|
47
|
+
const ignoreLinkedSiteForThisRun = (command) => {
|
|
48
|
+
const { site } = command.netlify;
|
|
49
|
+
command.siteId = undefined;
|
|
50
|
+
command.netlify.siteInfo = {};
|
|
51
|
+
command.netlify.site = {
|
|
52
|
+
root: site.root,
|
|
53
|
+
configPath: site.configPath,
|
|
54
|
+
get id() {
|
|
55
|
+
return command.siteId;
|
|
56
|
+
},
|
|
57
|
+
set id(id) {
|
|
58
|
+
site.id = id;
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
const getLinkedSiteInfo = async (command) => {
|
|
63
|
+
const { api, siteInfo } = command.netlify;
|
|
64
|
+
if (isEmpty(siteInfo)) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
if (siteInfo.name) {
|
|
68
|
+
return siteInfo;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
return (await api.getSite({ siteId: siteInfo.id }));
|
|
72
|
+
}
|
|
73
|
+
catch (error_) {
|
|
74
|
+
const { status, message } = error_;
|
|
75
|
+
const unusableLinks = {
|
|
76
|
+
403: {
|
|
77
|
+
reason: 'forbidden',
|
|
78
|
+
warning: `You don't have access to the Netlify project ${siteInfo.id} linked to this folder. Continuing without it.`,
|
|
79
|
+
},
|
|
80
|
+
404: {
|
|
81
|
+
reason: 'not_found',
|
|
82
|
+
warning: `The Netlify project ${siteInfo.id} linked to this folder no longer exists. Continuing without it.`,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
const unusableLink = unusableLinks[status];
|
|
86
|
+
if (unusableLink === undefined) {
|
|
87
|
+
return logAndThrowError(`Could not load the Netlify project ${siteInfo.id} linked to this folder (${status ? `status ${status.toString()}` : message}). Try again, or run ${chalk.cyanBright.bold(`${netlifyCommand()} unlink`)} to start over.`);
|
|
88
|
+
}
|
|
89
|
+
warn(unusableLink.warning);
|
|
90
|
+
await track('sites_unlinked', { siteId: siteInfo.id, kind: 'init', reason: unusableLink.reason });
|
|
91
|
+
ignoreLinkedSiteForThisRun(command);
|
|
92
|
+
if (command.netlify.accounts.length === 0) {
|
|
93
|
+
await command.refreshAccounts();
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
33
98
|
/**
|
|
34
99
|
* Creates and new site and exits the process
|
|
35
100
|
*/
|
|
@@ -167,7 +232,6 @@ export const init = async (options, command, { customizeExitMessage, exitAfterCo
|
|
|
167
232
|
resetContext: options.resetContext,
|
|
168
233
|
});
|
|
169
234
|
const { repositoryRoot, state } = command.netlify;
|
|
170
|
-
const { siteInfo: existingSiteInfo } = command.netlify;
|
|
171
235
|
// Check logged in status
|
|
172
236
|
await command.authenticate();
|
|
173
237
|
// Add .netlify to .gitignore file
|
|
@@ -175,13 +239,16 @@ export const init = async (options, command, { customizeExitMessage, exitAfterCo
|
|
|
175
239
|
if (shouldSetupAgentSkills) {
|
|
176
240
|
await installAgentSkills(command, resetContext);
|
|
177
241
|
}
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
180
|
-
logExistingAndExit({ siteInfo:
|
|
242
|
+
const linkedSiteInfo = await getLinkedSiteInfo(command);
|
|
243
|
+
if (linkedSiteInfo && getRepoUrl(linkedSiteInfo) && !options.force) {
|
|
244
|
+
logExistingAndExit({ siteInfo: linkedSiteInfo });
|
|
181
245
|
}
|
|
182
246
|
// Look for local repo
|
|
183
247
|
const repoData = await getRepoData({ workingDir: command.workingDir, remoteName: options.gitRemoteName });
|
|
184
248
|
if ('error' in repoData) {
|
|
249
|
+
if (linkedSiteInfo) {
|
|
250
|
+
return logLinkedWithoutGitAndExit({ siteInfo: linkedSiteInfo, repoError: repoData });
|
|
251
|
+
}
|
|
185
252
|
// TODO(ndhoule): Custom error messaage here
|
|
186
253
|
return await handleNoGitRemoteAndExit({
|
|
187
254
|
command,
|
|
@@ -191,7 +258,7 @@ export const init = async (options, command, { customizeExitMessage, exitAfterCo
|
|
|
191
258
|
customizeExitMessage,
|
|
192
259
|
});
|
|
193
260
|
}
|
|
194
|
-
const siteInfo =
|
|
261
|
+
const siteInfo = linkedSiteInfo ?? (await createOrLinkSiteToRepo(command));
|
|
195
262
|
log();
|
|
196
263
|
// Check for existing CI setup
|
|
197
264
|
const remoteBuildRepo = getRepoUrl(siteInfo);
|
|
@@ -7,11 +7,14 @@ export interface RepoData {
|
|
|
7
7
|
provider: string | null;
|
|
8
8
|
httpsUrl: string;
|
|
9
9
|
}
|
|
10
|
+
export type RepoDataErrorReason = 'no-remote' | 'unknown-remote' | 'invalid-url' | 'unreadable';
|
|
11
|
+
export interface RepoDataError {
|
|
12
|
+
error: string;
|
|
13
|
+
reason: RepoDataErrorReason;
|
|
14
|
+
}
|
|
10
15
|
declare const getRepoData: ({ remoteName, workingDir, }: {
|
|
11
16
|
remoteName?: string;
|
|
12
17
|
workingDir: string;
|
|
13
|
-
}) => Promise<RepoData |
|
|
14
|
-
error: string;
|
|
15
|
-
}>;
|
|
18
|
+
}) => Promise<RepoData | RepoDataError>;
|
|
16
19
|
export default getRepoData;
|
|
17
20
|
//# sourceMappingURL=get-repo-data.d.ts.map
|
|
@@ -5,14 +5,23 @@ import gitRepoInfo from 'git-repo-info';
|
|
|
5
5
|
import gitconfiglocal from 'gitconfiglocal';
|
|
6
6
|
import parseGithubUrl from 'parse-github-url';
|
|
7
7
|
import { log } from './command-helpers.js';
|
|
8
|
+
class RepoDataLookupError extends Error {
|
|
9
|
+
reason;
|
|
10
|
+
constructor(message, reason) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.reason = reason;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const redactCredentials = (url) => url.replace(/\/\/[^/@]*@/, '//***@');
|
|
8
16
|
const getRepoData = async ({ remoteName, workingDir, }) => {
|
|
9
17
|
try {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
const gitDirectory = await findUp('.git', { cwd: workingDir, type: 'directory' });
|
|
19
|
+
if (!gitDirectory) {
|
|
20
|
+
throw new RepoDataLookupError('No Git remote found', 'no-remote');
|
|
21
|
+
}
|
|
22
|
+
const gitConfig = await util.promisify(gitconfiglocal)(workingDir);
|
|
23
|
+
if (!gitConfig?.remote || Object.keys(gitConfig.remote).length === 0) {
|
|
24
|
+
throw new RepoDataLookupError('No Git remote found', 'no-remote');
|
|
16
25
|
}
|
|
17
26
|
const baseGitPath = dirname(gitDirectory);
|
|
18
27
|
if (workingDir !== baseGitPath) {
|
|
@@ -25,13 +34,13 @@ const getRepoData = async ({ remoteName, workingDir, }) => {
|
|
|
25
34
|
if (!Object.prototype.hasOwnProperty.call(gitConfig.remote, remoteName) ||
|
|
26
35
|
!gitConfig.remote[remoteName] ||
|
|
27
36
|
Object.keys(gitConfig.remote[remoteName]).length === 0) {
|
|
28
|
-
throw new
|
|
37
|
+
throw new RepoDataLookupError(`The specified remote "${remoteName}" is not defined in Git repo. Please use --git-remote-name flag to specify a remote.`, 'unknown-remote');
|
|
29
38
|
}
|
|
30
39
|
const { url } = gitConfig.remote[remoteName];
|
|
31
40
|
const parsedUrl = parseGithubUrl(url);
|
|
32
41
|
// TODO(serhalp): Validate more aggressively? We should probably require `owner`, `repo`, `host`?
|
|
33
42
|
if (parsedUrl == null) {
|
|
34
|
-
throw new
|
|
43
|
+
throw new RepoDataLookupError(`The specified Git remote ${remoteName} is not a valid URL: ${redactCredentials(String(url))}`, 'invalid-url');
|
|
35
44
|
}
|
|
36
45
|
const { host, name, owner, repo } = parsedUrl;
|
|
37
46
|
const { branch } = gitRepoInfo();
|
|
@@ -48,6 +57,7 @@ const getRepoData = async ({ remoteName, workingDir, }) => {
|
|
|
48
57
|
catch (error) {
|
|
49
58
|
return {
|
|
50
59
|
error: error instanceof Error ? error.message : (error?.toString() ?? 'Failed to get repo data'),
|
|
60
|
+
reason: error instanceof RepoDataLookupError ? error.reason : 'unreadable',
|
|
51
61
|
};
|
|
52
62
|
}
|
|
53
63
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify",
|
|
3
|
-
"version": "27.16.
|
|
3
|
+
"version": "27.16.3",
|
|
4
4
|
"description": "Netlify command line tool",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -54,11 +54,11 @@
|
|
|
54
54
|
"@fastify/static": "^10.0.0",
|
|
55
55
|
"@inquirer/prompts": "^8.7.2",
|
|
56
56
|
"@netlify/ai": "^1.0.2",
|
|
57
|
-
"@netlify/api": "^15.1.
|
|
57
|
+
"@netlify/api": "^15.1.6",
|
|
58
58
|
"@netlify/blobs": "^11.1.7",
|
|
59
|
-
"@netlify/build": "^37.7.
|
|
59
|
+
"@netlify/build": "^37.7.6",
|
|
60
60
|
"@netlify/build-info": "^11.3.1",
|
|
61
|
-
"@netlify/config": "^25.
|
|
61
|
+
"@netlify/config": "^25.6.2",
|
|
62
62
|
"@netlify/dev": "^5.1.12",
|
|
63
63
|
"@netlify/dev-utils": "^6.0.4",
|
|
64
64
|
"@netlify/edge-bundler": "^16.2.0",
|