firebase-tools 10.6.0 → 10.7.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/command.js +4 -4
- package/lib/commands/deploy.js +1 -1
- package/lib/commands/emulators-start.js +7 -2
- package/lib/commands/ext-configure.js +15 -5
- package/lib/commands/ext-export.js +6 -5
- package/lib/commands/ext-install.js +28 -44
- package/lib/commands/ext-update.js +9 -1
- package/lib/commands/functions-delete.js +2 -5
- package/lib/commands/hosting-channel-deploy.js +2 -2
- package/lib/deploy/database/deploy.js +4 -0
- package/lib/deploy/database/index.js +1 -0
- package/lib/deploy/extensions/deploy.js +4 -4
- package/lib/deploy/extensions/deploymentSummary.js +8 -5
- package/lib/deploy/extensions/planner.js +36 -9
- package/lib/deploy/extensions/prepare.js +1 -1
- package/lib/deploy/extensions/secrets.js +2 -2
- package/lib/deploy/extensions/tasks.js +60 -21
- package/lib/deploy/functions/backend.js +17 -2
- package/lib/deploy/functions/build.js +162 -0
- package/lib/deploy/functions/checkIam.js +6 -5
- package/lib/deploy/functions/deploy.js +14 -15
- package/lib/deploy/functions/ensure.js +4 -4
- package/lib/deploy/functions/functionsDeployHelper.js +54 -23
- package/lib/deploy/functions/prepare.js +85 -42
- package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
- package/lib/deploy/functions/pricing.js +6 -3
- package/lib/deploy/functions/prompts.js +1 -7
- package/lib/deploy/functions/release/fabricator.js +43 -2
- package/lib/deploy/functions/release/index.js +10 -6
- package/lib/deploy/functions/release/planner.js +9 -6
- package/lib/deploy/functions/release/reporter.js +14 -11
- package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +50 -3
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +24 -5
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/services/auth.js +95 -0
- package/lib/deploy/functions/services/index.js +41 -21
- package/lib/deploy/functions/validate.js +8 -5
- package/lib/deploy/hosting/args.js +2 -0
- package/lib/deploy/hosting/convertConfig.js +37 -8
- package/lib/deploy/hosting/deploy.js +3 -3
- package/lib/deploy/hosting/prepare.js +2 -2
- package/lib/deploy/hosting/release.js +6 -2
- package/lib/deploy/index.js +82 -93
- package/lib/deploy/remoteconfig/deploy.js +4 -0
- package/lib/deploy/remoteconfig/index.js +3 -1
- package/lib/emulator/auth/operations.js +5 -0
- package/lib/emulator/auth/utils.js +3 -25
- package/lib/emulator/controller.js +5 -5
- package/lib/emulator/downloadableEmulators.js +39 -23
- package/lib/emulator/extensions/validation.js +2 -2
- package/lib/emulator/extensionsEmulator.js +85 -21
- package/lib/emulator/functionsEmulator.js +79 -7
- package/lib/emulator/functionsEmulatorShared.js +20 -1
- package/lib/emulator/registry.js +34 -12
- package/lib/emulator/storage/apis/firebase.js +7 -2
- package/lib/emulator/storage/apis/gcloud.js +6 -3
- package/lib/emulator/storage/files.js +9 -1
- package/lib/ensureApiEnabled.js +8 -4
- package/lib/extensions/changelog.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +4 -3
- package/lib/extensions/emulator/specHelper.js +7 -1
- package/lib/extensions/extensionsHelper.js +30 -24
- package/lib/extensions/manifest.js +27 -7
- package/lib/extensions/paramHelper.js +5 -5
- package/lib/extensions/provisioningHelper.js +2 -2
- package/lib/extensions/warnings.js +3 -3
- package/lib/functions/events/index.js +7 -0
- package/lib/functions/events/v1.js +6 -0
- package/lib/functions/projectConfig.js +24 -3
- package/lib/gcp/cloudfunctions.js +31 -5
- package/lib/gcp/cloudfunctionsv2.js +27 -2
- package/lib/gcp/identityPlatform.js +44 -0
- package/lib/gcp/secretManager.js +1 -1
- package/lib/metaprogramming.js +2 -0
- package/lib/previews.js +1 -1
- package/lib/serve/hosting.js +25 -12
- package/lib/serve/index.js +6 -0
- package/lib/track.js +15 -21
- package/npm-shrinkwrap.json +44 -2
- package/package.json +4 -1
- package/schema/firebase-config.json +6 -0
|
@@ -14,31 +14,26 @@ const functionsConfig = require("../../functionsConfig");
|
|
|
14
14
|
const utils = require("../../utils");
|
|
15
15
|
const fsAsync = require("../../fsAsync");
|
|
16
16
|
const CONFIG_DEST_FILE = ".runtimeconfig.json";
|
|
17
|
-
async function getFunctionsConfig(
|
|
17
|
+
async function getFunctionsConfig(projectId) {
|
|
18
18
|
var _a, _b;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
try {
|
|
20
|
+
return await functionsConfig.materializeAll(projectId);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
logger_1.logger.debug(err);
|
|
24
|
+
let errorCode = (_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode;
|
|
25
|
+
if (!errorCode) {
|
|
26
|
+
logger_1.logger.debug("Got unexpected error from Runtime Config; it has no status code:", err);
|
|
27
|
+
errorCode = 500;
|
|
23
28
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
errorCode = 500;
|
|
30
|
-
}
|
|
31
|
-
if (errorCode === 500 || errorCode === 503) {
|
|
32
|
-
throw new error_1.FirebaseError("Cloud Runtime Config is currently experiencing issues, " +
|
|
33
|
-
"which is preventing your functions from being deployed. " +
|
|
34
|
-
"Please wait a few minutes and then try to deploy your functions again." +
|
|
35
|
-
"\nRun `firebase deploy --except functions` if you want to continue deploying the rest of your project.");
|
|
36
|
-
}
|
|
37
|
-
config = {};
|
|
29
|
+
if (errorCode === 500 || errorCode === 503) {
|
|
30
|
+
throw new error_1.FirebaseError("Cloud Runtime Config is currently experiencing issues, " +
|
|
31
|
+
"which is preventing your functions from being deployed. " +
|
|
32
|
+
"Please wait a few minutes and then try to deploy your functions again." +
|
|
33
|
+
"\nRun `firebase deploy --except functions` if you want to continue deploying the rest of your project.");
|
|
38
34
|
}
|
|
39
35
|
}
|
|
40
|
-
|
|
41
|
-
return config;
|
|
36
|
+
return {};
|
|
42
37
|
}
|
|
43
38
|
exports.getFunctionsConfig = getFunctionsConfig;
|
|
44
39
|
async function pipeAsync(from, to) {
|
|
@@ -93,14 +93,17 @@ exports.V2_FREE_TIER = {
|
|
|
93
93
|
vCpu: 180000,
|
|
94
94
|
egress: 1,
|
|
95
95
|
};
|
|
96
|
+
const VCPU_TO_GHZ = 2.4;
|
|
96
97
|
const MB_TO_GHZ = {
|
|
97
98
|
128: 0.2,
|
|
98
99
|
256: 0.4,
|
|
99
100
|
512: 0.8,
|
|
100
101
|
1024: 1.4,
|
|
101
|
-
2048:
|
|
102
|
-
4096:
|
|
103
|
-
8192:
|
|
102
|
+
2048: 1 * VCPU_TO_GHZ,
|
|
103
|
+
4096: 2 * VCPU_TO_GHZ,
|
|
104
|
+
8192: 2 * VCPU_TO_GHZ,
|
|
105
|
+
16384: 4 * VCPU_TO_GHZ,
|
|
106
|
+
32768: 8 * VCPU_TO_GHZ,
|
|
104
107
|
};
|
|
105
108
|
function canCalculateMinInstanceCost(endpoint) {
|
|
106
109
|
if (!endpoint.minInstances) {
|
|
@@ -136,19 +136,13 @@ async function promptForMinInstances(options, want, have) {
|
|
|
136
136
|
const cost = pricing.monthlyMinInstanceCost(backend.allEndpoints(want)).toFixed(2);
|
|
137
137
|
costLine = `With these options, your minimum bill will be $${cost} in a 30-day month`;
|
|
138
138
|
}
|
|
139
|
-
let cudAnnotation = "";
|
|
140
|
-
if (backend.someEndpoint(want, (fn) => fn.platform === "gcfv2" && !!fn.minInstances)) {
|
|
141
|
-
cudAnnotation =
|
|
142
|
-
"\nThis bill can be lowered with a one year commitment. See https://cloud.google.com/run/cud for more";
|
|
143
|
-
}
|
|
144
139
|
const warnMessage = "The following functions have reserved minimum instances. This will " +
|
|
145
140
|
"reduce the frequency of cold starts but increases the minimum cost. " +
|
|
146
141
|
"You will be charged for the memory allocation and a fraction of the " +
|
|
147
142
|
"CPU allocation of instances while they are idle.\n\n" +
|
|
148
143
|
functionLines +
|
|
149
144
|
"\n\n" +
|
|
150
|
-
costLine
|
|
151
|
-
cudAnnotation;
|
|
145
|
+
costLine;
|
|
152
146
|
utils.logLabeledWarning("functions", warnMessage);
|
|
153
147
|
const proceed = await (0, prompt_1.promptOnce)({
|
|
154
148
|
type: "confirm",
|
|
@@ -21,6 +21,8 @@ const reporter = require("./reporter");
|
|
|
21
21
|
const run = require("../../../gcp/run");
|
|
22
22
|
const scheduler = require("../../../gcp/cloudscheduler");
|
|
23
23
|
const utils = require("../../../utils");
|
|
24
|
+
const services = require("../services");
|
|
25
|
+
const v1_1 = require("../../../functions/events/v1");
|
|
24
26
|
const gcfV1PollerOptions = {
|
|
25
27
|
apiOrigin: api_1.functionsOrigin,
|
|
26
28
|
apiVersion: gcf.API_VERSION,
|
|
@@ -35,6 +37,7 @@ const gcfV2PollerOptions = {
|
|
|
35
37
|
};
|
|
36
38
|
const DEFAULT_GCFV2_CONCURRENCY = 80;
|
|
37
39
|
const rethrowAs = (endpoint, op) => (err) => {
|
|
40
|
+
logger_1.logger.error(err.message);
|
|
38
41
|
throw new reporter.DeploymentError(endpoint, op, err);
|
|
39
42
|
};
|
|
40
43
|
class Fabricator {
|
|
@@ -195,6 +198,14 @@ class Fabricator {
|
|
|
195
198
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
196
199
|
}
|
|
197
200
|
}
|
|
201
|
+
else if (backend.isBlockingTriggered(endpoint) &&
|
|
202
|
+
v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
|
|
203
|
+
await this.executor
|
|
204
|
+
.run(async () => {
|
|
205
|
+
await gcf.setInvokerCreate(endpoint.project, backend.functionName(endpoint), ["public"]);
|
|
206
|
+
})
|
|
207
|
+
.catch(rethrowAs(endpoint, "set invoker"));
|
|
208
|
+
}
|
|
198
209
|
}
|
|
199
210
|
async createV2Function(endpoint) {
|
|
200
211
|
var _a;
|
|
@@ -202,7 +213,7 @@ class Fabricator {
|
|
|
202
213
|
logger_1.logger.debug("Precondition failed. Cannot create a GCFv2 function without storage");
|
|
203
214
|
throw new Error("Precondition failed");
|
|
204
215
|
}
|
|
205
|
-
const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage
|
|
216
|
+
const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage);
|
|
206
217
|
const topic = (_a = apiFunction.eventTrigger) === null || _a === void 0 ? void 0 : _a.pubsubTopic;
|
|
207
218
|
if (topic) {
|
|
208
219
|
await this.executor
|
|
@@ -252,6 +263,12 @@ class Fabricator {
|
|
|
252
263
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
253
264
|
}
|
|
254
265
|
}
|
|
266
|
+
else if (backend.isBlockingTriggered(endpoint) &&
|
|
267
|
+
v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
|
|
268
|
+
await this.executor
|
|
269
|
+
.run(() => run.setInvokerCreate(endpoint.project, serviceName, ["public"]))
|
|
270
|
+
.catch(rethrowAs(endpoint, "set invoker"));
|
|
271
|
+
}
|
|
255
272
|
const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
|
|
256
273
|
if (mem >= backend.MIN_MEMORY_FOR_CONCURRENCY && endpoint.concurrency !== 1) {
|
|
257
274
|
await this.setConcurrency(endpoint, serviceName, endpoint.concurrency || DEFAULT_GCFV2_CONCURRENCY);
|
|
@@ -279,6 +296,10 @@ class Fabricator {
|
|
|
279
296
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
280
297
|
invoker = endpoint.taskQueueTrigger.invoker;
|
|
281
298
|
}
|
|
299
|
+
else if (backend.isBlockingTriggered(endpoint) &&
|
|
300
|
+
v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
|
|
301
|
+
invoker = ["public"];
|
|
302
|
+
}
|
|
282
303
|
if (invoker) {
|
|
283
304
|
await this.executor
|
|
284
305
|
.run(() => gcf.setInvokerUpdate(endpoint.project, backend.functionName(endpoint), invoker))
|
|
@@ -291,7 +312,7 @@ class Fabricator {
|
|
|
291
312
|
logger_1.logger.debug("Precondition failed. Cannot update a GCFv2 function without storage");
|
|
292
313
|
throw new Error("Precondition failed");
|
|
293
314
|
}
|
|
294
|
-
const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage
|
|
315
|
+
const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage);
|
|
295
316
|
if ((_a = apiFunction.eventTrigger) === null || _a === void 0 ? void 0 : _a.pubsubTopic) {
|
|
296
317
|
delete apiFunction.eventTrigger.pubsubTopic;
|
|
297
318
|
}
|
|
@@ -310,6 +331,10 @@ class Fabricator {
|
|
|
310
331
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
311
332
|
invoker = endpoint.taskQueueTrigger.invoker;
|
|
312
333
|
}
|
|
334
|
+
else if (backend.isBlockingTriggered(endpoint) &&
|
|
335
|
+
v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
|
|
336
|
+
invoker = ["public"];
|
|
337
|
+
}
|
|
313
338
|
if (invoker) {
|
|
314
339
|
await this.executor
|
|
315
340
|
.run(() => run.setInvokerUpdate(endpoint.project, serviceName, invoker))
|
|
@@ -369,6 +394,9 @@ class Fabricator {
|
|
|
369
394
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
370
395
|
await this.upsertTaskQueue(endpoint);
|
|
371
396
|
}
|
|
397
|
+
else if (backend.isBlockingTriggered(endpoint)) {
|
|
398
|
+
await this.registerBlockingTrigger(endpoint);
|
|
399
|
+
}
|
|
372
400
|
}
|
|
373
401
|
async deleteTrigger(endpoint) {
|
|
374
402
|
if (backend.isScheduleTriggered(endpoint)) {
|
|
@@ -385,6 +413,9 @@ class Fabricator {
|
|
|
385
413
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
386
414
|
await this.disableTaskQueue(endpoint);
|
|
387
415
|
}
|
|
416
|
+
else if (backend.isBlockingTriggered(endpoint)) {
|
|
417
|
+
await this.unregisterBlockingTrigger(endpoint);
|
|
418
|
+
}
|
|
388
419
|
}
|
|
389
420
|
async upsertScheduleV1(endpoint) {
|
|
390
421
|
const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
|
|
@@ -406,6 +437,11 @@ class Fabricator {
|
|
|
406
437
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
407
438
|
}
|
|
408
439
|
}
|
|
440
|
+
async registerBlockingTrigger(endpoint) {
|
|
441
|
+
await this.executor
|
|
442
|
+
.run(() => services.serviceForEndpoint(endpoint).registerTrigger(endpoint))
|
|
443
|
+
.catch(rethrowAs(endpoint, "register blocking trigger"));
|
|
444
|
+
}
|
|
409
445
|
async deleteScheduleV1(endpoint) {
|
|
410
446
|
const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
|
|
411
447
|
await this.executor
|
|
@@ -427,6 +463,11 @@ class Fabricator {
|
|
|
427
463
|
.run(() => cloudtasks.updateQueue(update))
|
|
428
464
|
.catch(rethrowAs(endpoint, "disable task queue"));
|
|
429
465
|
}
|
|
466
|
+
async unregisterBlockingTrigger(endpoint) {
|
|
467
|
+
await this.executor
|
|
468
|
+
.run(() => services.serviceForEndpoint(endpoint).unregisterTrigger(endpoint))
|
|
469
|
+
.catch(rethrowAs(endpoint, "unregister blocking trigger"));
|
|
470
|
+
}
|
|
430
471
|
logOpStart(op, endpoint) {
|
|
431
472
|
const runtime = (0, runtimes_1.getHumanFriendlyRuntimeName)(endpoint.runtime);
|
|
432
473
|
const label = helper.getFunctionLabel(endpoint);
|
|
@@ -21,7 +21,11 @@ async function release(context, options, payload) {
|
|
|
21
21
|
if (!context.config) {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
if (!payload.functions) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const { wantBackend, haveBackend } = payload.functions;
|
|
28
|
+
const plan = planner.createDeploymentPlan(wantBackend, haveBackend, context.filters);
|
|
25
29
|
const fnsToDelete = Object.values(plan)
|
|
26
30
|
.map((regionalChanges) => regionalChanges.endpointsToDelete)
|
|
27
31
|
.reduce(functional_1.reduceFlat, []);
|
|
@@ -40,15 +44,15 @@ async function release(context, options, payload) {
|
|
|
40
44
|
const fab = new fabricator.Fabricator({
|
|
41
45
|
functionExecutor,
|
|
42
46
|
executor: new executor.QueueExecutor({}),
|
|
43
|
-
sourceUrl: context.sourceUrl,
|
|
44
|
-
storage: context.storage,
|
|
47
|
+
sourceUrl: context.source.sourceUrl,
|
|
48
|
+
storage: context.source.storage,
|
|
45
49
|
appEngineLocation: (0, functionsConfig_1.getAppEngineLocation)(context.firebaseConfig),
|
|
46
50
|
});
|
|
47
51
|
const summary = await fab.applyPlan(plan);
|
|
48
52
|
await reporter.logAndTrackDeployStats(summary);
|
|
49
53
|
reporter.printErrors(summary);
|
|
50
|
-
printTriggerUrls(payload.functions.
|
|
51
|
-
const haveEndpoints = backend.allEndpoints(payload.functions.
|
|
54
|
+
printTriggerUrls(payload.functions.wantBackend);
|
|
55
|
+
const haveEndpoints = backend.allEndpoints(payload.functions.wantBackend);
|
|
52
56
|
const deletedEndpoints = Object.values(plan)
|
|
53
57
|
.map((r) => r.endpointsToDelete)
|
|
54
58
|
.reduce(functional_1.reduceFlat, []);
|
|
@@ -89,7 +93,7 @@ function printTriggerUrls(results) {
|
|
|
89
93
|
}
|
|
90
94
|
for (const httpsFunc of httpsFunctions) {
|
|
91
95
|
if (!httpsFunc.uri) {
|
|
92
|
-
logger_1.logger.debug("
|
|
96
|
+
logger_1.logger.debug("Not printing URL for HTTPS function. Typically this means it didn't match a filter or we failed deployment");
|
|
93
97
|
continue;
|
|
94
98
|
}
|
|
95
99
|
logger_1.logger.info(clc.bold("Function URL"), `(${(0, functionsDeployHelper_1.getFunctionLabel)(httpsFunc)}):`, httpsFunc.uri);
|
|
@@ -7,13 +7,13 @@ const error_1 = require("../../../error");
|
|
|
7
7
|
const utils = require("../../../utils");
|
|
8
8
|
const backend = require("../backend");
|
|
9
9
|
const v2events = require("../../../functions/events/v2");
|
|
10
|
-
function calculateChangesets(want, have, keyFn,
|
|
10
|
+
function calculateChangesets(want, have, keyFn, deleteAll) {
|
|
11
11
|
const toCreate = utils.groupBy(Object.keys(want)
|
|
12
12
|
.filter((id) => !have[id])
|
|
13
13
|
.map((id) => want[id]), keyFn);
|
|
14
14
|
const toDelete = utils.groupBy(Object.keys(have)
|
|
15
15
|
.filter((id) => !want[id])
|
|
16
|
-
.filter((id) =>
|
|
16
|
+
.filter((id) => deleteAll || (0, deploymentTool_1.isFirebaseManaged)(have[id].labels || {}))
|
|
17
17
|
.map((id) => have[id]), keyFn);
|
|
18
18
|
const toUpdate = utils.groupBy(Object.keys(want)
|
|
19
19
|
.filter((id) => have[id])
|
|
@@ -48,17 +48,17 @@ function calculateUpdate(want, have) {
|
|
|
48
48
|
return update;
|
|
49
49
|
}
|
|
50
50
|
exports.calculateUpdate = calculateUpdate;
|
|
51
|
-
function createDeploymentPlan(want, have,
|
|
51
|
+
function createDeploymentPlan(want, have, filters, deleteAll) {
|
|
52
52
|
let deployment = {};
|
|
53
53
|
want = backend.matchingBackend(want, (endpoint) => {
|
|
54
|
-
return (0, functionsDeployHelper_1.
|
|
54
|
+
return (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
|
|
55
55
|
});
|
|
56
56
|
have = backend.matchingBackend(have, (endpoint) => {
|
|
57
|
-
return (0, functionsDeployHelper_1.
|
|
57
|
+
return (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
|
|
58
58
|
});
|
|
59
59
|
const regions = new Set([...Object.keys(want.endpoints), ...Object.keys(have.endpoints)]);
|
|
60
60
|
for (const region of regions) {
|
|
61
|
-
const changesets = calculateChangesets(want.endpoints[region] || {}, have.endpoints[region] || {}, (e) => `${e.region}-${e.availableMemoryMb || "default"}`,
|
|
61
|
+
const changesets = calculateChangesets(want.endpoints[region] || {}, have.endpoints[region] || {}, (e) => `${e.region}-${e.availableMemoryMb || "default"}`, deleteAll);
|
|
62
62
|
deployment = Object.assign(Object.assign({}, deployment), changesets);
|
|
63
63
|
}
|
|
64
64
|
if (upgradedToGCFv2WithoutSettingConcurrency(want, have)) {
|
|
@@ -157,6 +157,9 @@ function checkForIllegalUpdate(want, have) {
|
|
|
157
157
|
else if (backend.isTaskQueueTriggered(e)) {
|
|
158
158
|
return "a task queue";
|
|
159
159
|
}
|
|
160
|
+
else if (backend.isBlockingTriggered(e)) {
|
|
161
|
+
return e.blockingTrigger.eventType;
|
|
162
|
+
}
|
|
160
163
|
throw Error("Functions release planner is not able to handle an unknown trigger type");
|
|
161
164
|
};
|
|
162
165
|
const wantType = triggerType(want);
|
|
@@ -4,7 +4,7 @@ exports.triggerTag = exports.printAbortedErrors = exports.printErrors = exports.
|
|
|
4
4
|
const backend = require("../backend");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const logger_1 = require("../../../logger");
|
|
7
|
-
const
|
|
7
|
+
const track_1 = require("../../../track");
|
|
8
8
|
const utils = require("../../../utils");
|
|
9
9
|
const functionsDeployHelper_1 = require("../functionsDeployHelper");
|
|
10
10
|
class DeploymentError extends Error {
|
|
@@ -36,23 +36,23 @@ async function logAndTrackDeployStats(summary) {
|
|
|
36
36
|
totalTime += result.durationMs;
|
|
37
37
|
if (!result.error) {
|
|
38
38
|
totalSuccesses++;
|
|
39
|
-
reports.push(
|
|
39
|
+
reports.push((0, track_1.track)("function_deploy_success", tag, result.durationMs));
|
|
40
40
|
}
|
|
41
41
|
else if (result.error instanceof AbortedDeploymentError) {
|
|
42
42
|
totalAborts++;
|
|
43
|
-
reports.push(
|
|
43
|
+
reports.push((0, track_1.track)("function_deploy_abort", tag, result.durationMs));
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
46
|
totalErrors++;
|
|
47
|
-
reports.push(
|
|
47
|
+
reports.push((0, track_1.track)("function_deploy_failure", tag, result.durationMs));
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
const regionCountTag = regions.size < 5 ? regions.size.toString() : ">=5";
|
|
51
|
-
reports.push(
|
|
51
|
+
reports.push((0, track_1.track)("functions_region_count", regionCountTag, 1));
|
|
52
52
|
const gcfv1 = summary.results.find((r) => r.endpoint.platform === "gcfv1");
|
|
53
53
|
const gcfv2 = summary.results.find((r) => r.endpoint.platform === "gcfv2");
|
|
54
54
|
const tag = gcfv1 && gcfv2 ? "v1+v2" : gcfv1 ? "v1" : "v2";
|
|
55
|
-
reports.push(
|
|
55
|
+
reports.push((0, track_1.track)("functions_codebase_deploy", tag, summary.results.length));
|
|
56
56
|
const avgTime = totalTime / (totalSuccesses + totalErrors);
|
|
57
57
|
logger_1.logger.debug(`Total Function Deployment time: ${summary.totalTime}`);
|
|
58
58
|
logger_1.logger.debug(`${totalErrors + totalSuccesses + totalAborts} Functions Deployed`);
|
|
@@ -61,15 +61,15 @@ async function logAndTrackDeployStats(summary) {
|
|
|
61
61
|
logger_1.logger.debug(`Average Function Deployment time: ${avgTime}`);
|
|
62
62
|
if (totalErrors + totalSuccesses > 0) {
|
|
63
63
|
if (totalErrors === 0) {
|
|
64
|
-
reports.push(
|
|
64
|
+
reports.push((0, track_1.track)("functions_deploy_result", "success", totalSuccesses));
|
|
65
65
|
}
|
|
66
66
|
else if (totalSuccesses > 0) {
|
|
67
|
-
reports.push(
|
|
68
|
-
reports.push(
|
|
69
|
-
reports.push(
|
|
67
|
+
reports.push((0, track_1.track)("functions_deploy_result", "partial_success", totalSuccesses));
|
|
68
|
+
reports.push((0, track_1.track)("functions_deploy_result", "partial_failure", totalErrors));
|
|
69
|
+
reports.push((0, track_1.track)("functions_deploy_result", "partial_error_ratio", totalErrors / (totalSuccesses + totalErrors)));
|
|
70
70
|
}
|
|
71
71
|
else {
|
|
72
|
-
reports.push(
|
|
72
|
+
reports.push((0, track_1.track)("functions_deploy_result", "failure", totalErrors));
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
await utils.allSettled(reports);
|
|
@@ -171,6 +171,9 @@ function triggerTag(endpoint) {
|
|
|
171
171
|
}
|
|
172
172
|
return `${prefix}.https`;
|
|
173
173
|
}
|
|
174
|
+
if (backend.isBlockingTriggered(endpoint)) {
|
|
175
|
+
return `${prefix}.blocking`;
|
|
176
|
+
}
|
|
174
177
|
return endpoint.eventTrigger.eventType;
|
|
175
178
|
}
|
|
176
179
|
exports.triggerTag = triggerTag;
|
|
@@ -19,31 +19,37 @@ function assertKeyTypes(prefix, yaml, schema) {
|
|
|
19
19
|
}
|
|
20
20
|
for (const [keyAsString, value] of Object.entries(yaml)) {
|
|
21
21
|
const key = keyAsString;
|
|
22
|
-
const fullKey = prefix ? prefix
|
|
22
|
+
const fullKey = prefix ? `${prefix}.${keyAsString}` : keyAsString;
|
|
23
23
|
if (!schema[key] || schema[key] === "omit") {
|
|
24
24
|
throw new error_1.FirebaseError(`Unexpected key ${fullKey}. You may need to install a newer version of the Firebase CLI.`);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
const schemaType = schema[key];
|
|
27
|
+
if (typeof schemaType === "function") {
|
|
28
|
+
if (!schemaType(value)) {
|
|
29
|
+
throw new error_1.FirebaseError(`${Array.isArray(value) ? "array" : typeof value} ${fullKey} failed validation`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (schemaType === "string") {
|
|
27
33
|
if (typeof value !== "string") {
|
|
28
34
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be string; was ${typeof value}`);
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
|
-
else if (
|
|
37
|
+
else if (schemaType === "number") {
|
|
32
38
|
if (typeof value !== "number") {
|
|
33
39
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be a number; was ${typeof value}`);
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
|
-
else if (
|
|
42
|
+
else if (schemaType === "boolean") {
|
|
37
43
|
if (typeof value !== "boolean") {
|
|
38
44
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be a boolean; was ${typeof value}`);
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
|
-
else if (
|
|
47
|
+
else if (schemaType === "array") {
|
|
42
48
|
if (!Array.isArray(value)) {
|
|
43
49
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be an array; was ${typeof value}`);
|
|
44
50
|
}
|
|
45
51
|
}
|
|
46
|
-
else if (
|
|
52
|
+
else if (schemaType === "object") {
|
|
47
53
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
48
54
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be an object; was ${typeof value}`);
|
|
49
55
|
}
|
|
@@ -5,6 +5,12 @@ const backend = require("../../backend");
|
|
|
5
5
|
const proto_1 = require("../../../../gcp/proto");
|
|
6
6
|
const parsing_1 = require("./parsing");
|
|
7
7
|
const error_1 = require("../../../../error");
|
|
8
|
+
const CHANNEL_NAME_REGEX = new RegExp("(projects\\/" +
|
|
9
|
+
"(?<project>(?:\\d+)|(?:[A-Za-z]+[A-Za-z\\d-]*[A-Za-z\\d]?))\\/)?" +
|
|
10
|
+
"locations\\/" +
|
|
11
|
+
"(?<location>[A-Za-z\\d\\-_]+)\\/" +
|
|
12
|
+
"channels\\/" +
|
|
13
|
+
"(?<channel>[A-Za-z\\d\\-_]+)");
|
|
8
14
|
function backendFromV1Alpha1(yaml, project, region, runtime) {
|
|
9
15
|
const manifest = JSON.parse(JSON.stringify(yaml));
|
|
10
16
|
const bkend = backend.empty();
|
|
@@ -42,9 +48,9 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
|
|
|
42
48
|
const ep = manifest.endpoints[id];
|
|
43
49
|
(0, parsing_1.assertKeyTypes)(prefix, ep, {
|
|
44
50
|
region: "array",
|
|
45
|
-
platform:
|
|
51
|
+
platform: (platform) => backend.AllFunctionsPlatforms.includes(platform),
|
|
46
52
|
entryPoint: "string",
|
|
47
|
-
availableMemoryMb:
|
|
53
|
+
availableMemoryMb: (mem) => backend.AllMemoryOptions.includes(mem),
|
|
48
54
|
maxInstances: "number",
|
|
49
55
|
minInstances: "number",
|
|
50
56
|
concurrency: "number",
|
|
@@ -52,7 +58,7 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
|
|
|
52
58
|
timeoutSeconds: "number",
|
|
53
59
|
vpc: "object",
|
|
54
60
|
labels: "object",
|
|
55
|
-
ingressSettings:
|
|
61
|
+
ingressSettings: (setting) => backend.AllIngressSettings.includes(setting),
|
|
56
62
|
environmentVariables: "object",
|
|
57
63
|
secretEnvironmentVariables: "array",
|
|
58
64
|
httpsTrigger: "object",
|
|
@@ -60,7 +66,16 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
|
|
|
60
66
|
eventTrigger: "object",
|
|
61
67
|
scheduleTrigger: "object",
|
|
62
68
|
taskQueueTrigger: "object",
|
|
69
|
+
blockingTrigger: "object",
|
|
70
|
+
cpu: (cpu) => typeof cpu === "number" || cpu === "gcf_gen1",
|
|
63
71
|
});
|
|
72
|
+
if (ep.vpc) {
|
|
73
|
+
(0, parsing_1.assertKeyTypes)(prefix + ".vpc", ep.vpc, {
|
|
74
|
+
connector: "string",
|
|
75
|
+
egressSettings: (setting) => backend.AllVpcEgressSettings.includes(setting),
|
|
76
|
+
});
|
|
77
|
+
(0, parsing_1.requireKeys)(prefix + ".vpc", ep.vpc, "connector");
|
|
78
|
+
}
|
|
64
79
|
let triggerCount = 0;
|
|
65
80
|
if (ep.httpsTrigger) {
|
|
66
81
|
triggerCount++;
|
|
@@ -77,6 +92,9 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
|
|
|
77
92
|
if (ep.taskQueueTrigger) {
|
|
78
93
|
triggerCount++;
|
|
79
94
|
}
|
|
95
|
+
if (ep.blockingTrigger) {
|
|
96
|
+
triggerCount++;
|
|
97
|
+
}
|
|
80
98
|
if (!triggerCount) {
|
|
81
99
|
throw new error_1.FirebaseError("Expected trigger in endpoint " + id);
|
|
82
100
|
}
|
|
@@ -97,6 +115,7 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
|
|
|
97
115
|
channel: "string",
|
|
98
116
|
});
|
|
99
117
|
triggered = { eventTrigger: ep.eventTrigger };
|
|
118
|
+
(0, proto_1.renameIfPresent)(triggered.eventTrigger, ep.eventTrigger, "channel", "channel", (c) => resolveChannelName(project, c, defaultRegion));
|
|
100
119
|
for (const [k, v] of Object.entries(triggered.eventTrigger.eventFilters)) {
|
|
101
120
|
if (k === "topic" && !v.startsWith("projects/")) {
|
|
102
121
|
triggered.eventTrigger.eventFilters[k] = `projects/${project}/topics/${v}`;
|
|
@@ -151,6 +170,14 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
|
|
|
151
170
|
}
|
|
152
171
|
triggered = { taskQueueTrigger: ep.taskQueueTrigger };
|
|
153
172
|
}
|
|
173
|
+
else if (backend.isBlockingTriggered(ep)) {
|
|
174
|
+
(0, parsing_1.requireKeys)(prefix + ".blockingTrigger", ep.blockingTrigger, "eventType");
|
|
175
|
+
(0, parsing_1.assertKeyTypes)(prefix + ".blockingTrigger", ep.blockingTrigger, {
|
|
176
|
+
eventType: "string",
|
|
177
|
+
options: "object",
|
|
178
|
+
});
|
|
179
|
+
triggered = { blockingTrigger: ep.blockingTrigger };
|
|
180
|
+
}
|
|
154
181
|
else {
|
|
155
182
|
throw new error_1.FirebaseError(`Do not recognize trigger type for endpoint ${id}. Try upgrading ` +
|
|
156
183
|
"firebase-tools with npm install -g firebase-tools@latest");
|
|
@@ -165,3 +192,23 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
|
|
|
165
192
|
}
|
|
166
193
|
return allParsed;
|
|
167
194
|
}
|
|
195
|
+
function resolveChannelName(projectId, channel, defaultRegion) {
|
|
196
|
+
if (!channel.includes("/")) {
|
|
197
|
+
const location = defaultRegion;
|
|
198
|
+
const channelId = channel;
|
|
199
|
+
return "projects/" + projectId + "/locations/" + location + "/channels/" + channelId;
|
|
200
|
+
}
|
|
201
|
+
const match = CHANNEL_NAME_REGEX.exec(channel);
|
|
202
|
+
if (!(match === null || match === void 0 ? void 0 : match.groups)) {
|
|
203
|
+
throw new error_1.FirebaseError("Invalid channel name format.");
|
|
204
|
+
}
|
|
205
|
+
const matchedProjectId = match.groups.project;
|
|
206
|
+
const location = match.groups.location;
|
|
207
|
+
const channelId = match.groups.channel;
|
|
208
|
+
if (matchedProjectId) {
|
|
209
|
+
return "projects/" + matchedProjectId + "/locations/" + location + "/channels/" + channelId;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
return "projects/" + projectId + "/locations/" + location + "/channels/" + channelId;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -4,7 +4,7 @@ exports.getRuntimeChoice = exports.DEPRECATED_NODE_VERSION_INFO = exports.UNSUPP
|
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const error_1 = require("../../../../error");
|
|
7
|
-
const
|
|
7
|
+
const track_1 = require("../../../../track");
|
|
8
8
|
const runtimes = require("../../runtimes");
|
|
9
9
|
const cjson = require("cjson");
|
|
10
10
|
const ENGINE_RUNTIMES = {
|
|
@@ -46,11 +46,11 @@ function getRuntimeChoice(sourceDir, runtimeFromConfig) {
|
|
|
46
46
|
? exports.UNSUPPORTED_NODE_VERSION_FIREBASE_JSON_MSG
|
|
47
47
|
: exports.UNSUPPORTED_NODE_VERSION_PACKAGE_JSON_MSG) + exports.DEPRECATED_NODE_VERSION_INFO;
|
|
48
48
|
if (!runtime || !ENGINE_RUNTIMES_NAMES.includes(runtime)) {
|
|
49
|
-
void track("functions_runtime_notices", "package_missing_runtime");
|
|
49
|
+
void (0, track_1.track)("functions_runtime_notices", "package_missing_runtime");
|
|
50
50
|
throw new error_1.FirebaseError(errorMessage, { exit: 1 });
|
|
51
51
|
}
|
|
52
52
|
if (runtimes.isDeprecatedRuntime(runtime) || !runtimes.isValidRuntime(runtime)) {
|
|
53
|
-
void track("functions_runtime_notices", `${runtime}_deploy_prohibited`);
|
|
53
|
+
void (0, track_1.track)("functions_runtime_notices", `${runtime}_deploy_prohibited`);
|
|
54
54
|
throw new error_1.FirebaseError(errorMessage, { exit: 1 });
|
|
55
55
|
}
|
|
56
56
|
return runtime;
|
|
@@ -9,7 +9,7 @@ const logger_1 = require("../../../../logger");
|
|
|
9
9
|
const backend = require("../../backend");
|
|
10
10
|
const api = require("../../../../api");
|
|
11
11
|
const proto = require("../../../../gcp/proto");
|
|
12
|
-
const
|
|
12
|
+
const events = require("../../../../functions/events");
|
|
13
13
|
const TRIGGER_PARSER = path.resolve(__dirname, "./triggerParser.js");
|
|
14
14
|
function removeInspectOptions(options) {
|
|
15
15
|
return options.filter((opt) => !opt.startsWith("--inspect"));
|
|
@@ -62,7 +62,9 @@ function mergeRequiredAPIs(backend) {
|
|
|
62
62
|
const apiToReasons = {};
|
|
63
63
|
for (const { api, reason } of backend.requiredAPIs) {
|
|
64
64
|
const reasons = apiToReasons[api] || new Set();
|
|
65
|
-
|
|
65
|
+
if (reason) {
|
|
66
|
+
reasons.add(reason);
|
|
67
|
+
}
|
|
66
68
|
apiToReasons[api] = reasons;
|
|
67
69
|
}
|
|
68
70
|
const merged = [];
|
|
@@ -77,7 +79,10 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
|
|
|
77
79
|
Object.freeze(annotation);
|
|
78
80
|
for (const region of annotation.regions || [api.functionsDefaultRegion]) {
|
|
79
81
|
let triggered;
|
|
80
|
-
const triggerCount = +!!annotation.httpsTrigger +
|
|
82
|
+
const triggerCount = +!!annotation.httpsTrigger +
|
|
83
|
+
+!!annotation.eventTrigger +
|
|
84
|
+
+!!annotation.taskQueueTrigger +
|
|
85
|
+
+!!annotation.blockingTrigger;
|
|
81
86
|
if (triggerCount !== 1) {
|
|
82
87
|
throw new error_1.FirebaseError("Unexpected annotation generated by the Firebase Functions SDK. This should never happen.");
|
|
83
88
|
}
|
|
@@ -109,6 +114,20 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
|
|
|
109
114
|
});
|
|
110
115
|
triggered = { scheduleTrigger: annotation.schedule };
|
|
111
116
|
}
|
|
117
|
+
else if (annotation.blockingTrigger) {
|
|
118
|
+
if (events.v1.AUTH_BLOCKING_EVENTS.includes(annotation.blockingTrigger.eventType)) {
|
|
119
|
+
want.requiredAPIs.push({
|
|
120
|
+
api: "identitytoolkit.googleapis.com",
|
|
121
|
+
reason: "Needed for auth blocking functions.",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
triggered = {
|
|
125
|
+
blockingTrigger: {
|
|
126
|
+
eventType: annotation.blockingTrigger.eventType,
|
|
127
|
+
options: annotation.blockingTrigger.options,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
112
131
|
else {
|
|
113
132
|
triggered = {
|
|
114
133
|
eventTrigger: {
|
|
@@ -118,10 +137,10 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
|
|
|
118
137
|
},
|
|
119
138
|
};
|
|
120
139
|
if (annotation.platform === "gcfv2") {
|
|
121
|
-
if (annotation.eventTrigger.eventType ===
|
|
140
|
+
if (annotation.eventTrigger.eventType === events.v2.PUBSUB_PUBLISH_EVENT) {
|
|
122
141
|
triggered.eventTrigger.eventFilters = { topic: annotation.eventTrigger.resource };
|
|
123
142
|
}
|
|
124
|
-
if (
|
|
143
|
+
if (events.v2.STORAGE_EVENTS.find((event) => { var _a; return event === (((_a = annotation.eventTrigger) === null || _a === void 0 ? void 0 : _a.eventType) || ""); })) {
|
|
125
144
|
triggered.eventTrigger.eventFilters = { bucket: annotation.eventTrigger.resource };
|
|
126
145
|
}
|
|
127
146
|
}
|
|
@@ -7,7 +7,7 @@ const semver = require("semver");
|
|
|
7
7
|
const spawn = require("cross-spawn");
|
|
8
8
|
const utils = require("../../../../utils");
|
|
9
9
|
const logger_1 = require("../../../../logger");
|
|
10
|
-
const
|
|
10
|
+
const track_1 = require("../../../../track");
|
|
11
11
|
const MIN_SDK_VERSION = "2.0.0";
|
|
12
12
|
exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = clc.bold.yellow("functions: ") +
|
|
13
13
|
"You must have a " +
|
|
@@ -52,7 +52,7 @@ exports.getLatestSDKVersion = getLatestSDKVersion;
|
|
|
52
52
|
function checkFunctionsSDKVersion(currentVersion) {
|
|
53
53
|
try {
|
|
54
54
|
if (semver.lt(currentVersion, MIN_SDK_VERSION)) {
|
|
55
|
-
void track("functions_runtime_notices", "functions_sdk_too_old");
|
|
55
|
+
void (0, track_1.track)("functions_runtime_notices", "functions_sdk_too_old");
|
|
56
56
|
utils.logWarning(exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING);
|
|
57
57
|
}
|
|
58
58
|
const latest = exports.getLatestSDKVersion();
|