firebase-tools 13.23.0 → 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.
@@ -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;
@@ -264,19 +264,25 @@ async function promptCloneUri(projectId, connection) {
264
264
  return cloneUri;
265
265
  }
266
266
  async function promptGitHubBranch(repoLink) {
267
+ var _a;
267
268
  const branches = await devConnect.listAllBranches(repoLink.name);
268
- while (true) {
269
- const branch = await (0, prompt_1.promptOnce)({
270
- name: "branch",
271
- type: "input",
272
- default: "main",
273
- message: "Pick a branch for continuous deployment",
274
- });
275
- if (branches.has(branch)) {
276
- return branch;
277
- }
278
- utils.logWarning(`The branch "${branch}" does not exist on "${extractRepoSlugFromUri(repoLink.cloneUri)}". Please enter a valid branch for this repo.`);
279
- }
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;
280
286
  }
281
287
  exports.promptGitHubBranch = promptGitHubBranch;
282
288
  async function ensureSecretManagerAdminGrant(projectId) {
@@ -1,280 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getBackendForAmbiguousLocation = 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 getBackendForAmbiguousLocation(projectId, backendId, locationDisambugationPrompt) {
258
- let { unreachable, backends } = await apphosting.listBackends(projectId, "-");
259
- if (unreachable && unreachable.length !== 0) {
260
- (0, utils_1.logWarning)(`The following locations are currently unreachable: ${unreachable}.\n` +
261
- "If your backend is in one of these regions, please try again later.");
262
- }
263
- backends = backends.filter((backend) => apphosting.parseBackendName(backend.name).id === backendId);
264
- if (backends.length === 0) {
265
- throw new error_1.FirebaseError(`No backend named "${backendId}" found.`);
266
- }
267
- if (backends.length === 1) {
268
- return backends[0];
269
- }
270
- const backendsByLocation = new Map();
271
- backends.forEach((backend) => backendsByLocation.set(apphosting.parseBackendName(backend.name).location, backend));
272
- const location = await (0, prompt_1.promptOnce)({
273
- name: "location",
274
- type: "list",
275
- message: locationDisambugationPrompt,
276
- choices: [...backendsByLocation.keys()],
277
- });
278
- return backendsByLocation.get(location);
279
- }
280
- exports.getBackendForAmbiguousLocation = getBackendForAmbiguousLocation;
3
+ exports.setupBackend = void 0;
4
+ const backend_1 = require("./backend");
5
+ Object.defineProperty(exports, "setupBackend", { enumerable: true, get: function () { return backend_1.doSetup; } });
@@ -7,9 +7,9 @@ const ora = require("ora");
7
7
  const devConnect_1 = require("../gcp/devConnect");
8
8
  const githubConnections_1 = require("../apphosting/githubConnections");
9
9
  const poller = require("../operation-poller");
10
- const prompt_1 = require("../prompt");
11
10
  const utils_1 = require("../utils");
12
11
  const api_1 = require("../api");
12
+ const backend_1 = require("./backend");
13
13
  const apphostingPollerOptions = {
14
14
  apiOrigin: (0, api_1.apphostingOrigin)(),
15
15
  apiVersion: apphosting.API_VERSION,
@@ -18,7 +18,14 @@ const apphostingPollerOptions = {
18
18
  };
19
19
  const GIT_COMMIT_SHA_REGEX = /^(?:[0-9a-f]{40}|[0-9a-f]{7})$/;
20
20
  async function createRollout(backendId, projectId, location, branch, commit, force) {
21
- const backend = await apphosting.getBackend(projectId, location, backendId);
21
+ let backend;
22
+ if (location === "-" || location === "") {
23
+ backend = await (0, backend_1.getBackendForAmbiguousLocation)(projectId, backendId, "Please select the location of the backend you'd like to roll out:", force);
24
+ location = apphosting.parseBackendName(backend.name).location;
25
+ }
26
+ else {
27
+ backend = await (0, backend_1.getBackendForLocation)(projectId, location, backendId);
28
+ }
22
29
  if (!backend.codebase.repository) {
23
30
  throw new error_1.FirebaseError(`Backend ${backendId} is misconfigured due to missing a connected repository. You can delete and recreate your backend using 'firebase apphosting:backends:delete' and 'firebase apphosting:backends:create'.`);
24
31
  }
@@ -48,18 +55,14 @@ async function createRollout(backendId, projectId, location, branch, commit, for
48
55
  }
49
56
  }
50
57
  else {
58
+ if (force) {
59
+ throw new error_1.FirebaseError(`Failed to create rollout with --force option because no target branch or commit was specified. Please specify which branch or commit to roll out with the --git-branch or --git-commit flag.`);
60
+ }
51
61
  branch = await (0, githubConnections_1.promptGitHubBranch)(repoLink);
52
62
  const branchInfo = await (0, githubConnections_1.getGitHubBranch)(owner, repo, branch, readToken.token);
53
63
  targetCommit = branchInfo.commit;
54
64
  }
55
65
  (0, utils_1.logBullet)(`You are about to deploy [${targetCommit.sha.substring(0, 7)}]: ${targetCommit.commit.message}`);
56
- const confirmRollout = await (0, prompt_1.confirm)({
57
- force: !!force,
58
- message: "Do you want to continue?",
59
- });
60
- if (!confirmRollout) {
61
- return;
62
- }
63
66
  (0, utils_1.logBullet)(`You may also track this rollout at:\n\t${(0, api_1.consoleOrigin)()}/project/${projectId}/apphosting`);
64
67
  const createRolloutSpinner = ora("Starting a new rollout; this may take a few minutes. It's safe to exit now.").start();
65
68
  try {
@@ -16,7 +16,7 @@ const marked_1 = require("marked");
16
16
  marked_1.marked.use((0, marked_terminal_1.markedTerminal)());
17
17
  const node_path_1 = require("node:path");
18
18
  const triple_beam_1 = require("triple-beam");
19
- const stripAnsi = require("strip-ansi");
19
+ const node_util_1 = require("node:util");
20
20
  const fs = require("node:fs");
21
21
  const configstore_1 = require("../configstore");
22
22
  const errorOut_1 = require("../errorOut");
@@ -58,7 +58,7 @@ logger_1.logger.add(new winston.transports.File({
58
58
  filename: logFilename,
59
59
  format: winston.format.printf((info) => {
60
60
  const segments = [info.message, ...(info[triple_beam_1.SPLAT] || [])].map(utils.tryStringify);
61
- return `[${info.level}] ${stripAnsi(segments.join(" "))}`;
61
+ return `[${info.level}] ${(0, node_util_1.stripVTControlCharacters)(segments.join(" "))}`;
62
62
  }),
63
63
  }));
64
64
  logger_1.logger.debug("-".repeat(70));
@@ -4,8 +4,8 @@ exports.command = void 0;
4
4
  const command_1 = require("../command");
5
5
  const projectUtils_1 = require("../projectUtils");
6
6
  const requireInteractive_1 = require("../requireInteractive");
7
- const apphosting_1 = require("../apphosting");
8
- const apphosting_2 = require("../gcp/apphosting");
7
+ const backend_1 = require("../apphosting/backend");
8
+ const apphosting_1 = require("../gcp/apphosting");
9
9
  const firedata_1 = require("../gcp/firedata");
10
10
  const requireTosAcceptance_1 = require("../requireTosAcceptance");
11
11
  exports.command = new command_1.Command("apphosting:backends:create")
@@ -13,7 +13,7 @@ exports.command = new command_1.Command("apphosting:backends:create")
13
13
  .option("-a, --app <webAppId>", "specify an existing Firebase web app's ID to associate your App Hosting backend with")
14
14
  .option("-l, --location <location>", "specify the location of the backend", "")
15
15
  .option("-s, --service-account <serviceAccount>", "specify the service account used to run the server", "")
16
- .before(apphosting_2.ensureApiEnabled)
16
+ .before(apphosting_1.ensureApiEnabled)
17
17
  .before(requireInteractive_1.default)
18
18
  .before((0, requireTosAcceptance_1.requireTosAcceptance)(firedata_1.APPHOSTING_TOS_ID))
19
19
  .action(async (options) => {
@@ -21,5 +21,5 @@ exports.command = new command_1.Command("apphosting:backends:create")
21
21
  const webAppId = options.app;
22
22
  const location = options.location;
23
23
  const serviceAccount = options.serviceAccount;
24
- await (0, apphosting_1.doSetup)(projectId, webAppId, location, serviceAccount);
24
+ await (0, backend_1.doSetup)(projectId, webAppId, location, serviceAccount);
25
25
  });
@@ -8,7 +8,7 @@ const prompt_1 = require("../prompt");
8
8
  const utils = require("../utils");
9
9
  const apphosting = require("../gcp/apphosting");
10
10
  const apphosting_backends_list_1 = require("./apphosting-backends-list");
11
- const apphosting_1 = require("../apphosting");
11
+ const backend_1 = require("../apphosting/backend");
12
12
  const ora = require("ora");
13
13
  exports.command = new command_1.Command("apphosting:backends:delete <backend>")
14
14
  .description("delete a Firebase App Hosting backend")
@@ -20,11 +20,11 @@ exports.command = new command_1.Command("apphosting:backends:delete <backend>")
20
20
  let location = options.location;
21
21
  let backend;
22
22
  if (location === "-" || location === "") {
23
- backend = await (0, apphosting_1.getBackendForAmbiguousLocation)(projectId, backendId, "Please select the location of the backend you'd like to delete:");
23
+ backend = await (0, backend_1.getBackendForAmbiguousLocation)(projectId, backendId, "Please select the location of the backend you'd like to delete:");
24
24
  location = apphosting.parseBackendName(backend.name).location;
25
25
  }
26
26
  else {
27
- backend = await getBackendForLocation(projectId, location, backendId);
27
+ backend = await (0, backend_1.getBackendForLocation)(projectId, location, backendId);
28
28
  }
29
29
  utils.logWarning("You are about to permanently delete this backend:");
30
30
  (0, apphosting_backends_list_1.printBackendsTable)([backend]);
@@ -39,7 +39,7 @@ exports.command = new command_1.Command("apphosting:backends:delete <backend>")
39
39
  }
40
40
  const spinner = ora("Deleting backend...").start();
41
41
  try {
42
- await (0, apphosting_1.deleteBackendAndPoll)(projectId, location, backendId);
42
+ await (0, backend_1.deleteBackendAndPoll)(projectId, location, backendId);
43
43
  spinner.succeed(`Successfully deleted the backend: ${backendId}`);
44
44
  }
45
45
  catch (err) {
@@ -47,13 +47,3 @@ exports.command = new command_1.Command("apphosting:backends:delete <backend>")
47
47
  throw new error_1.FirebaseError(`Failed to delete backend: ${backendId}.`, { original: err });
48
48
  }
49
49
  });
50
- async function getBackendForLocation(projectId, location, backendId) {
51
- try {
52
- return await apphosting.getBackend(projectId, location, backendId);
53
- }
54
- catch (err) {
55
- throw new error_1.FirebaseError(`No backend named "${backendId}" found in ${location}.`, {
56
- original: err,
57
- });
58
- }
59
- }
@@ -4,20 +4,20 @@ exports.command = void 0;
4
4
  const command_1 = require("../command");
5
5
  const projectUtils_1 = require("../projectUtils");
6
6
  const requireInteractive_1 = require("../requireInteractive");
7
- const apphosting_1 = require("../apphosting");
8
- const apphosting_2 = require("../gcp/apphosting");
7
+ const backend_1 = require("../apphosting/backend");
8
+ const apphosting_1 = require("../gcp/apphosting");
9
9
  const firedata_1 = require("../gcp/firedata");
10
10
  const requireTosAcceptance_1 = require("../requireTosAcceptance");
11
11
  exports.command = new command_1.Command("apphosting:repos:create")
12
12
  .description("create a Firebase App Hosting Developer Connect Git Repository Link")
13
13
  .option("-l, --location <location>", "specify the location of the backend", "")
14
14
  .option("-g, --gitconnection <connection>", "id of the connection", "")
15
- .before(apphosting_2.ensureApiEnabled)
15
+ .before(apphosting_1.ensureApiEnabled)
16
16
  .before(requireInteractive_1.default)
17
17
  .before((0, requireTosAcceptance_1.requireTosAcceptance)(firedata_1.APPHOSTING_TOS_ID))
18
18
  .action(async (options) => {
19
19
  const projectId = (0, projectUtils_1.needProjectId)(options);
20
20
  const location = options.location;
21
21
  const connection = options.gitconnection;
22
- await (0, apphosting_1.createGitRepoLink)(projectId, location, connection);
22
+ await (0, backend_1.createGitRepoLink)(projectId, location, connection);
23
23
  });
@@ -8,10 +8,9 @@ const error_1 = require("../error");
8
8
  const rollout_1 = require("../apphosting/rollout");
9
9
  exports.command = new command_1.Command("apphosting:rollouts:create <backendId>")
10
10
  .description("create a rollout using a build for an App Hosting backend")
11
- .option("-l, --location <location>", "specify the region of the backend", "us-central1")
12
- .option("-i, --id <rolloutId>", "id of the rollout (defaults to autogenerating a random id)", "")
13
- .option("-gb, --git-branch <gitBranch>", "repository branch to deploy (mutually exclusive with -gc)")
14
- .option("-gc, --git-commit <gitCommit>", "git commit to deploy (mutually exclusive with -gb)")
11
+ .option("-l, --location <location>", "specify the region of the backend", "-")
12
+ .option("-b, --git-branch <gitBranch>", "repository branch to deploy (mutually exclusive with -g)")
13
+ .option("-g, --git-commit <gitCommit>", "git commit to deploy (mutually exclusive with -b)")
15
14
  .withForce("Skip confirmation before creating rollout")
16
15
  .before(apphosting.ensureApiEnabled)
17
16
  .action(async (backendId, options) => {
@@ -9,7 +9,7 @@ const secretManager = require("../gcp/secretManager");
9
9
  const requirePermissions_1 = require("../requirePermissions");
10
10
  const apphosting = require("../gcp/apphosting");
11
11
  const secrets = require("../apphosting/secrets");
12
- const apphosting_1 = require("../apphosting");
12
+ const backend_1 = require("../apphosting/backend");
13
13
  exports.command = new command_1.Command("apphosting:secrets:grantaccess <secretName>")
14
14
  .description("grant service accounts permissions to the provided secret")
15
15
  .option("-l, --location <location>", "backend location", "-")
@@ -39,7 +39,7 @@ exports.command = new command_1.Command("apphosting:secrets:grantaccess <secretN
39
39
  const location = options.location;
40
40
  let backend;
41
41
  if (location === "" || location === "-") {
42
- backend = await (0, apphosting_1.getBackendForAmbiguousLocation)(projectId, backendId, "Please select the location of your backend:");
42
+ backend = await (0, backend_1.getBackendForAmbiguousLocation)(projectId, backendId, "Please select the location of your backend:");
43
43
  }
44
44
  else {
45
45
  backend = await apphosting.getBackend(projectId, location, backendId);
@@ -37,6 +37,12 @@ let choices = [
37
37
  name: "Functions: Configure a Cloud Functions directory and its files",
38
38
  checked: false,
39
39
  },
40
+ {
41
+ value: "apphosting",
42
+ name: "App Hosting: Configure an apphosting.yaml file for App Hosting",
43
+ checked: false,
44
+ hidden: false,
45
+ },
40
46
  {
41
47
  value: "hosting",
42
48
  name: "Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys",
@@ -79,12 +85,6 @@ let choices = [
79
85
  checked: false,
80
86
  hidden: true,
81
87
  },
82
- {
83
- value: "apphosting",
84
- name: "App Hosting: Configure an apphosting.yaml file for App Hosting",
85
- checked: false,
86
- hidden: false,
87
- },
88
88
  ];
89
89
  if ((0, experiments_1.isEnabled)("genkit")) {
90
90
  choices = [
@@ -384,5 +384,5 @@ async function checkJavaMajorVersion() {
384
384
  }
385
385
  exports.checkJavaMajorVersion = checkJavaMajorVersion;
386
386
  exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = 11;
387
- exports.JAVA_DEPRECATION_WARNING = "firebase-tools no longer supports Java version before 11. " +
388
- "Please upgrade to Java version 11 or above to continue using the emulators.";
387
+ exports.JAVA_DEPRECATION_WARNING = "firebase-tools no longer supports Java versions before 11. " +
388
+ "Please install a JDK at version 11 or above to get a compatible runtime.";
@@ -48,20 +48,20 @@ const EMULATOR_UPDATE_DETAILS = {
48
48
  },
49
49
  dataconnect: process.platform === "darwin"
50
50
  ? {
51
- version: "1.5.1",
52
- expectedSize: 25289472,
53
- expectedChecksum: "92c425072db66c7e2cfa40b703ed807b",
51
+ version: "1.6.0",
52
+ expectedSize: 25301760,
53
+ expectedChecksum: "4fad7ada11b35ecea6c8aadf132a5a8a",
54
54
  }
55
55
  : process.platform === "win32"
56
56
  ? {
57
- version: "1.5.1",
58
- expectedSize: 25720320,
59
- expectedChecksum: "2a5c654770233b740980d5f98f24be73",
57
+ version: "1.6.0",
58
+ expectedSize: 25731072,
59
+ expectedChecksum: "8d3a59cc79cd74199ee1d8c28012297f",
60
60
  }
61
61
  : {
62
- version: "1.5.1",
63
- expectedSize: 25202840,
64
- expectedChecksum: "f95156cbcac237268791638ea0eb10e7",
62
+ version: "1.6.0",
63
+ expectedSize: 25219224,
64
+ expectedChecksum: "ceb10cfca7ded004c48f7724dbc0cccf",
65
65
  },
66
66
  };
67
67
  exports.DownloadDetails = {
@@ -7,7 +7,7 @@ const triple_beam_1 = require("triple-beam");
7
7
  const WebSocket = require("ws");
8
8
  const TransportStream = require("winston-transport");
9
9
  const logger_1 = require("../logger");
10
- const stripAnsi = require("strip-ansi");
10
+ const node_util_1 = require("node:util");
11
11
  class LoggingEmulator {
12
12
  constructor(args) {
13
13
  this.args = args;
@@ -106,7 +106,7 @@ class WebSocketTransport extends TransportStream {
106
106
  if (bundle.data && bundle.data.metadata && bundle.data.metadata.message) {
107
107
  bundle.message = bundle.data.metadata.message;
108
108
  }
109
- bundle.message = stripAnsi(bundle.message);
109
+ bundle.message = (0, node_util_1.stripVTControlCharacters)(bundle.message);
110
110
  this.history.push(bundle);
111
111
  this.connections.forEach((ws) => {
112
112
  ws.send(JSON.stringify(bundle));
@@ -126,8 +126,11 @@ async function build(dir, target, context) {
126
126
  (0, utils_1.readJSON)((0, path_1.join)(dir, distDir, "server", constants_2.SERVER_REFERENCE_MANIFEST)).catch(() => undefined),
127
127
  ]);
128
128
  if (appPathRoutesManifest) {
129
- const headersFromMetaFiles = await (0, utils_2.getHeadersFromMetaFiles)(dir, distDir, baseUrl, appPathRoutesManifest);
129
+ const { headers: headersFromMetaFiles, pprRoutes } = await (0, utils_2.getAppMetadataFromMetaFiles)(dir, distDir, baseUrl, appPathRoutesManifest);
130
130
  headers.push(...headersFromMetaFiles);
131
+ for (const route of pprRoutes) {
132
+ reasonsForBackend.add(`route with ppr ${route}`);
133
+ }
131
134
  if (appPathsManifest) {
132
135
  const unrenderedServerComponents = (0, utils_2.getNonStaticServerComponents)(appPathsManifest, appPathRoutesManifest, prerenderedRoutes, dynamicRoutes);
133
136
  const notFoundPageKey = ["/_not-found", "/_not-found/page"].find((key) => unrenderedServerComponents.has(key));
@@ -270,6 +273,7 @@ async function ɵcodegenPublicDirectory(sourceDir, destDir, _, context) {
270
273
  ];
271
274
  }));
272
275
  const routesToCopy = Object.assign(Object.assign({}, prerenderManifest.routes), pagesManifestLikePrerender);
276
+ const { pprRoutes } = await (0, utils_2.getAppMetadataFromMetaFiles)(sourceDir, distDir, basePath, appPathRoutesManifest);
273
277
  await Promise.all(Object.entries(routesToCopy).map(async ([path, route]) => {
274
278
  var _a, _b;
275
279
  if (route.initialRevalidateSeconds) {
@@ -308,6 +312,10 @@ async function ɵcodegenPublicDirectory(sourceDir, destDir, _, context) {
308
312
  let defaultDestPath = isDefaultLocale && (0, path_1.join)(destDir, basePath, ...destPartsOrIndex);
309
313
  if (!(0, fsutils_1.fileExistsSync)(sourcePath) && (0, fsutils_1.fileExistsSync)(`${sourcePath}.html`)) {
310
314
  sourcePath += ".html";
315
+ if (pprRoutes.includes(path)) {
316
+ logger_1.logger.debug(`skipping ${path} due to ppr`);
317
+ return;
318
+ }
311
319
  if (localizedDestPath)
312
320
  localizedDestPath += ".html";
313
321
  if (defaultDestPath)
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.installEsbuild = exports.getGlobalEsbuildVersion = exports.findEsbuildPath = exports.whichNextConfigFile = exports.getProductionDistDirFiles = exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
3
+ exports.installEsbuild = exports.getGlobalEsbuildVersion = exports.findEsbuildPath = exports.whichNextConfigFile = exports.getProductionDistDirFiles = exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getAppMetadataFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const fs_extra_1 = require("fs-extra");
6
6
  const path_1 = require("path");
@@ -167,8 +167,9 @@ function getNonStaticServerComponents(appPathsManifest, appPathRoutesManifest, p
167
167
  return new Set(nonStaticServerComponents);
168
168
  }
169
169
  exports.getNonStaticServerComponents = getNonStaticServerComponents;
170
- async function getHeadersFromMetaFiles(sourceDir, distDir, basePath, appPathRoutesManifest) {
170
+ async function getAppMetadataFromMetaFiles(sourceDir, distDir, basePath, appPathRoutesManifest) {
171
171
  const headers = [];
172
+ const pprRoutes = [];
172
173
  await Promise.all(Object.entries(appPathRoutesManifest).map(async ([key, source]) => {
173
174
  if (!["route", "page"].includes((0, path_1.basename)(key)))
174
175
  return;
@@ -183,11 +184,13 @@ async function getHeadersFromMetaFiles(sourceDir, distDir, basePath, appPathRout
183
184
  source: path_1.posix.join(basePath, source),
184
185
  headers: Object.entries(meta.headers).map(([key, value]) => ({ key, value })),
185
186
  });
187
+ if (meta.postponed)
188
+ pprRoutes.push(source);
186
189
  }
187
190
  }));
188
- return headers;
191
+ return { headers, pprRoutes };
189
192
  }
190
- exports.getHeadersFromMetaFiles = getHeadersFromMetaFiles;
193
+ exports.getAppMetadataFromMetaFiles = getAppMetadataFromMetaFiles;
191
194
  async function getBuildId(distDir) {
192
195
  const buildId = await (0, promises_1.readFile)((0, path_1.join)(distDir, "BUILD_ID"));
193
196
  return buildId.toString();
@@ -6,7 +6,7 @@ const cross_spawn_1 = require("cross-spawn");
6
6
  const fs_1 = require("fs");
7
7
  const fs_extra_1 = require("fs-extra");
8
8
  const path_1 = require("path");
9
- const stripAnsi = require("strip-ansi");
9
+ const node_util_1 = require("node:util");
10
10
  const prompt_1 = require("../../prompt");
11
11
  const utils_1 = require("../utils");
12
12
  exports.name = "Vite";
@@ -94,7 +94,7 @@ async function getDevModeHandle(dir) {
94
94
  const serve = (0, cross_spawn_1.spawn)(cli, [], { cwd: dir });
95
95
  serve.stdout.on("data", (data) => {
96
96
  process.stdout.write(data);
97
- const dataWithoutAnsiCodes = stripAnsi(data.toString());
97
+ const dataWithoutAnsiCodes = (0, node_util_1.stripVTControlCharacters)(data.toString());
98
98
  const match = dataWithoutAnsiCodes.match(/(http:\/\/.+:\d+)/);
99
99
  if (match)
100
100
  resolve(match[1]);
@@ -9,7 +9,7 @@ const ensureApiEnabled_1 = require("../ensureApiEnabled");
9
9
  const deploymentTool = require("../deploymentTool");
10
10
  const error_1 = require("../error");
11
11
  const metaprogramming_1 = require("../metaprogramming");
12
- exports.API_VERSION = "v1alpha";
12
+ exports.API_VERSION = "v1beta";
13
13
  exports.client = new apiv2_1.Client({
14
14
  urlPrefix: (0, api_1.apphostingOrigin)(),
15
15
  auth: true,
@@ -4,9 +4,12 @@ exports.doSetup = void 0;
4
4
  const clc = require("colorette");
5
5
  const utils = require("../../utils");
6
6
  const templates_1 = require("../../templates");
7
+ const cloudbilling_1 = require("../../gcp/cloudbilling");
7
8
  const APPHOSTING_YAML_TEMPLATE = (0, templates_1.readTemplateSync)("init/apphosting/apphosting.yaml");
8
9
  async function doSetup(setup, config) {
10
+ await (0, cloudbilling_1.checkBillingEnabled)(setup.projectId);
9
11
  utils.logBullet("Writing default settings to " + clc.bold("apphosting.yaml") + "...");
10
12
  await config.askWriteProjectFile("apphosting.yaml", APPHOSTING_YAML_TEMPLATE);
13
+ utils.logSuccess("Create a new App Hosting backend with `firebase apphosting:backends:create`");
11
14
  }
12
15
  exports.doSetup = doSetup;
package/lib/utils.js CHANGED
@@ -17,7 +17,7 @@ const stream_1 = require("stream");
17
17
  const winston = require("winston");
18
18
  const triple_beam_1 = require("triple-beam");
19
19
  const assert_1 = require("assert");
20
- const stripAnsi = require("strip-ansi");
20
+ const node_util_1 = require("node:util");
21
21
  const portfinder_1 = require("portfinder");
22
22
  const configstore_1 = require("./configstore");
23
23
  const error_1 = require("./error");
@@ -315,7 +315,7 @@ function setupLoggers() {
315
315
  level: "debug",
316
316
  format: winston.format.printf((info) => {
317
317
  const segments = [info.message, ...(info[triple_beam_1.SPLAT] || [])].map(tryStringify);
318
- return `${stripAnsi(segments.join(" "))}`;
318
+ return `${(0, node_util_1.stripVTControlCharacters)(segments.join(" "))}`;
319
319
  }),
320
320
  }));
321
321
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.23.0",
3
+ "version": "13.23.1",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -114,7 +114,6 @@
114
114
  "sql-formatter": "^15.3.0",
115
115
  "stream-chain": "^2.2.4",
116
116
  "stream-json": "^1.7.3",
117
- "strip-ansi": "^6.0.1",
118
117
  "superstatic": "^9.0.3",
119
118
  "tar": "^6.1.11",
120
119
  "tcp-port-used": "^1.0.2",