firebase-tools 13.22.1 → 13.23.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/apiv2.js +2 -2
- package/lib/apphosting/backend.js +294 -0
- package/lib/apphosting/githubConnections.js +54 -23
- package/lib/apphosting/index.js +3 -260
- package/lib/apphosting/rollout.js +12 -9
- package/lib/bin/firebase.js +2 -2
- package/lib/commands/apphosting-backends-create.js +4 -4
- package/lib/commands/apphosting-backends-delete.js +4 -14
- package/lib/commands/apphosting-repos-create.js +23 -0
- package/lib/commands/apphosting-rollouts-create.js +3 -4
- package/lib/commands/apphosting-secrets-grantaccess.js +2 -2
- package/lib/commands/dataconnect-sql-shell.js +111 -0
- package/lib/commands/index.js +3 -0
- package/lib/commands/init.js +6 -6
- package/lib/dataconnect/schemaMigration.js +2 -1
- package/lib/deploy/functions/services/auth.js +40 -15
- package/lib/deploy/functions/services/index.js +2 -0
- package/lib/emulator/commandUtils.js +2 -2
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/emulator/functionsEmulator.js +0 -2
- package/lib/emulator/loggingEmulator.js +2 -2
- package/lib/firestore/delete.js +11 -0
- package/lib/frameworks/next/index.js +9 -1
- package/lib/frameworks/next/utils.js +7 -4
- package/lib/frameworks/vite/index.js +2 -2
- package/lib/functions/constants.js +4 -0
- package/lib/functions/events/v1.js +9 -2
- package/lib/gcp/apphosting.js +1 -1
- package/lib/gcp/cloudsql/interactive.js +49 -0
- package/lib/gcp/firestore.js +5 -1
- package/lib/init/features/apphosting.js +3 -0
- package/lib/responseToError.js +5 -2
- package/lib/utils.js +2 -2
- package/package.json +1 -2
package/lib/apiv2.js
CHANGED
|
@@ -303,13 +303,13 @@ class Client {
|
|
|
303
303
|
setAccessToken(await getAccessToken());
|
|
304
304
|
}
|
|
305
305
|
if ((_a = options.retryCodes) === null || _a === void 0 ? void 0 : _a.includes(res.status)) {
|
|
306
|
-
const err = (0, responseToError_1.responseToError)({ statusCode: res.status }, body) || undefined;
|
|
306
|
+
const err = (0, responseToError_1.responseToError)({ statusCode: res.status }, body, fetchURL) || undefined;
|
|
307
307
|
if (operation.retry(err)) {
|
|
308
308
|
return;
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
if (!options.resolveOnHTTPError) {
|
|
312
|
-
return reject((0, responseToError_1.responseToError)({ statusCode: res.status }, body));
|
|
312
|
+
return reject((0, responseToError_1.responseToError)({ statusCode: res.status }, body, fetchURL));
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
resolve({
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getBackendForAmbiguousLocation = exports.getBackendForLocation = exports.promptLocation = exports.deleteBackendAndPoll = exports.setDefaultTrafficPolicy = exports.createBackend = exports.ensureAppHostingComputeServiceAccount = exports.createGitRepoLink = exports.doSetup = void 0;
|
|
4
|
+
const clc = require("colorette");
|
|
5
|
+
const poller = require("../operation-poller");
|
|
6
|
+
const apphosting = require("../gcp/apphosting");
|
|
7
|
+
const githubConnections = require("./githubConnections");
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
const api_1 = require("../api");
|
|
10
|
+
const apphosting_1 = require("../gcp/apphosting");
|
|
11
|
+
const resourceManager_1 = require("../gcp/resourceManager");
|
|
12
|
+
const iam = require("../gcp/iam");
|
|
13
|
+
const error_1 = require("../error");
|
|
14
|
+
const prompt_1 = require("../prompt");
|
|
15
|
+
const constants_1 = require("./constants");
|
|
16
|
+
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
17
|
+
const deploymentTool = require("../deploymentTool");
|
|
18
|
+
const app_1 = require("./app");
|
|
19
|
+
const ora = require("ora");
|
|
20
|
+
const node_fetch_1 = require("node-fetch");
|
|
21
|
+
const rollout_1 = require("./rollout");
|
|
22
|
+
const DEFAULT_COMPUTE_SERVICE_ACCOUNT_NAME = "firebase-app-hosting-compute";
|
|
23
|
+
const apphostingPollerOptions = {
|
|
24
|
+
apiOrigin: (0, api_1.apphostingOrigin)(),
|
|
25
|
+
apiVersion: apphosting_1.API_VERSION,
|
|
26
|
+
masterTimeout: 25 * 60 * 1000,
|
|
27
|
+
maxBackoff: 10000,
|
|
28
|
+
};
|
|
29
|
+
async function tlsReady(url) {
|
|
30
|
+
var _a;
|
|
31
|
+
try {
|
|
32
|
+
await (0, node_fetch_1.default)(url);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const maybeNodeError = err;
|
|
37
|
+
if (/HANDSHAKE_FAILURE/.test((_a = maybeNodeError === null || maybeNodeError === void 0 ? void 0 : maybeNodeError.cause) === null || _a === void 0 ? void 0 : _a.code) ||
|
|
38
|
+
"EPROTO" === (maybeNodeError === null || maybeNodeError === void 0 ? void 0 : maybeNodeError.code)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function awaitTlsReady(url) {
|
|
45
|
+
let ready;
|
|
46
|
+
do {
|
|
47
|
+
ready = await tlsReady(url);
|
|
48
|
+
if (!ready) {
|
|
49
|
+
await (0, utils_1.sleep)(1000);
|
|
50
|
+
}
|
|
51
|
+
} while (!ready);
|
|
52
|
+
}
|
|
53
|
+
async function doSetup(projectId, webAppName, location, serviceAccount) {
|
|
54
|
+
await Promise.all([
|
|
55
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.developerConnectOrigin)(), "apphosting", true),
|
|
56
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.cloudbuildOrigin)(), "apphosting", true),
|
|
57
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.secretManagerOrigin)(), "apphosting", true),
|
|
58
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.cloudRunApiOrigin)(), "apphosting", true),
|
|
59
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.artifactRegistryDomain)(), "apphosting", true),
|
|
60
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.iamOrigin)(), "apphosting", true),
|
|
61
|
+
]);
|
|
62
|
+
await ensureAppHostingComputeServiceAccount(projectId, serviceAccount);
|
|
63
|
+
const allowedLocations = (await apphosting.listLocations(projectId)).map((loc) => loc.locationId);
|
|
64
|
+
if (location) {
|
|
65
|
+
if (!allowedLocations.includes(location)) {
|
|
66
|
+
throw new error_1.FirebaseError(`Invalid location ${location}. Valid choices are ${allowedLocations.join(", ")}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
location =
|
|
70
|
+
location || (await promptLocation(projectId, "Select a location to host your backend:\n"));
|
|
71
|
+
const gitRepositoryLink = await githubConnections.linkGitHubRepository(projectId, location);
|
|
72
|
+
const rootDir = await (0, prompt_1.promptOnce)({
|
|
73
|
+
name: "rootDir",
|
|
74
|
+
type: "input",
|
|
75
|
+
default: "/",
|
|
76
|
+
message: "Specify your app's root directory relative to your repository",
|
|
77
|
+
});
|
|
78
|
+
const branch = await githubConnections.promptGitHubBranch(gitRepositoryLink);
|
|
79
|
+
(0, utils_1.logSuccess)(`Repo linked successfully!\n`);
|
|
80
|
+
(0, utils_1.logBullet)(`${clc.yellow("===")} Set up your backend`);
|
|
81
|
+
const backendId = await promptNewBackendId(projectId, location, {
|
|
82
|
+
name: "backendId",
|
|
83
|
+
type: "input",
|
|
84
|
+
default: "my-web-app",
|
|
85
|
+
message: "Provide a name for your backend [1-30 characters]",
|
|
86
|
+
});
|
|
87
|
+
(0, utils_1.logSuccess)(`Name set to ${backendId}\n`);
|
|
88
|
+
const webApp = await app_1.webApps.getOrCreateWebApp(projectId, webAppName, backendId);
|
|
89
|
+
if (!webApp) {
|
|
90
|
+
(0, utils_1.logWarning)(`Firebase web app not set`);
|
|
91
|
+
}
|
|
92
|
+
const createBackendSpinner = ora("Creating your new backend...").start();
|
|
93
|
+
const backend = await createBackend(projectId, location, backendId, gitRepositoryLink, serviceAccount, webApp === null || webApp === void 0 ? void 0 : webApp.id, rootDir);
|
|
94
|
+
createBackendSpinner.succeed(`Successfully created backend!\n\t${backend.name}\n`);
|
|
95
|
+
await setDefaultTrafficPolicy(projectId, location, backendId, branch);
|
|
96
|
+
const confirmRollout = await (0, prompt_1.promptOnce)({
|
|
97
|
+
type: "confirm",
|
|
98
|
+
name: "rollout",
|
|
99
|
+
default: true,
|
|
100
|
+
message: "Do you want to deploy now?",
|
|
101
|
+
});
|
|
102
|
+
if (!confirmRollout) {
|
|
103
|
+
(0, utils_1.logSuccess)(`Your backend will be deployed at:\n\thttps://${backend.uri}`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const url = `https://${backend.uri}`;
|
|
107
|
+
(0, utils_1.logBullet)(`You may also track this rollout at:\n\t${(0, api_1.consoleOrigin)()}/project/${projectId}/apphosting`);
|
|
108
|
+
const createRolloutSpinner = ora("Starting a new rollout; this may take a few minutes. It's safe to exit now.").start();
|
|
109
|
+
await (0, rollout_1.orchestrateRollout)({
|
|
110
|
+
projectId,
|
|
111
|
+
location,
|
|
112
|
+
backendId,
|
|
113
|
+
buildInput: {
|
|
114
|
+
source: {
|
|
115
|
+
codebase: {
|
|
116
|
+
branch,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
isFirstRollout: true,
|
|
121
|
+
});
|
|
122
|
+
createRolloutSpinner.succeed("Rollout complete");
|
|
123
|
+
if (!(await tlsReady(url))) {
|
|
124
|
+
const tlsSpinner = ora("Finalizing your backend's TLS certificate; this may take a few minutes.").start();
|
|
125
|
+
await awaitTlsReady(url);
|
|
126
|
+
tlsSpinner.succeed("TLS certificate ready");
|
|
127
|
+
}
|
|
128
|
+
(0, utils_1.logSuccess)(`Your backend is now deployed at:\n\thttps://${backend.uri}`);
|
|
129
|
+
}
|
|
130
|
+
exports.doSetup = doSetup;
|
|
131
|
+
async function createGitRepoLink(projectId, location, connectionId) {
|
|
132
|
+
await Promise.all([
|
|
133
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.developerConnectOrigin)(), "apphosting", true),
|
|
134
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.secretManagerOrigin)(), "apphosting", true),
|
|
135
|
+
(0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.iamOrigin)(), "apphosting", true),
|
|
136
|
+
]);
|
|
137
|
+
const allowedLocations = (await apphosting.listLocations(projectId)).map((loc) => loc.locationId);
|
|
138
|
+
if (location) {
|
|
139
|
+
if (!allowedLocations.includes(location)) {
|
|
140
|
+
throw new error_1.FirebaseError(`Invalid location ${location}. Valid choices are ${allowedLocations.join(", ")}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
location =
|
|
144
|
+
location ||
|
|
145
|
+
(await promptLocation(projectId, "Select a location for your GitRepoLink's connection:\n"));
|
|
146
|
+
await githubConnections.linkGitHubRepository(projectId, location, connectionId);
|
|
147
|
+
}
|
|
148
|
+
exports.createGitRepoLink = createGitRepoLink;
|
|
149
|
+
async function ensureAppHostingComputeServiceAccount(projectId, serviceAccount) {
|
|
150
|
+
const sa = serviceAccount || defaultComputeServiceAccountEmail(projectId);
|
|
151
|
+
const name = `projects/${projectId}/serviceAccounts/${sa}`;
|
|
152
|
+
try {
|
|
153
|
+
await iam.testResourceIamPermissions((0, api_1.iamOrigin)(), "v1", name, ["iam.serviceAccounts.actAs"], `projects/${projectId}`);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
if (!(err instanceof error_1.FirebaseError)) {
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
if (err.status === 404) {
|
|
160
|
+
await provisionDefaultComputeServiceAccount(projectId);
|
|
161
|
+
}
|
|
162
|
+
else if (err.status === 403) {
|
|
163
|
+
throw new error_1.FirebaseError(`Failed to create backend due to missing delegation permissions for ${sa}. Make sure you have the iam.serviceAccounts.actAs permission.`, { original: err });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.ensureAppHostingComputeServiceAccount = ensureAppHostingComputeServiceAccount;
|
|
168
|
+
async function promptNewBackendId(projectId, location, prompt) {
|
|
169
|
+
while (true) {
|
|
170
|
+
const backendId = await (0, prompt_1.promptOnce)(prompt);
|
|
171
|
+
try {
|
|
172
|
+
await apphosting.getBackend(projectId, location, backendId);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
if (err.status === 404) {
|
|
176
|
+
return backendId;
|
|
177
|
+
}
|
|
178
|
+
throw new error_1.FirebaseError(`Failed to check if backend with id ${backendId} already exists in ${location}`, { original: err });
|
|
179
|
+
}
|
|
180
|
+
(0, utils_1.logWarning)(`Backend with id ${backendId} already exists in ${location}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function defaultComputeServiceAccountEmail(projectId) {
|
|
184
|
+
return `${DEFAULT_COMPUTE_SERVICE_ACCOUNT_NAME}@${projectId}.iam.gserviceaccount.com`;
|
|
185
|
+
}
|
|
186
|
+
async function createBackend(projectId, location, backendId, repository, serviceAccount, webAppId, rootDir = "/") {
|
|
187
|
+
const defaultServiceAccount = defaultComputeServiceAccountEmail(projectId);
|
|
188
|
+
const backendReqBody = {
|
|
189
|
+
servingLocality: "GLOBAL_ACCESS",
|
|
190
|
+
codebase: {
|
|
191
|
+
repository: `${repository.name}`,
|
|
192
|
+
rootDirectory: rootDir,
|
|
193
|
+
},
|
|
194
|
+
labels: deploymentTool.labels(),
|
|
195
|
+
serviceAccount: serviceAccount || defaultServiceAccount,
|
|
196
|
+
appId: webAppId,
|
|
197
|
+
};
|
|
198
|
+
async function createBackendAndPoll() {
|
|
199
|
+
const op = await apphosting.createBackend(projectId, location, backendReqBody, backendId);
|
|
200
|
+
return await poller.pollOperation(Object.assign(Object.assign({}, apphostingPollerOptions), { pollerName: `create-${projectId}-${location}-${backendId}`, operationResourceName: op.name }));
|
|
201
|
+
}
|
|
202
|
+
return await createBackendAndPoll();
|
|
203
|
+
}
|
|
204
|
+
exports.createBackend = createBackend;
|
|
205
|
+
async function provisionDefaultComputeServiceAccount(projectId) {
|
|
206
|
+
try {
|
|
207
|
+
await iam.createServiceAccount(projectId, DEFAULT_COMPUTE_SERVICE_ACCOUNT_NAME, "Default service account used to run builds and deploys for Firebase App Hosting", "Firebase App Hosting compute service account");
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
if (err.status !== 409) {
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
await (0, resourceManager_1.addServiceAccountToRoles)(projectId, defaultComputeServiceAccountEmail(projectId), [
|
|
215
|
+
"roles/firebaseapphosting.computeRunner",
|
|
216
|
+
"roles/firebase.sdkAdminServiceAgent",
|
|
217
|
+
"roles/developerconnect.readTokenAccessor",
|
|
218
|
+
], true);
|
|
219
|
+
}
|
|
220
|
+
async function setDefaultTrafficPolicy(projectId, location, backendId, codebaseBranch) {
|
|
221
|
+
const traffic = {
|
|
222
|
+
rolloutPolicy: {
|
|
223
|
+
codebaseBranch: codebaseBranch,
|
|
224
|
+
stages: [
|
|
225
|
+
{
|
|
226
|
+
progression: "IMMEDIATE",
|
|
227
|
+
targetPercent: 100,
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
const op = await apphosting.updateTraffic(projectId, location, backendId, traffic);
|
|
233
|
+
await poller.pollOperation(Object.assign(Object.assign({}, apphostingPollerOptions), { pollerName: `updateTraffic-${projectId}-${location}-${backendId}`, operationResourceName: op.name }));
|
|
234
|
+
}
|
|
235
|
+
exports.setDefaultTrafficPolicy = setDefaultTrafficPolicy;
|
|
236
|
+
async function deleteBackendAndPoll(projectId, location, backendId) {
|
|
237
|
+
const op = await apphosting.deleteBackend(projectId, location, backendId);
|
|
238
|
+
await poller.pollOperation(Object.assign(Object.assign({}, apphostingPollerOptions), { pollerName: `delete-${projectId}-${location}-${backendId}`, operationResourceName: op.name }));
|
|
239
|
+
}
|
|
240
|
+
exports.deleteBackendAndPoll = deleteBackendAndPoll;
|
|
241
|
+
async function promptLocation(projectId, prompt = "Please select a location:") {
|
|
242
|
+
const allowedLocations = (await apphosting.listLocations(projectId)).map((loc) => loc.locationId);
|
|
243
|
+
if (allowedLocations.length === 1) {
|
|
244
|
+
return allowedLocations[0];
|
|
245
|
+
}
|
|
246
|
+
const location = (await (0, prompt_1.promptOnce)({
|
|
247
|
+
name: "location",
|
|
248
|
+
type: "list",
|
|
249
|
+
default: constants_1.DEFAULT_LOCATION,
|
|
250
|
+
message: prompt,
|
|
251
|
+
choices: allowedLocations,
|
|
252
|
+
}));
|
|
253
|
+
(0, utils_1.logSuccess)(`Location set to ${location}.\n`);
|
|
254
|
+
return location;
|
|
255
|
+
}
|
|
256
|
+
exports.promptLocation = promptLocation;
|
|
257
|
+
async function getBackendForLocation(projectId, location, backendId) {
|
|
258
|
+
try {
|
|
259
|
+
return await apphosting.getBackend(projectId, location, backendId);
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
throw new error_1.FirebaseError(`No backend named "${backendId}" found in ${location}.`, {
|
|
263
|
+
original: err,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.getBackendForLocation = getBackendForLocation;
|
|
268
|
+
async function getBackendForAmbiguousLocation(projectId, backendId, locationDisambugationPrompt, force) {
|
|
269
|
+
let { unreachable, backends } = await apphosting.listBackends(projectId, "-");
|
|
270
|
+
if (unreachable && unreachable.length !== 0) {
|
|
271
|
+
(0, utils_1.logWarning)(`The following locations are currently unreachable: ${unreachable}.\n` +
|
|
272
|
+
"If your backend is in one of these regions, please try again later.");
|
|
273
|
+
}
|
|
274
|
+
backends = backends.filter((backend) => apphosting.parseBackendName(backend.name).id === backendId);
|
|
275
|
+
if (backends.length === 0) {
|
|
276
|
+
throw new error_1.FirebaseError(`No backend named "${backendId}" found.`);
|
|
277
|
+
}
|
|
278
|
+
if (backends.length === 1) {
|
|
279
|
+
return backends[0];
|
|
280
|
+
}
|
|
281
|
+
if (force) {
|
|
282
|
+
throw new error_1.FirebaseError(`Multiple backends found with ID ${backendId}. Please specify the region of your target backend.`);
|
|
283
|
+
}
|
|
284
|
+
const backendsByLocation = new Map();
|
|
285
|
+
backends.forEach((backend) => backendsByLocation.set(apphosting.parseBackendName(backend.name).location, backend));
|
|
286
|
+
const location = await (0, prompt_1.promptOnce)({
|
|
287
|
+
name: "location",
|
|
288
|
+
type: "list",
|
|
289
|
+
message: locationDisambugationPrompt,
|
|
290
|
+
choices: [...backendsByLocation.keys()],
|
|
291
|
+
});
|
|
292
|
+
return backendsByLocation.get(location);
|
|
293
|
+
}
|
|
294
|
+
exports.getBackendForAmbiguousLocation = getBackendForAmbiguousLocation;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getGitHubCommit = exports.getGitHubBranch = exports.fetchRepositoryCloneUris = exports.listAppHostingConnections = exports.getOrCreateRepository = exports.getOrCreateConnection = exports.createConnection = exports.ensureSecretManagerAdminGrant = exports.promptGitHubBranch = exports.getOrCreateOauthConnection = exports.listValidInstallations = exports.promptGitHubInstallation = exports.getConnectionForInstallation = exports.linkGitHubRepository = exports.generateRepositoryId = exports.extractRepoSlugFromUri = exports.parseConnectionName = void 0;
|
|
3
|
+
exports.getGitHubCommit = exports.getGitHubBranch = exports.fetchRepositoryCloneUris = exports.listAppHostingConnections = exports.getOrCreateRepository = exports.getOrCreateConnection = exports.createConnection = exports.ensureSecretManagerAdminGrant = exports.promptGitHubBranch = exports.getOrCreateOauthConnection = exports.listValidInstallations = exports.promptGitHubInstallation = exports.getConnectionForInstallation = exports.linkGitHubRepository = exports.getOrCreateGithubConnectionWithSentinel = exports.generateRepositoryId = exports.extractRepoSlugFromUri = exports.parseConnectionName = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const devConnect = require("../gcp/devConnect");
|
|
6
6
|
const rm = require("../gcp/resourceManager");
|
|
@@ -55,9 +55,20 @@ function generateConnectionId() {
|
|
|
55
55
|
}
|
|
56
56
|
const ADD_ACCOUNT_CHOICE = "@ADD_ACCOUNT";
|
|
57
57
|
const MANAGE_INSTALLATION_CHOICE = "@MANAGE_INSTALLATION";
|
|
58
|
-
async function
|
|
59
|
-
var _a, _b;
|
|
58
|
+
async function getOrCreateGithubConnectionWithSentinel(projectId, location, createConnectionId) {
|
|
60
59
|
utils.logBullet(clc.bold(`${clc.yellow("===")} Import a GitHub repository`));
|
|
60
|
+
if (createConnectionId) {
|
|
61
|
+
try {
|
|
62
|
+
const connection = await devConnect.getConnection(projectId, location, createConnectionId);
|
|
63
|
+
utils.logBullet(`Reusing existing connection ${createConnectionId}`);
|
|
64
|
+
return connection;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (err.status !== 404) {
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
61
72
|
const oauthConn = await getOrCreateOauthConnection(projectId, location);
|
|
62
73
|
let installationId = await promptGitHubInstallation(projectId, location, oauthConn);
|
|
63
74
|
while (installationId === ADD_ACCOUNT_CHOICE) {
|
|
@@ -71,21 +82,35 @@ async function linkGitHubRepository(projectId, location) {
|
|
|
71
82
|
});
|
|
72
83
|
installationId = await promptGitHubInstallation(projectId, location, oauthConn);
|
|
73
84
|
}
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
85
|
+
const connectionMatchingInstallation = await getConnectionForInstallation(projectId, location, installationId);
|
|
86
|
+
if (connectionMatchingInstallation) {
|
|
87
|
+
const { id: matchingConnectionId } = parseConnectionName(connectionMatchingInstallation.name);
|
|
88
|
+
if (!createConnectionId) {
|
|
89
|
+
utils.logBullet(`Reusing matching connection ${matchingConnectionId}`);
|
|
90
|
+
return connectionMatchingInstallation;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!createConnectionId) {
|
|
94
|
+
createConnectionId = generateConnectionId();
|
|
77
95
|
}
|
|
96
|
+
const connection = await createFullyInstalledConnection(projectId, location, createConnectionId, oauthConn, installationId);
|
|
97
|
+
return connection;
|
|
98
|
+
}
|
|
99
|
+
exports.getOrCreateGithubConnectionWithSentinel = getOrCreateGithubConnectionWithSentinel;
|
|
100
|
+
async function linkGitHubRepository(projectId, location, createConnectionId) {
|
|
101
|
+
var _a, _b;
|
|
102
|
+
const connection = await getOrCreateGithubConnectionWithSentinel(projectId, location, createConnectionId);
|
|
78
103
|
let repoCloneUri;
|
|
79
104
|
do {
|
|
80
105
|
if (repoCloneUri === MANAGE_INSTALLATION_CHOICE) {
|
|
81
|
-
await manageInstallation(
|
|
106
|
+
await manageInstallation(connection);
|
|
82
107
|
}
|
|
83
|
-
repoCloneUri = await promptCloneUri(projectId,
|
|
108
|
+
repoCloneUri = await promptCloneUri(projectId, connection);
|
|
84
109
|
} while (repoCloneUri === MANAGE_INSTALLATION_CHOICE);
|
|
85
|
-
const { id: connectionId } = parseConnectionName(
|
|
110
|
+
const { id: connectionId } = parseConnectionName(connection.name);
|
|
86
111
|
await getOrCreateConnection(projectId, location, connectionId, {
|
|
87
|
-
authorizerCredential: (_a =
|
|
88
|
-
appInstallationId: (_b =
|
|
112
|
+
authorizerCredential: (_a = connection.githubConfig) === null || _a === void 0 ? void 0 : _a.authorizerCredential,
|
|
113
|
+
appInstallationId: (_b = connection.githubConfig) === null || _b === void 0 ? void 0 : _b.appInstallationId,
|
|
89
114
|
});
|
|
90
115
|
const repo = await getOrCreateRepository(projectId, location, connectionId, repoCloneUri);
|
|
91
116
|
return repo;
|
|
@@ -239,19 +264,25 @@ async function promptCloneUri(projectId, connection) {
|
|
|
239
264
|
return cloneUri;
|
|
240
265
|
}
|
|
241
266
|
async function promptGitHubBranch(repoLink) {
|
|
267
|
+
var _a;
|
|
242
268
|
const branches = await devConnect.listAllBranches(repoLink.name);
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
269
|
+
const branch = await (0, prompt_1.promptOnce)({
|
|
270
|
+
type: "autocomplete",
|
|
271
|
+
name: "branch",
|
|
272
|
+
message: "Pick a branch for continuous deployment",
|
|
273
|
+
source: (_, input = "") => {
|
|
274
|
+
return new Promise((resolve) => resolve([
|
|
275
|
+
...fuzzy.filter(input, Array.from(branches)).map((result) => {
|
|
276
|
+
return {
|
|
277
|
+
name: result.original,
|
|
278
|
+
value: result.original,
|
|
279
|
+
};
|
|
280
|
+
}),
|
|
281
|
+
]));
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
utils.logWarning(`The branch "${branch}" does not exist on "${(_a = extractRepoSlugFromUri(repoLink.cloneUri)) !== null && _a !== void 0 ? _a : ""}". Please enter a valid branch for this repo.`);
|
|
285
|
+
return branch;
|
|
255
286
|
}
|
|
256
287
|
exports.promptGitHubBranch = promptGitHubBranch;
|
|
257
288
|
async function ensureSecretManagerAdminGrant(projectId) {
|