firebase-tools 14.26.0 → 14.27.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/lib/commands/functions-list.js +16 -6
- package/lib/commands/remoteconfig-versions-list.js +9 -5
- package/lib/deploy/functions/backend.js +21 -18
- package/lib/deploy/functions/build.js +0 -8
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +0 -2
- package/lib/deploy/functions/runtimes/supported/types.js +1 -1
- package/lib/deploy/functions/validate.js +14 -1
- package/lib/deploy/index.js +2 -2
- package/lib/emulator/downloadableEmulatorInfo.json +24 -24
- package/lib/experiments.js +6 -2
- package/lib/frameworks/next/index.js +24 -0
- package/lib/frameworks/next/utils.js +41 -1
- package/lib/gcp/cloudscheduler.js +9 -2
- package/lib/gcp/runv2.js +2 -2
- package/lib/gcp/storage.js +18 -5
- package/lib/init/features/dataconnect/index.js +9 -5
- package/lib/init/features/hosting/github.js +1 -5
- package/lib/mcp/tools/core/get_environment.js +60 -37
- package/lib/mcp/util/crashlytics/availability.js +4 -16
- package/lib/utils.js +6 -2
- package/package.json +2 -1
|
@@ -7,25 +7,35 @@ const requirePermissions_1 = require("../requirePermissions");
|
|
|
7
7
|
const backend = require("../deploy/functions/backend");
|
|
8
8
|
const logger_1 = require("../logger");
|
|
9
9
|
const Table = require("cli-table3");
|
|
10
|
+
const PLATFORM_TO_DISPLAY_NAME = {
|
|
11
|
+
gcfv1: "v1",
|
|
12
|
+
gcfv2: "v2",
|
|
13
|
+
run: "run",
|
|
14
|
+
};
|
|
10
15
|
exports.command = new command_1.Command("functions:list")
|
|
11
16
|
.description("list all deployed functions in your Firebase project")
|
|
12
|
-
.before(requirePermissions_1.requirePermissions, ["cloudfunctions.functions.list"])
|
|
17
|
+
.before(requirePermissions_1.requirePermissions, ["cloudfunctions.functions.list", "run.services.list"])
|
|
13
18
|
.action(async (options) => {
|
|
19
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
14
20
|
const context = {
|
|
15
|
-
projectId
|
|
21
|
+
projectId,
|
|
16
22
|
};
|
|
17
23
|
const existing = await backend.existingBackend(context);
|
|
18
|
-
const
|
|
24
|
+
const endpoints = backend.allEndpoints(existing).sort(backend.compareFunctions);
|
|
25
|
+
if (endpoints.length === 0) {
|
|
26
|
+
logger_1.logger.info(`No functions found in project ${projectId}.`);
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
19
29
|
const table = new Table({
|
|
20
30
|
head: ["Function", "Version", "Trigger", "Location", "Memory", "Runtime"],
|
|
21
31
|
style: { head: ["yellow"] },
|
|
22
32
|
});
|
|
23
|
-
for (const endpoint of
|
|
33
|
+
for (const endpoint of endpoints) {
|
|
24
34
|
const trigger = backend.endpointTriggerType(endpoint);
|
|
25
35
|
const availableMemoryMb = endpoint.availableMemoryMb || "---";
|
|
26
36
|
const entry = [
|
|
27
37
|
endpoint.id,
|
|
28
|
-
endpoint.platform
|
|
38
|
+
PLATFORM_TO_DISPLAY_NAME[endpoint.platform] || "v1",
|
|
29
39
|
trigger,
|
|
30
40
|
endpoint.region,
|
|
31
41
|
availableMemoryMb,
|
|
@@ -34,5 +44,5 @@ exports.command = new command_1.Command("functions:list")
|
|
|
34
44
|
table.push(entry);
|
|
35
45
|
}
|
|
36
46
|
logger_1.logger.info(table.toString());
|
|
37
|
-
return
|
|
47
|
+
return endpoints;
|
|
38
48
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.command = void 0;
|
|
3
|
+
exports.printVersionsTable = exports.command = void 0;
|
|
4
4
|
const logger_1 = require("../logger");
|
|
5
5
|
const rcVersion = require("../remoteconfig/versionslist");
|
|
6
6
|
const command_1 = require("../command");
|
|
@@ -25,10 +25,14 @@ exports.command = new command_1.Command("remoteconfig:versions:list")
|
|
|
25
25
|
.before(requirePermissions_1.requirePermissions, ["cloudconfig.configs.get"])
|
|
26
26
|
.action(async (options) => {
|
|
27
27
|
const versionsList = await rcVersion.getVersions((0, projectUtils_1.needProjectId)(options), options.limit);
|
|
28
|
+
printVersionsTable(versionsList);
|
|
29
|
+
return versionsList;
|
|
30
|
+
});
|
|
31
|
+
function printVersionsTable(versionsList) {
|
|
28
32
|
const table = new Table({ head: tableHead, style: { head: ["green"] } });
|
|
29
|
-
for (
|
|
30
|
-
pushTableContents(table,
|
|
33
|
+
for (const version of versionsList.versions || []) {
|
|
34
|
+
pushTableContents(table, version);
|
|
31
35
|
}
|
|
32
36
|
logger_1.logger.info(table.toString());
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
}
|
|
38
|
+
exports.printVersionsTable = printVersionsTable;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.merge = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_CPU_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.DEFAULT_CONCURRENCY = exports.memoryToGen2Cpu = exports.memoryToGen1Cpu = exports.memoryOptionDisplayName = exports.
|
|
3
|
+
exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.merge = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_CPU_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.DEFAULT_CONCURRENCY = exports.memoryToGen2Cpu = exports.memoryToGen1Cpu = exports.memoryOptionDisplayName = exports.isValidEgressSetting = exports.isValidMemoryOption = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.endpointTriggerType = void 0;
|
|
4
4
|
const gcf = require("../../gcp/cloudfunctions");
|
|
5
5
|
const gcfV2 = require("../../gcp/cloudfunctionsv2");
|
|
6
|
+
const run = require("../../gcp/runv2");
|
|
6
7
|
const utils = require("../../utils");
|
|
7
8
|
const error_1 = require("../../error");
|
|
8
9
|
const functional_1 = require("../../functional");
|
|
10
|
+
const logger_1 = require("../../logger");
|
|
11
|
+
const experiments = require("../../experiments");
|
|
9
12
|
function endpointTriggerType(endpoint) {
|
|
10
13
|
if (isScheduleTriggered(endpoint)) {
|
|
11
14
|
return "scheduled";
|
|
@@ -43,12 +46,6 @@ function isValidEgressSetting(egress) {
|
|
|
43
46
|
return egress === "PRIVATE_RANGES_ONLY" || egress === "ALL_TRAFFIC";
|
|
44
47
|
}
|
|
45
48
|
exports.isValidEgressSetting = isValidEgressSetting;
|
|
46
|
-
exports.MIN_ATTEMPT_DEADLINE_SECONDS = 15;
|
|
47
|
-
exports.MAX_ATTEMPT_DEADLINE_SECONDS = 1800;
|
|
48
|
-
function isValidAttemptDeadline(seconds) {
|
|
49
|
-
return seconds >= exports.MIN_ATTEMPT_DEADLINE_SECONDS && seconds <= exports.MAX_ATTEMPT_DEADLINE_SECONDS;
|
|
50
|
-
}
|
|
51
|
-
exports.isValidAttemptDeadline = isValidAttemptDeadline;
|
|
52
49
|
function memoryOptionDisplayName(option) {
|
|
53
50
|
return {
|
|
54
51
|
128: "128MB",
|
|
@@ -184,7 +181,6 @@ function existingBackend(context, forceRefresh) {
|
|
|
184
181
|
}
|
|
185
182
|
exports.existingBackend = existingBackend;
|
|
186
183
|
async function loadExistingBackend(ctx) {
|
|
187
|
-
var _a;
|
|
188
184
|
const existingBackend = Object.assign({}, empty());
|
|
189
185
|
const unreachableRegions = {
|
|
190
186
|
gcfV1: [],
|
|
@@ -198,9 +194,23 @@ async function loadExistingBackend(ctx) {
|
|
|
198
194
|
existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint;
|
|
199
195
|
}
|
|
200
196
|
unreachableRegions.gcfV1 = gcfV1Results.unreachable;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
if (experiments.isEnabled("functionsrunapionly")) {
|
|
198
|
+
try {
|
|
199
|
+
const runServices = await run.listServices(ctx.projectId);
|
|
200
|
+
for (const service of runServices) {
|
|
201
|
+
const endpoint = run.endpointFromService(service);
|
|
202
|
+
existingBackend.endpoints[endpoint.region] =
|
|
203
|
+
existingBackend.endpoints[endpoint.region] || {};
|
|
204
|
+
existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
logger_1.logger.debug(err.message);
|
|
209
|
+
unreachableRegions.run = ["unknown"];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
const gcfV2Results = await gcfV2.listAllFunctions(ctx.projectId);
|
|
204
214
|
for (const apiFunction of gcfV2Results.functions) {
|
|
205
215
|
const endpoint = gcfV2.endpointFromFunction(apiFunction);
|
|
206
216
|
existingBackend.endpoints[endpoint.region] = existingBackend.endpoints[endpoint.region] || {};
|
|
@@ -208,13 +218,6 @@ async function loadExistingBackend(ctx) {
|
|
|
208
218
|
}
|
|
209
219
|
unreachableRegions.gcfV2 = gcfV2Results.unreachable;
|
|
210
220
|
}
|
|
211
|
-
catch (err) {
|
|
212
|
-
if (err.status === 404 && ((_a = err.message) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes("method not found"))) {
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
throw err;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
221
|
ctx.existingBackend = existingBackend;
|
|
219
222
|
ctx.unreachableRegions = unreachableRegions;
|
|
220
223
|
return ctx.existingBackend;
|
|
@@ -285,14 +285,6 @@ function discoverTrigger(endpoint, region, r) {
|
|
|
285
285
|
else if (endpoint.scheduleTrigger.retryConfig === null) {
|
|
286
286
|
bkSchedule.retryConfig = null;
|
|
287
287
|
}
|
|
288
|
-
if (typeof endpoint.scheduleTrigger.attemptDeadlineSeconds !== "undefined") {
|
|
289
|
-
const attemptDeadlineSeconds = r.resolveInt(endpoint.scheduleTrigger.attemptDeadlineSeconds);
|
|
290
|
-
if (attemptDeadlineSeconds !== null &&
|
|
291
|
-
!backend.isValidAttemptDeadline(attemptDeadlineSeconds)) {
|
|
292
|
-
throw new error_1.FirebaseError(`attemptDeadlineSeconds must be between ${backend.MIN_ATTEMPT_DEADLINE_SECONDS} and ${backend.MAX_ATTEMPT_DEADLINE_SECONDS} seconds (inclusive).`);
|
|
293
|
-
}
|
|
294
|
-
bkSchedule.attemptDeadlineSeconds = attemptDeadlineSeconds;
|
|
295
|
-
}
|
|
296
288
|
return { scheduleTrigger: bkSchedule };
|
|
297
289
|
}
|
|
298
290
|
else if ("taskQueueTrigger" in endpoint) {
|
|
@@ -143,7 +143,6 @@ function assertBuildEndpoint(ep, id) {
|
|
|
143
143
|
schedule: "Field<string>",
|
|
144
144
|
timeZone: "Field<string>?",
|
|
145
145
|
retryConfig: "object?",
|
|
146
|
-
attemptDeadlineSeconds: "Field<number>?",
|
|
147
146
|
});
|
|
148
147
|
if (ep.scheduleTrigger.retryConfig) {
|
|
149
148
|
(0, parsing_1.assertKeyTypes)(prefix + ".scheduleTrigger.retryConfig", ep.scheduleTrigger.retryConfig, {
|
|
@@ -240,7 +239,6 @@ function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
|
|
|
240
239
|
else if (ep.scheduleTrigger.retryConfig === null) {
|
|
241
240
|
st.retryConfig = null;
|
|
242
241
|
}
|
|
243
|
-
(0, proto_1.copyIfPresent)(st, ep.scheduleTrigger, "attemptDeadlineSeconds");
|
|
244
242
|
triggered = { scheduleTrigger: st };
|
|
245
243
|
}
|
|
246
244
|
else if (build.isTaskQueueTriggered(ep)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.secretsAreValid = exports.functionIdsAreValid = exports.functionsDirectoryExists = exports.endpointsAreUnique = exports.cpuConfigIsValid = exports.endpointsAreValid = void 0;
|
|
3
|
+
exports.secretsAreValid = exports.functionIdsAreValid = exports.functionsDirectoryExists = exports.endpointsAreUnique = exports.cpuConfigIsValid = exports.endpointsAreValid = exports.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = exports.DEFAULT_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const clc = require("colorette");
|
|
6
6
|
const error_1 = require("../../error");
|
|
@@ -11,6 +11,8 @@ const backend = require("./backend");
|
|
|
11
11
|
const utils = require("../../utils");
|
|
12
12
|
const secrets = require("../../functions/secrets");
|
|
13
13
|
const services_1 = require("./services");
|
|
14
|
+
exports.DEFAULT_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = 180;
|
|
15
|
+
exports.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = 1800;
|
|
14
16
|
function matchingIds(endpoints, filter) {
|
|
15
17
|
return endpoints
|
|
16
18
|
.filter(filter)
|
|
@@ -24,10 +26,21 @@ const cpu = (endpoint) => {
|
|
|
24
26
|
? backend.memoryToGen1Cpu(mem(endpoint))
|
|
25
27
|
: (_a = endpoint.cpu) !== null && _a !== void 0 ? _a : backend.memoryToGen2Cpu(mem(endpoint));
|
|
26
28
|
};
|
|
29
|
+
function validateScheduledTimeout(ep) {
|
|
30
|
+
if (backend.isScheduleTriggered(ep) &&
|
|
31
|
+
ep.timeoutSeconds &&
|
|
32
|
+
ep.timeoutSeconds > exports.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS) {
|
|
33
|
+
utils.logLabeledWarning("functions", `Scheduled function ${ep.id} has a timeout of ${ep.timeoutSeconds} seconds, ` +
|
|
34
|
+
`which exceeds the maximum attempt deadline of ${exports.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS} seconds for Cloud Scheduler. ` +
|
|
35
|
+
"This is probably not what you want! Having a timeout longer than the attempt deadline may lead to unexpected retries and multiple function executions. " +
|
|
36
|
+
`The attempt deadline will be capped at ${exports.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS} seconds.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
27
39
|
function endpointsAreValid(wantBackend) {
|
|
28
40
|
const endpoints = backend.allEndpoints(wantBackend);
|
|
29
41
|
functionIdsAreValid(endpoints);
|
|
30
42
|
for (const ep of endpoints) {
|
|
43
|
+
validateScheduledTimeout(ep);
|
|
31
44
|
(0, services_1.serviceForEndpoint)(ep).validateTrigger(ep, wantBackend);
|
|
32
45
|
}
|
|
33
46
|
const gcfV1WithConcurrency = matchingIds(endpoints, (endpoint) => (endpoint.concurrency || 1) !== 1 && endpoint.platform === "gcfv1");
|
package/lib/deploy/index.js
CHANGED
|
@@ -23,7 +23,7 @@ const DataConnectTarget = require("./dataconnect");
|
|
|
23
23
|
const AppHostingTarget = require("./apphosting");
|
|
24
24
|
const frameworks_1 = require("../frameworks");
|
|
25
25
|
const prepare_1 = require("./hosting/prepare");
|
|
26
|
-
const
|
|
26
|
+
const utils_2 = require("../utils");
|
|
27
27
|
const deploy_1 = require("../commands/deploy");
|
|
28
28
|
const requirePermissions_1 = require("../requirePermissions");
|
|
29
29
|
const context_1 = require("./dataconnect/context");
|
|
@@ -86,7 +86,7 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
86
86
|
await (0, requirePermissions_1.requirePermissions)(options, deploy_1.TARGET_PERMISSIONS["functions"]);
|
|
87
87
|
}
|
|
88
88
|
catch (e) {
|
|
89
|
-
if ((0,
|
|
89
|
+
if ((0, utils_2.isRunningInGithubAction)()) {
|
|
90
90
|
throw new error_1.FirebaseError("It looks like you are deploying a Hosting site along with Cloud Functions " +
|
|
91
91
|
"using a GitHub action version that did not include Cloud Functions " +
|
|
92
92
|
"permissions. Please reinstall the GitHub action with" +
|
|
@@ -54,36 +54,36 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "2.17.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v2.17.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.
|
|
57
|
+
"version": "2.17.3",
|
|
58
|
+
"expectedSize": 30053216,
|
|
59
|
+
"expectedChecksum": "b9bc9fefd65143b8ed3a5b0f0e1f26fa",
|
|
60
|
+
"expectedChecksumSHA256": "03766907fdbd513e333602f989ebaf486cd53a73d9cdefd221a02988f757a7cc",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v2.17.3",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.3"
|
|
63
63
|
},
|
|
64
64
|
"darwin_arm64": {
|
|
65
|
-
"version": "2.17.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v2.17.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.
|
|
65
|
+
"version": "2.17.3",
|
|
66
|
+
"expectedSize": 29510226,
|
|
67
|
+
"expectedChecksum": "a585f578ac6891366059a6c4814b10cc",
|
|
68
|
+
"expectedChecksumSHA256": "c1b6ad2976d6399435a11a9079a5e63e19b0af04c792cdb843a7464be793618c",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v2.17.3",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.3"
|
|
71
71
|
},
|
|
72
72
|
"win32": {
|
|
73
|
-
"version": "2.17.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v2.17.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.
|
|
73
|
+
"version": "2.17.3",
|
|
74
|
+
"expectedSize": 30547968,
|
|
75
|
+
"expectedChecksum": "5a9d3490a1de85978441afff8582e7c3",
|
|
76
|
+
"expectedChecksumSHA256": "8f2b8d38cbad8138cc33479456c91c5e4b046c689a4209a63fbb0c4dca9c6af8",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v2.17.3",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.3.exe"
|
|
79
79
|
},
|
|
80
80
|
"linux": {
|
|
81
|
-
"version": "2.17.
|
|
82
|
-
"expectedSize":
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v2.17.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.
|
|
81
|
+
"version": "2.17.3",
|
|
82
|
+
"expectedSize": 29974712,
|
|
83
|
+
"expectedChecksum": "aacf4f08de500df0913ff8ec6261a0ad",
|
|
84
|
+
"expectedChecksumSHA256": "2d593500745c4ce0d522d6f5f75c89ed413ad4853891b8afafb5cbfbb45d4abd",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v2.17.3",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.3"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
package/lib/experiments.js
CHANGED
|
@@ -6,7 +6,7 @@ const leven = require("leven");
|
|
|
6
6
|
const path_1 = require("path");
|
|
7
7
|
const configstore_1 = require("./configstore");
|
|
8
8
|
const error_1 = require("./error");
|
|
9
|
-
const
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
10
|
function experiments(exp) {
|
|
11
11
|
return Object.freeze(exp);
|
|
12
12
|
}
|
|
@@ -45,6 +45,10 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
45
45
|
shortDescription: "Functions created using the V2 API target Cloud Run Functions (not production ready)",
|
|
46
46
|
public: false,
|
|
47
47
|
},
|
|
48
|
+
functionsrunapionly: {
|
|
49
|
+
shortDescription: "Use Cloud Run API to list v2 functions",
|
|
50
|
+
public: false,
|
|
51
|
+
},
|
|
48
52
|
emulatoruisnapshot: {
|
|
49
53
|
shortDescription: "Load pre-release versions of the emulator UI",
|
|
50
54
|
},
|
|
@@ -179,7 +183,7 @@ function assertEnabled(name, task) {
|
|
|
179
183
|
var _a;
|
|
180
184
|
if (!isEnabled(name)) {
|
|
181
185
|
const prefix = `Cannot ${task} because the experiment ${(0, colorette_1.bold)(name)} is not enabled.`;
|
|
182
|
-
if ((0,
|
|
186
|
+
if ((0, utils_1.isRunningInGithubAction)()) {
|
|
183
187
|
const path = (_a = process.env.GITHUB_WORKFLOW_REF) === null || _a === void 0 ? void 0 : _a.split("@")[0];
|
|
184
188
|
const filename = path ? `.github/workflows/${(0, path_1.basename)(path)}` : "your action's yml";
|
|
185
189
|
const newValue = [process.env.FIREBASE_CLI_EXPERIMENTS, name].filter((it) => !!it).join(",");
|
|
@@ -179,6 +179,30 @@ async function build(dir, target, context) {
|
|
|
179
179
|
destination,
|
|
180
180
|
}));
|
|
181
181
|
const wantsBackend = reasonsForBackend.size > 0;
|
|
182
|
+
if (wantsBackend && (0, utils_2.isUsingAppDirectory)((0, path_1.join)(dir, distDir))) {
|
|
183
|
+
const nextVersion = (0, utils_2.getNextVersionRaw)(dir);
|
|
184
|
+
if (nextVersion && (0, utils_2.isNextJsVersionVulnerable)(nextVersion)) {
|
|
185
|
+
let message = `Next.js version ${nextVersion} is vulnerable to CVE-2025-66478.\n` +
|
|
186
|
+
`Please upgrade to a patched version: `;
|
|
187
|
+
const { major } = (0, semver_1.coerce)(nextVersion) || {};
|
|
188
|
+
if (major === 16) {
|
|
189
|
+
message += "16.0.7+.";
|
|
190
|
+
}
|
|
191
|
+
else if (major === 15) {
|
|
192
|
+
message += "15.0.5+, 15.1.9+, 15.2.6+, 15.3.6+, 15.4.8+, or 15.5.7+.";
|
|
193
|
+
}
|
|
194
|
+
else if (major === 14) {
|
|
195
|
+
message += "downgrade to a stable Next.js 14.x release.";
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
message +=
|
|
199
|
+
"15.0.5+, 15.1.9+, 15.2.6+, 15.3.6+, 15.4.8+, 15.5.7+, 16.0.7+ " +
|
|
200
|
+
"or downgrade to a stable Next.js 14.x release if using canary.";
|
|
201
|
+
}
|
|
202
|
+
message += `\nSee https://nextjs.org/blog/CVE-2025-66478 for more details.`;
|
|
203
|
+
throw new error_1.FirebaseError(message);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
182
206
|
if (wantsBackend) {
|
|
183
207
|
logger_1.logger.info("Building a Cloud Function to run this application. This is needed due to:");
|
|
184
208
|
for (const reason of Array.from(reasonsForBackend).slice(0, DEFAULT_NUMBER_OF_REASONS_TO_LIST)) {
|
|
@@ -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.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;
|
|
3
|
+
exports.isNextJsVersionVulnerable = exports.installEsbuild = exports.getGlobalEsbuildVersion = exports.findEsbuildPath = exports.whichNextConfigFile = exports.getProductionDistDirFiles = exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersionRaw = 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");
|
|
@@ -206,6 +206,11 @@ function getNextVersion(cwd) {
|
|
|
206
206
|
return nextVersionSemver.toString();
|
|
207
207
|
}
|
|
208
208
|
exports.getNextVersion = getNextVersion;
|
|
209
|
+
function getNextVersionRaw(cwd) {
|
|
210
|
+
const dependency = (0, utils_1.findDependency)("next", { cwd, depth: 0, omitDev: false });
|
|
211
|
+
return dependency === null || dependency === void 0 ? void 0 : dependency.version;
|
|
212
|
+
}
|
|
213
|
+
exports.getNextVersionRaw = getNextVersionRaw;
|
|
209
214
|
async function hasStaticAppNotFoundComponent(sourceDir, distDir) {
|
|
210
215
|
return (0, fs_extra_1.pathExists)((0, path_1.join)(sourceDir, distDir, "server", "app", "_not-found.html"));
|
|
211
216
|
}
|
|
@@ -296,3 +301,38 @@ function installEsbuild(version) {
|
|
|
296
301
|
}
|
|
297
302
|
}
|
|
298
303
|
exports.installEsbuild = installEsbuild;
|
|
304
|
+
function isNextJsVersionVulnerable(versionStr) {
|
|
305
|
+
const v = (0, semver_1.parse)(versionStr);
|
|
306
|
+
if (!v)
|
|
307
|
+
return false;
|
|
308
|
+
if (v.major === 15) {
|
|
309
|
+
if (v.minor === 0)
|
|
310
|
+
return (0, semver_1.lt)(versionStr, "15.0.5");
|
|
311
|
+
if (v.minor === 1)
|
|
312
|
+
return (0, semver_1.lt)(versionStr, "15.1.9");
|
|
313
|
+
if (v.minor === 2)
|
|
314
|
+
return (0, semver_1.lt)(versionStr, "15.2.6");
|
|
315
|
+
if (v.minor === 3)
|
|
316
|
+
return (0, semver_1.lt)(versionStr, "15.3.6");
|
|
317
|
+
if (v.minor === 4)
|
|
318
|
+
return (0, semver_1.lt)(versionStr, "15.4.8");
|
|
319
|
+
if (v.minor === 5)
|
|
320
|
+
return (0, semver_1.lt)(versionStr, "15.5.7");
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
if (v.major === 16) {
|
|
324
|
+
if (v.minor === 0)
|
|
325
|
+
return (0, semver_1.lt)(versionStr, "16.0.7");
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
if (v.major === 14) {
|
|
329
|
+
const pre = (0, semver_1.prerelease)(versionStr);
|
|
330
|
+
if (pre && pre.includes("canary")) {
|
|
331
|
+
if ((0, semver_1.gte)(versionStr, "14.3.0-canary.77")) {
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
exports.isNextJsVersionVulnerable = isNextJsVersionVulnerable;
|
|
@@ -6,10 +6,11 @@ const error_1 = require("../error");
|
|
|
6
6
|
const logger_1 = require("../logger");
|
|
7
7
|
const api_1 = require("../api");
|
|
8
8
|
const apiv2_1 = require("../apiv2");
|
|
9
|
+
const functional_1 = require("../functional");
|
|
10
|
+
const validate_1 = require("../deploy/functions/validate");
|
|
9
11
|
const backend = require("../deploy/functions/backend");
|
|
10
12
|
const proto = require("./proto");
|
|
11
13
|
const gce = require("../gcp/computeEngine");
|
|
12
|
-
const functional_1 = require("../functional");
|
|
13
14
|
const VERSION = "v1";
|
|
14
15
|
const DEFAULT_TIME_ZONE_V1 = "America/Los_Angeles";
|
|
15
16
|
const DEFAULT_TIME_ZONE_V2 = "UTC";
|
|
@@ -145,7 +146,13 @@ async function jobFromEndpoint(endpoint, location, projectNumber) {
|
|
|
145
146
|
}
|
|
146
147
|
job.schedule = endpoint.scheduleTrigger.schedule;
|
|
147
148
|
if (endpoint.platform === "gcfv2" || endpoint.platform === "run") {
|
|
148
|
-
proto.convertIfPresent(job, endpoint
|
|
149
|
+
proto.convertIfPresent(job, endpoint, "attemptDeadline", "timeoutSeconds", (timeout) => {
|
|
150
|
+
if (timeout === null) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const attemptDeadlineSeconds = Math.max(Math.min(timeout, validate_1.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS), validate_1.DEFAULT_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS);
|
|
154
|
+
return proto.durationFromSeconds(attemptDeadlineSeconds);
|
|
155
|
+
});
|
|
149
156
|
}
|
|
150
157
|
if (endpoint.scheduleTrigger.retryConfig) {
|
|
151
158
|
job.retryConfig = {};
|
package/lib/gcp/runv2.js
CHANGED
|
@@ -11,7 +11,7 @@ const backend = require("../deploy/functions/backend");
|
|
|
11
11
|
const constants_1 = require("../functions/constants");
|
|
12
12
|
const k8s_1 = require("./k8s");
|
|
13
13
|
const supported_1 = require("../deploy/functions/runtimes/supported");
|
|
14
|
-
const
|
|
14
|
+
const logger_1 = require("../logger");
|
|
15
15
|
const functional_1 = require("../functional");
|
|
16
16
|
exports.API_VERSION = "v2";
|
|
17
17
|
const client = new apiv2_1.Client({
|
|
@@ -99,7 +99,7 @@ function endpointFromService(service) {
|
|
|
99
99
|
svcId;
|
|
100
100
|
const memory = (0, k8s_1.mebibytes)(service.template.containers[0].resources.limits.memory);
|
|
101
101
|
if (!backend.isValidMemoryOption(memory)) {
|
|
102
|
-
|
|
102
|
+
logger_1.logger.debug("Converting a service to an endpoint with an invalid memory option", memory);
|
|
103
103
|
}
|
|
104
104
|
const cpu = Number(service.template.containers[0].resources.limits.cpu);
|
|
105
105
|
const endpoint = Object.assign({ platform: ((_e = service.labels) === null || _e === void 0 ? void 0 : _e[exports.CLIENT_NAME_LABEL]) === "cloud-functions" ? "gcfv2" : "run", id,
|
package/lib/gcp/storage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getDownloadUrl = exports.getServiceAccount = exports.listBuckets = exports.upsertBucket = exports.randomString = exports.patchBucket = exports.createBucket = exports.getBucket = exports.deleteObject = exports.getObject = exports.uploadObject = exports.upload = exports.getDefaultBucket = void 0;
|
|
3
|
+
exports.getDownloadUrl = exports.getServiceAccount = exports.listBuckets = exports.upsertBucket = exports.randomString = exports.patchBucket = exports.createBucket = exports.getBucket = exports.deleteObject = exports.getObject = exports.uploadObject = exports.upload = exports.getDefaultBucket = exports.ContentType = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const clc = require("colorette");
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
@@ -11,6 +11,11 @@ const logger_1 = require("../logger");
|
|
|
11
11
|
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
12
12
|
const utils = require("../utils");
|
|
13
13
|
const proto_1 = require("./proto");
|
|
14
|
+
var ContentType;
|
|
15
|
+
(function (ContentType) {
|
|
16
|
+
ContentType["ZIP"] = "ZIP";
|
|
17
|
+
ContentType["TAR"] = "TAR";
|
|
18
|
+
})(ContentType = exports.ContentType || (exports.ContentType = {}));
|
|
14
19
|
async function getDefaultBucket(projectId) {
|
|
15
20
|
var _a;
|
|
16
21
|
await (0, ensureApiEnabled_1.ensure)(projectId, (0, api_1.firebaseStorageOrigin)(), "storage", false);
|
|
@@ -54,9 +59,17 @@ async function upload(source, uploadUrl, extraHeaders, ignoreQuotaProject) {
|
|
|
54
59
|
};
|
|
55
60
|
}
|
|
56
61
|
exports.upload = upload;
|
|
57
|
-
async function uploadObject(source, bucketName) {
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
async function uploadObject(source, bucketName, contentType) {
|
|
63
|
+
switch (contentType) {
|
|
64
|
+
case ContentType.TAR:
|
|
65
|
+
if (!source.file.endsWith(".tar.gz")) {
|
|
66
|
+
throw new error_1.FirebaseError(`Expected a file name ending in .tar.gz, got ${source.file}`);
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
default:
|
|
70
|
+
if (path.extname(source.file) !== ".zip") {
|
|
71
|
+
throw new error_1.FirebaseError(`Expected a file name ending in .zip, got ${source.file}`);
|
|
72
|
+
}
|
|
60
73
|
}
|
|
61
74
|
const localAPIClient = new apiv2_1.Client({ urlPrefix: (0, api_1.storageOrigin)() });
|
|
62
75
|
const location = `/${bucketName}/${path.basename(source.file)}`;
|
|
@@ -64,7 +77,7 @@ async function uploadObject(source, bucketName) {
|
|
|
64
77
|
method: "PUT",
|
|
65
78
|
path: location,
|
|
66
79
|
headers: {
|
|
67
|
-
"Content-Type": "application/zip",
|
|
80
|
+
"Content-Type": contentType === ContentType.TAR ? "application/octet-stream" : "application/zip",
|
|
68
81
|
"x-goog-content-length-range": "0,123289600",
|
|
69
82
|
},
|
|
70
83
|
body: source.stream,
|
|
@@ -463,10 +463,6 @@ async function promptForCloudSQL(setup, info) {
|
|
|
463
463
|
}
|
|
464
464
|
if (freeTrialUsed) {
|
|
465
465
|
(0, utils_1.logLabeledWarning)("dataconnect", "CloudSQL no cost trial has already been used on this project.");
|
|
466
|
-
if (!billingEnabled) {
|
|
467
|
-
setup.instructions.push((0, freeTrial_1.upgradeInstructions)(setup.projectId || "your-firebase-project", true));
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
466
|
}
|
|
471
467
|
else if (instrumentlessTrialEnabled || billingEnabled) {
|
|
472
468
|
(0, utils_1.logLabeledSuccess)("dataconnect", "CloudSQL no cost trial available!");
|
|
@@ -487,7 +483,11 @@ async function promptForCloudSQL(setup, info) {
|
|
|
487
483
|
choices.push({ name: "Create a new free trial instance", value: "", location: "" });
|
|
488
484
|
}
|
|
489
485
|
else {
|
|
490
|
-
choices.push({
|
|
486
|
+
choices.push({
|
|
487
|
+
name: `Create a new CloudSQL instance${billingEnabled ? "" : " (requires billing account)"}`,
|
|
488
|
+
value: "",
|
|
489
|
+
location: "",
|
|
490
|
+
});
|
|
491
491
|
}
|
|
492
492
|
info.cloudSqlInstanceId = await (0, prompt_1.select)({
|
|
493
493
|
message: `Which CloudSQL instance would you like to use?`,
|
|
@@ -499,6 +499,10 @@ async function promptForCloudSQL(setup, info) {
|
|
|
499
499
|
}
|
|
500
500
|
else {
|
|
501
501
|
info.flow += "_pick_new_csql";
|
|
502
|
+
if (!billingEnabled && freeTrialUsed) {
|
|
503
|
+
setup.instructions.push((0, freeTrial_1.upgradeInstructions)(setup.projectId || "your-firebase-project", true));
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
502
506
|
info.cloudSqlInstanceId = await (0, prompt_1.input)({
|
|
503
507
|
message: `What ID would you like to use for your new CloudSQL instance?`,
|
|
504
508
|
default: (0, utils_1.newUniqueId)(`${defaultServiceId().toLowerCase()}-fdc`, instances.map((i) => i.name)),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.initGitHub = void 0;
|
|
4
4
|
const colorette_1 = require("colorette");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const yaml = require("yaml");
|
|
@@ -389,7 +389,3 @@ async function encryptServiceAccountJSON(serviceAccountJSON, key) {
|
|
|
389
389
|
const encryptedBytes = libsodium.crypto_box_seal(messageBytes, keyBytes);
|
|
390
390
|
return Buffer.from(encryptedBytes).toString("base64");
|
|
391
391
|
}
|
|
392
|
-
function isRunningInGithubAction() {
|
|
393
|
-
return process.env.GITHUB_ACTION_REPOSITORY === HOSTING_GITHUB_ACTION_NAME.split("@")[0];
|
|
394
|
-
}
|
|
395
|
-
exports.isRunningInGithubAction = isRunningInGithubAction;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.get_environment = void 0;
|
|
3
|
+
exports.get_environment = exports.hydrateTemplate = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const tool_1 = require("../../tool");
|
|
6
6
|
const util_1 = require("../../util");
|
|
@@ -9,6 +9,48 @@ const js_yaml_1 = require("js-yaml");
|
|
|
9
9
|
const auth_1 = require("../../../auth");
|
|
10
10
|
const configstore_1 = require("../../../configstore");
|
|
11
11
|
const appUtils_1 = require("../../../appUtils");
|
|
12
|
+
function hydrateTemplate(config) {
|
|
13
|
+
const activeProject = config.projectId
|
|
14
|
+
? `${config.projectId}${config.projectAliases.length ? ` (alias: ${config.projectAliases.join(",")})` : ""}`
|
|
15
|
+
: "<NONE>";
|
|
16
|
+
const projectConfigPath = config.projectConfigPath || "<NO CONFIG PRESENT>";
|
|
17
|
+
const geminiTosAccepted = config.geminiTosAccepted ? "Accepted" : "<NOT ACCEPTED>";
|
|
18
|
+
const authenticatedUser = config.authenticatedUser || "<NONE>";
|
|
19
|
+
const detectedApps = Object.entries(config.detectedAppIds).length > 0
|
|
20
|
+
? `\n\n${(0, js_yaml_1.dump)(config.detectedAppIds).trim()}\n`
|
|
21
|
+
: "<NONE>";
|
|
22
|
+
const availableProjects = Object.entries(config.projectAliasMap).length > 0
|
|
23
|
+
? `\n\n${(0, js_yaml_1.dump)(config.projectAliasMap)}`
|
|
24
|
+
: "<NONE>";
|
|
25
|
+
const hasOtherAccounts = config.allAccounts.filter((email) => email !== config.authenticatedUser).length > 0;
|
|
26
|
+
const availableAccounts = hasOtherAccounts ? `${(0, js_yaml_1.dump)(config.allAccounts).trim()}` : "";
|
|
27
|
+
return `# Environment Information
|
|
28
|
+
|
|
29
|
+
Project Directory: ${config.projectDir}
|
|
30
|
+
Project Config Path: ${projectConfigPath}
|
|
31
|
+
Active Project ID: ${activeProject}
|
|
32
|
+
Gemini in Firebase Terms of Service: ${geminiTosAccepted}
|
|
33
|
+
Authenticated User: ${authenticatedUser}
|
|
34
|
+
Detected App IDs: ${detectedApps}
|
|
35
|
+
Available Project Aliases (format: '[alias]: [projectId]'): ${availableProjects}${hasOtherAccounts ? `\nAvailable Accounts: \n\n${availableAccounts}` : ""}
|
|
36
|
+
${config.projectFileContents
|
|
37
|
+
? `\nfirebase.json contents:
|
|
38
|
+
|
|
39
|
+
\`\`\`json
|
|
40
|
+
${config.projectFileContents}
|
|
41
|
+
\`\`\``
|
|
42
|
+
: `\nNo firebase.json file was found.
|
|
43
|
+
|
|
44
|
+
If this project does not use Firebase services that require a firebase.json file, no action is necessary.
|
|
45
|
+
|
|
46
|
+
If this project uses Firebase services that require a firebase.json file, the user will most likely want to:
|
|
47
|
+
|
|
48
|
+
a) Change the project directory using the 'firebase_update_environment' tool to select a directory with a 'firebase.json' file in it, or
|
|
49
|
+
b) Initialize a new Firebase project directory using the 'firebase_init' tool.
|
|
50
|
+
|
|
51
|
+
Confirm with the user before taking action.`}`;
|
|
52
|
+
}
|
|
53
|
+
exports.hydrateTemplate = hydrateTemplate;
|
|
12
54
|
exports.get_environment = (0, tool_1.tool)("core", {
|
|
13
55
|
name: "get_environment",
|
|
14
56
|
description: "Use this to retrieve the current Firebase **environment** configuration for the Firebase CLI and Firebase MCP server, including current authenticated user, project directory, active Firebase Project, and more.",
|
|
@@ -27,45 +69,26 @@ exports.get_environment = (0, tool_1.tool)("core", {
|
|
|
27
69
|
const projectFileExists = config.projectFileExists("firebase.json");
|
|
28
70
|
const detectedApps = await (0, appUtils_1.detectApps)(process.cwd());
|
|
29
71
|
const allAccounts = (0, auth_1.getAllAccounts)().map((account) => account.user.email);
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
? config.path("firebase.json")
|
|
33
|
-
: "<NO CONFIG PRESENT>";
|
|
34
|
-
const detectedAppsMap = detectedApps
|
|
72
|
+
const detectedAppsMap = {};
|
|
73
|
+
detectedApps
|
|
35
74
|
.filter((app) => !!app.appId)
|
|
36
75
|
.reduce((map, app) => {
|
|
37
76
|
if (app.appId) {
|
|
38
|
-
map
|
|
77
|
+
map[app.appId] = app.bundleId ? app.bundleId : "<UNKNOWN BUNDLE ID>";
|
|
39
78
|
}
|
|
40
79
|
return map;
|
|
41
|
-
},
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
${projectFileExists
|
|
56
|
-
? `\nfirebase.json contents:
|
|
57
|
-
|
|
58
|
-
\`\`\`json
|
|
59
|
-
${config.readProjectFile("firebase.json")}
|
|
60
|
-
\`\`\``
|
|
61
|
-
: `\nNo firebase.json file was found.
|
|
62
|
-
|
|
63
|
-
If this project does not use Firebase services that require a firebase.json file, no action is necessary.
|
|
64
|
-
|
|
65
|
-
If this project uses Firebase services that require a firebase.json file, the user will most likely want to:
|
|
66
|
-
|
|
67
|
-
a) Change the project directory using the 'firebase_update_environment' tool to select a directory with a 'firebase.json' file in it, or
|
|
68
|
-
b) Initialize a new Firebase project directory using the 'firebase_init' tool.
|
|
69
|
-
|
|
70
|
-
Confirm with the user before taking action.`}`);
|
|
80
|
+
}, detectedAppsMap);
|
|
81
|
+
const environmentTemplateConfig = {
|
|
82
|
+
projectId,
|
|
83
|
+
projectAliases: aliases,
|
|
84
|
+
projectDir: host.cachedProjectDir,
|
|
85
|
+
projectConfigPath: projectFileExists ? config.path("firebase.json") : undefined,
|
|
86
|
+
geminiTosAccepted,
|
|
87
|
+
authenticatedUser: accountEmail || undefined,
|
|
88
|
+
projectAliasMap: rc.projects,
|
|
89
|
+
allAccounts,
|
|
90
|
+
detectedAppIds: detectedAppsMap,
|
|
91
|
+
projectFileContents: projectFileExists ? config.readProjectFile("firebase.json") : undefined,
|
|
92
|
+
};
|
|
93
|
+
return (0, util_1.toContent)(hydrateTemplate(environmentTemplateConfig));
|
|
71
94
|
});
|
|
@@ -46,22 +46,10 @@ async function androidAppUsesCrashlytics(appPath) {
|
|
|
46
46
|
return false;
|
|
47
47
|
}
|
|
48
48
|
async function iosAppUsesCrashlytics(appPath) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
const swiftPackageFiles = await (0, appUtils_1.detectFiles)(appPath, "Package.swift");
|
|
57
|
-
for (const file of swiftPackageFiles) {
|
|
58
|
-
const content = await fs.readFile(path.join(appPath, file), "utf8");
|
|
59
|
-
if (content.includes("Crashlytics")) {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
const cartFiles = await (0, appUtils_1.detectFiles)(appPath, "Cartfile*");
|
|
64
|
-
for (const file of cartFiles) {
|
|
49
|
+
const filePatternsToDetect = ["Podfile", "Package.swift", "Cartfile*", "project.pbxproj"];
|
|
50
|
+
const fileArrays = await Promise.all(filePatternsToDetect.map((term) => (0, appUtils_1.detectFiles)(appPath, term)));
|
|
51
|
+
const files = fileArrays.flat();
|
|
52
|
+
for (const file of files) {
|
|
65
53
|
const content = await fs.readFile(path.join(appPath, file), "utf8");
|
|
66
54
|
if (content.includes("Crashlytics")) {
|
|
67
55
|
return true;
|
package/lib/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.resolveWithin = exports.commandExistsSync = exports.newUniqueId = exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generatePassword = exports.generateId = exports.wrappedSafeLoad = exports.readFileFromDirectory = exports.getHostnameFromUrl = void 0;
|
|
3
|
+
exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInGithubAction = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.sleep = exports.promiseWithSpinner = exports.tryParse = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarningToStderr = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.setVSCodeEnvVars = exports.getInheritedOption = exports.consoleUrl = exports.vscodeEnvVars = exports.envOverrides = exports.IS_WINDOWS = void 0;
|
|
4
|
+
exports.resolveWithin = exports.commandExistsSync = exports.newUniqueId = exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generatePassword = exports.generateId = exports.wrappedSafeLoad = exports.readFileFromDirectory = exports.getHostnameFromUrl = exports.openInBrowserPopup = void 0;
|
|
5
5
|
const fs = require("fs-extra");
|
|
6
6
|
const tty = require("tty");
|
|
7
7
|
const path = require("node:path");
|
|
@@ -367,6 +367,10 @@ function isRunningInWSL() {
|
|
|
367
367
|
return !!process.env.WSL_DISTRO_NAME;
|
|
368
368
|
}
|
|
369
369
|
exports.isRunningInWSL = isRunningInWSL;
|
|
370
|
+
function isRunningInGithubAction() {
|
|
371
|
+
return process.env.GITHUB_ACTION_REPOSITORY === "FirebaseExtended/action-hosting-deploy";
|
|
372
|
+
}
|
|
373
|
+
exports.isRunningInGithubAction = isRunningInGithubAction;
|
|
370
374
|
function thirtyDaysFromNow() {
|
|
371
375
|
return new Date(Date.now() + THIRTY_DAYS_IN_MILLISECONDS);
|
|
372
376
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.27.0",
|
|
4
4
|
"description": "Command-Line Interface for Firebase",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
|
+
"mcpName": "io.github.firebase/firebase-mcp",
|
|
6
7
|
"bin": {
|
|
7
8
|
"firebase": "./lib/bin/firebase.js"
|
|
8
9
|
},
|