firebase-tools 10.5.0 → 10.7.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.
Files changed (105) hide show
  1. package/lib/command.js +4 -4
  2. package/lib/commands/deploy.js +1 -1
  3. package/lib/commands/emulators-start.js +7 -2
  4. package/lib/commands/ext-configure.js +15 -5
  5. package/lib/commands/ext-export.js +6 -5
  6. package/lib/commands/ext-install.js +28 -44
  7. package/lib/commands/ext-update.js +9 -1
  8. package/lib/commands/functions-delete.js +7 -3
  9. package/lib/commands/functions-secrets-destroy.js +23 -3
  10. package/lib/commands/functions-secrets-prune.js +15 -12
  11. package/lib/commands/functions-secrets-set.js +51 -4
  12. package/lib/commands/hosting-channel-deploy.js +2 -2
  13. package/lib/deploy/database/deploy.js +4 -0
  14. package/lib/deploy/database/index.js +1 -0
  15. package/lib/deploy/extensions/deploy.js +4 -4
  16. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  17. package/lib/deploy/extensions/planner.js +36 -9
  18. package/lib/deploy/extensions/prepare.js +1 -1
  19. package/lib/deploy/extensions/secrets.js +2 -2
  20. package/lib/deploy/extensions/tasks.js +60 -21
  21. package/lib/deploy/functions/backend.js +37 -6
  22. package/lib/deploy/functions/build.js +162 -0
  23. package/lib/deploy/functions/checkIam.js +10 -6
  24. package/lib/deploy/functions/deploy.js +49 -28
  25. package/lib/deploy/functions/ensure.js +4 -4
  26. package/lib/deploy/functions/functionsDeployHelper.js +99 -24
  27. package/lib/deploy/functions/prepare.js +130 -62
  28. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  29. package/lib/deploy/functions/pricing.js +6 -3
  30. package/lib/deploy/functions/prompts.js +1 -7
  31. package/lib/deploy/functions/release/fabricator.js +70 -28
  32. package/lib/deploy/functions/release/index.js +41 -6
  33. package/lib/deploy/functions/release/planner.js +19 -12
  34. package/lib/deploy/functions/release/reporter.js +14 -11
  35. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  36. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
  37. package/lib/deploy/functions/runtimes/node/index.js +1 -1
  38. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
  40. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  41. package/lib/deploy/functions/services/auth.js +95 -0
  42. package/lib/deploy/functions/services/index.js +41 -21
  43. package/lib/deploy/functions/services/storage.js +1 -6
  44. package/lib/deploy/functions/validate.js +32 -6
  45. package/lib/deploy/hosting/args.js +2 -0
  46. package/lib/deploy/hosting/convertConfig.js +39 -8
  47. package/lib/deploy/hosting/deploy.js +3 -3
  48. package/lib/deploy/hosting/prepare.js +2 -2
  49. package/lib/deploy/hosting/release.js +6 -2
  50. package/lib/deploy/index.js +82 -93
  51. package/lib/deploy/remoteconfig/deploy.js +4 -0
  52. package/lib/deploy/remoteconfig/index.js +3 -1
  53. package/lib/emulator/auth/operations.js +5 -0
  54. package/lib/emulator/auth/utils.js +3 -25
  55. package/lib/emulator/controller.js +17 -14
  56. package/lib/emulator/downloadableEmulators.js +39 -23
  57. package/lib/emulator/extensions/postinstall.js +41 -0
  58. package/lib/emulator/extensions/validation.js +2 -2
  59. package/lib/emulator/extensionsEmulator.js +85 -21
  60. package/lib/emulator/functionsEmulator.js +88 -10
  61. package/lib/emulator/functionsEmulatorShared.js +37 -21
  62. package/lib/emulator/functionsEmulatorShell.js +2 -3
  63. package/lib/emulator/pubsubEmulator.js +13 -9
  64. package/lib/emulator/registry.js +34 -12
  65. package/lib/emulator/storage/apis/firebase.js +13 -8
  66. package/lib/emulator/storage/apis/gcloud.js +15 -9
  67. package/lib/emulator/storage/files.js +14 -3
  68. package/lib/emulator/storage/index.js +9 -1
  69. package/lib/emulator/storage/metadata.js +18 -8
  70. package/lib/emulator/storage/rules/manager.js +7 -17
  71. package/lib/emulator/storage/server.js +38 -12
  72. package/lib/ensureApiEnabled.js +8 -4
  73. package/lib/extensions/askUserForParam.js +14 -11
  74. package/lib/extensions/changelog.js +1 -1
  75. package/lib/extensions/emulator/optionsHelper.js +9 -10
  76. package/lib/extensions/emulator/specHelper.js +7 -1
  77. package/lib/extensions/emulator/triggerHelper.js +11 -14
  78. package/lib/extensions/extensionsApi.js +2 -1
  79. package/lib/extensions/extensionsHelper.js +30 -24
  80. package/lib/extensions/manifest.js +28 -8
  81. package/lib/extensions/paramHelper.js +19 -13
  82. package/lib/extensions/provisioningHelper.js +2 -2
  83. package/lib/extensions/warnings.js +3 -3
  84. package/lib/functions/env.js +10 -2
  85. package/lib/functions/events/index.js +7 -0
  86. package/lib/functions/events/v1.js +6 -0
  87. package/lib/functions/projectConfig.js +32 -6
  88. package/lib/functions/runtimeConfigExport.js +10 -6
  89. package/lib/functions/secrets.js +99 -6
  90. package/lib/functionsShellCommandAction.js +1 -1
  91. package/lib/gcp/cloudfunctions.js +44 -18
  92. package/lib/gcp/cloudfunctionsv2.js +48 -25
  93. package/lib/gcp/cloudtasks.js +5 -3
  94. package/lib/gcp/identityPlatform.js +44 -0
  95. package/lib/gcp/secretManager.js +2 -2
  96. package/lib/metaprogramming.js +2 -0
  97. package/lib/previews.js +1 -1
  98. package/lib/serve/functions.js +16 -19
  99. package/lib/serve/hosting.js +25 -12
  100. package/lib/serve/index.js +6 -0
  101. package/lib/track.js +15 -21
  102. package/lib/utils.js +30 -1
  103. package/npm-shrinkwrap.json +44 -2
  104. package/package.json +4 -1
  105. package/schema/firebase-config.json +6 -0
@@ -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,14 +37,14 @@ 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 {
41
44
  constructor(args) {
42
45
  this.executor = args.executor;
43
46
  this.functionExecutor = args.functionExecutor;
44
- this.sourceUrl = args.sourceUrl;
45
- this.storage = args.storage;
47
+ this.sources = args.sources;
46
48
  this.appEngineLocation = args.appEngineLocation;
47
49
  }
48
50
  async applyPlan(plan) {
@@ -124,9 +126,7 @@ class Fabricator {
124
126
  await this.setTrigger(endpoint);
125
127
  }
126
128
  async updateEndpoint(update, scraper) {
127
- if (update.deleteAndRecreate || update.endpoint.platform !== "gcfv2") {
128
- update.endpoint.labels = Object.assign(Object.assign({}, update.endpoint.labels), deploymentTool.labels());
129
- }
129
+ update.endpoint.labels = Object.assign(Object.assign({}, update.endpoint.labels), deploymentTool.labels());
130
130
  if (update.deleteAndRecreate) {
131
131
  await this.deleteEndpoint(update.deleteAndRecreate);
132
132
  await this.createEndpoint(update.endpoint, scraper);
@@ -153,12 +153,13 @@ class Fabricator {
153
153
  }
154
154
  }
155
155
  async createV1Function(endpoint, scraper) {
156
- var _a;
157
- if (!this.sourceUrl) {
156
+ var _a, _b;
157
+ const sourceUrl = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.sourceUrl;
158
+ if (!sourceUrl) {
158
159
  logger_1.logger.debug("Precondition failed. Cannot create a GCF function without sourceUrl");
159
160
  throw new Error("Precondition failed");
160
161
  }
161
- const apiFunction = gcf.functionFromEndpoint(endpoint, this.sourceUrl);
162
+ const apiFunction = gcf.functionFromEndpoint(endpoint, sourceUrl);
162
163
  if (apiFunction.httpsTrigger) {
163
164
  apiFunction.httpsTrigger.securityLevel = "SECURE_ALWAYS";
164
165
  }
@@ -166,10 +167,10 @@ class Fabricator {
166
167
  const resultFunction = await this.functionExecutor
167
168
  .run(async () => {
168
169
  const op = await gcf.createFunction(apiFunction);
169
- return poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `create-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
170
+ return poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `create-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
170
171
  })
171
172
  .catch(rethrowAs(endpoint, "create"));
172
- endpoint.uri = (_a = resultFunction === null || resultFunction === void 0 ? void 0 : resultFunction.httpsTrigger) === null || _a === void 0 ? void 0 : _a.url;
173
+ endpoint.uri = (_b = resultFunction === null || resultFunction === void 0 ? void 0 : resultFunction.httpsTrigger) === null || _b === void 0 ? void 0 : _b.url;
173
174
  if (backend.isHttpsTriggered(endpoint)) {
174
175
  const invoker = endpoint.httpsTrigger.invoker || ["public"];
175
176
  if (!invoker.includes("private")) {
@@ -197,15 +198,24 @@ class Fabricator {
197
198
  .catch(rethrowAs(endpoint, "set invoker"));
198
199
  }
199
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
+ }
200
209
  }
201
210
  async createV2Function(endpoint) {
202
- var _a;
203
- if (!this.storage) {
211
+ var _a, _b;
212
+ const storage = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
213
+ if (!storage) {
204
214
  logger_1.logger.debug("Precondition failed. Cannot create a GCFv2 function without storage");
205
215
  throw new Error("Precondition failed");
206
216
  }
207
- const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage[endpoint.region]);
208
- const topic = (_a = apiFunction.eventTrigger) === null || _a === void 0 ? void 0 : _a.pubsubTopic;
217
+ const apiFunction = gcfV2.functionFromEndpoint(endpoint, storage);
218
+ const topic = (_b = apiFunction.eventTrigger) === null || _b === void 0 ? void 0 : _b.pubsubTopic;
209
219
  if (topic) {
210
220
  await this.executor
211
221
  .run(async () => {
@@ -226,7 +236,7 @@ class Fabricator {
226
236
  const resultFunction = await this.functionExecutor
227
237
  .run(async () => {
228
238
  const op = await gcfV2.createFunction(apiFunction);
229
- return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `create-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
239
+ return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `create-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
230
240
  })
231
241
  .catch(rethrowAs(endpoint, "create"));
232
242
  endpoint.uri = resultFunction.serviceConfig.uri;
@@ -254,26 +264,33 @@ class Fabricator {
254
264
  .catch(rethrowAs(endpoint, "set invoker"));
255
265
  }
256
266
  }
267
+ else if (backend.isBlockingTriggered(endpoint) &&
268
+ v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
269
+ await this.executor
270
+ .run(() => run.setInvokerCreate(endpoint.project, serviceName, ["public"]))
271
+ .catch(rethrowAs(endpoint, "set invoker"));
272
+ }
257
273
  const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
258
274
  if (mem >= backend.MIN_MEMORY_FOR_CONCURRENCY && endpoint.concurrency !== 1) {
259
275
  await this.setConcurrency(endpoint, serviceName, endpoint.concurrency || DEFAULT_GCFV2_CONCURRENCY);
260
276
  }
261
277
  }
262
278
  async updateV1Function(endpoint, scraper) {
263
- var _a;
264
- if (!this.sourceUrl) {
279
+ var _a, _b;
280
+ const sourceUrl = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.sourceUrl;
281
+ if (!sourceUrl) {
265
282
  logger_1.logger.debug("Precondition failed. Cannot update a GCF function without sourceUrl");
266
283
  throw new Error("Precondition failed");
267
284
  }
268
- const apiFunction = gcf.functionFromEndpoint(endpoint, this.sourceUrl);
285
+ const apiFunction = gcf.functionFromEndpoint(endpoint, sourceUrl);
269
286
  apiFunction.sourceToken = await scraper.tokenPromise();
270
287
  const resultFunction = await this.functionExecutor
271
288
  .run(async () => {
272
289
  const op = await gcf.updateFunction(apiFunction);
273
- return await poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `update-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
290
+ return await poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `update-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
274
291
  })
275
292
  .catch(rethrowAs(endpoint, "update"));
276
- endpoint.uri = (_a = resultFunction === null || resultFunction === void 0 ? void 0 : resultFunction.httpsTrigger) === null || _a === void 0 ? void 0 : _a.url;
293
+ endpoint.uri = (_b = resultFunction === null || resultFunction === void 0 ? void 0 : resultFunction.httpsTrigger) === null || _b === void 0 ? void 0 : _b.url;
277
294
  let invoker;
278
295
  if (backend.isHttpsTriggered(endpoint)) {
279
296
  invoker = endpoint.httpsTrigger.invoker;
@@ -281,6 +298,10 @@ class Fabricator {
281
298
  else if (backend.isTaskQueueTriggered(endpoint)) {
282
299
  invoker = endpoint.taskQueueTrigger.invoker;
283
300
  }
301
+ else if (backend.isBlockingTriggered(endpoint) &&
302
+ v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
303
+ invoker = ["public"];
304
+ }
284
305
  if (invoker) {
285
306
  await this.executor
286
307
  .run(() => gcf.setInvokerUpdate(endpoint.project, backend.functionName(endpoint), invoker))
@@ -288,19 +309,20 @@ class Fabricator {
288
309
  }
289
310
  }
290
311
  async updateV2Function(endpoint) {
291
- var _a;
292
- if (!this.storage) {
312
+ var _a, _b;
313
+ const storage = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
314
+ if (!storage) {
293
315
  logger_1.logger.debug("Precondition failed. Cannot update a GCFv2 function without storage");
294
316
  throw new Error("Precondition failed");
295
317
  }
296
- const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage[endpoint.region]);
297
- if ((_a = apiFunction.eventTrigger) === null || _a === void 0 ? void 0 : _a.pubsubTopic) {
318
+ const apiFunction = gcfV2.functionFromEndpoint(endpoint, storage);
319
+ if ((_b = apiFunction.eventTrigger) === null || _b === void 0 ? void 0 : _b.pubsubTopic) {
298
320
  delete apiFunction.eventTrigger.pubsubTopic;
299
321
  }
300
322
  const resultFunction = await this.functionExecutor
301
323
  .run(async () => {
302
324
  const op = await gcfV2.updateFunction(apiFunction);
303
- return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `update-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
325
+ return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `update-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
304
326
  })
305
327
  .catch(rethrowAs(endpoint, "update"));
306
328
  endpoint.uri = resultFunction.serviceConfig.uri;
@@ -312,6 +334,10 @@ class Fabricator {
312
334
  else if (backend.isTaskQueueTriggered(endpoint)) {
313
335
  invoker = endpoint.taskQueueTrigger.invoker;
314
336
  }
337
+ else if (backend.isBlockingTriggered(endpoint) &&
338
+ v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
339
+ invoker = ["public"];
340
+ }
315
341
  if (invoker) {
316
342
  await this.executor
317
343
  .run(() => run.setInvokerUpdate(endpoint.project, serviceName, invoker))
@@ -326,7 +352,7 @@ class Fabricator {
326
352
  await this.functionExecutor
327
353
  .run(async () => {
328
354
  const op = await gcf.deleteFunction(fnName);
329
- const pollerOptions = Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `delete-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name });
355
+ const pollerOptions = Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `delete-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name });
330
356
  await poller.pollOperation(pollerOptions);
331
357
  })
332
358
  .catch(rethrowAs(endpoint, "delete"));
@@ -336,7 +362,7 @@ class Fabricator {
336
362
  await this.functionExecutor
337
363
  .run(async () => {
338
364
  const op = await gcfV2.deleteFunction(fnName);
339
- const pollerOptions = Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `delete-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name });
365
+ const pollerOptions = Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `delete-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name });
340
366
  await poller.pollOperation(pollerOptions);
341
367
  })
342
368
  .catch(rethrowAs(endpoint, "delete"));
@@ -371,6 +397,9 @@ class Fabricator {
371
397
  else if (backend.isTaskQueueTriggered(endpoint)) {
372
398
  await this.upsertTaskQueue(endpoint);
373
399
  }
400
+ else if (backend.isBlockingTriggered(endpoint)) {
401
+ await this.registerBlockingTrigger(endpoint);
402
+ }
374
403
  }
375
404
  async deleteTrigger(endpoint) {
376
405
  if (backend.isScheduleTriggered(endpoint)) {
@@ -387,6 +416,9 @@ class Fabricator {
387
416
  else if (backend.isTaskQueueTriggered(endpoint)) {
388
417
  await this.disableTaskQueue(endpoint);
389
418
  }
419
+ else if (backend.isBlockingTriggered(endpoint)) {
420
+ await this.unregisterBlockingTrigger(endpoint);
421
+ }
390
422
  }
391
423
  async upsertScheduleV1(endpoint) {
392
424
  const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
@@ -408,6 +440,11 @@ class Fabricator {
408
440
  .catch(rethrowAs(endpoint, "set invoker"));
409
441
  }
410
442
  }
443
+ async registerBlockingTrigger(endpoint) {
444
+ await this.executor
445
+ .run(() => services.serviceForEndpoint(endpoint).registerTrigger(endpoint))
446
+ .catch(rethrowAs(endpoint, "register blocking trigger"));
447
+ }
411
448
  async deleteScheduleV1(endpoint) {
412
449
  const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
413
450
  await this.executor
@@ -429,10 +466,15 @@ class Fabricator {
429
466
  .run(() => cloudtasks.updateQueue(update))
430
467
  .catch(rethrowAs(endpoint, "disable task queue"));
431
468
  }
469
+ async unregisterBlockingTrigger(endpoint) {
470
+ await this.executor
471
+ .run(() => services.serviceForEndpoint(endpoint).unregisterTrigger(endpoint))
472
+ .catch(rethrowAs(endpoint, "unregister blocking trigger"));
473
+ }
432
474
  logOpStart(op, endpoint) {
433
475
  const runtime = (0, runtimes_1.getHumanFriendlyRuntimeName)(endpoint.runtime);
434
476
  const label = helper.getFunctionLabel(endpoint);
435
- utils.logBullet(`${clc.bold.cyan("functions:")} ${op} ${runtime} function ${clc.bold(label)}...`);
477
+ utils.logLabeledBullet("functions", `${op} ${runtime} function ${clc.bold(label)}...`);
436
478
  }
437
479
  logOpSuccess(op, endpoint) {
438
480
  const label = helper.getFunctionLabel(endpoint);
@@ -11,14 +11,31 @@ const fabricator = require("./fabricator");
11
11
  const reporter = require("./reporter");
12
12
  const executor = require("./executor");
13
13
  const prompts = require("../prompts");
14
+ const secrets = require("../../../functions/secrets");
14
15
  const functionsConfig_1 = require("../../../functionsConfig");
15
16
  const functionsDeployHelper_1 = require("../functionsDeployHelper");
16
17
  const error_1 = require("../../../error");
18
+ const projectUtils_1 = require("../../../projectUtils");
19
+ const utils_1 = require("../../../utils");
17
20
  async function release(context, options, payload) {
18
21
  if (!context.config) {
19
22
  return;
20
23
  }
21
- const plan = planner.createDeploymentPlan(payload.functions.backend, await backend.existingBackend(context), { filters: context.filters });
24
+ if (!payload.functions) {
25
+ return;
26
+ }
27
+ if (!context.sources) {
28
+ return;
29
+ }
30
+ let plan = {};
31
+ for (const [codebase, { wantBackend, haveBackend }] of Object.entries(payload.functions)) {
32
+ plan = Object.assign(Object.assign({}, plan), planner.createDeploymentPlan({
33
+ codebase,
34
+ wantBackend,
35
+ haveBackend,
36
+ filters: context.filters,
37
+ }));
38
+ }
22
39
  const fnsToDelete = Object.values(plan)
23
40
  .map((regionalChanges) => regionalChanges.endpointsToDelete)
24
41
  .reduce(functional_1.reduceFlat, []);
@@ -37,15 +54,15 @@ async function release(context, options, payload) {
37
54
  const fab = new fabricator.Fabricator({
38
55
  functionExecutor,
39
56
  executor: new executor.QueueExecutor({}),
40
- sourceUrl: context.sourceUrl,
41
- storage: context.storage,
57
+ sources: context.sources,
42
58
  appEngineLocation: (0, functionsConfig_1.getAppEngineLocation)(context.firebaseConfig),
43
59
  });
44
60
  const summary = await fab.applyPlan(plan);
45
61
  await reporter.logAndTrackDeployStats(summary);
46
62
  reporter.printErrors(summary);
47
- printTriggerUrls(payload.functions.backend);
48
- const haveEndpoints = backend.allEndpoints(payload.functions.backend);
63
+ const wantBackend = backend.merge(...Object.values(payload.functions).map((p) => p.wantBackend));
64
+ printTriggerUrls(wantBackend);
65
+ const haveEndpoints = backend.allEndpoints(wantBackend);
49
66
  const deletedEndpoints = Object.values(plan)
50
67
  .map((r) => r.endpointsToDelete)
51
68
  .reduce(functional_1.reduceFlat, []);
@@ -59,6 +76,24 @@ async function release(context, options, payload) {
59
76
  const opts = allErrors.length === 1 ? { original: allErrors[0] } : { children: allErrors };
60
77
  throw new error_1.FirebaseError("There was an error deploying functions", Object.assign(Object.assign({}, opts), { exit: 2 }));
61
78
  }
79
+ else {
80
+ if (secrets.of(haveEndpoints).length > 0) {
81
+ const projectId = (0, projectUtils_1.needProjectId)(options);
82
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
83
+ const reloadedBackend = await backend.existingBackend({ projectId });
84
+ const prunedResult = await secrets.pruneAndDestroySecrets({ projectId, projectNumber }, backend.allEndpoints(reloadedBackend));
85
+ if (prunedResult.destroyed.length > 0) {
86
+ (0, utils_1.logLabeledBullet)("functions", `Destroyed unused secret versions: ${prunedResult.destroyed
87
+ .map((s) => `${s.secret}@${s.version}`)
88
+ .join(", ")}`);
89
+ }
90
+ if (prunedResult.erred.length > 0) {
91
+ (0, utils_1.logLabeledWarning)("functions", `Failed to destroy unused secret versions:\n\t${prunedResult.erred
92
+ .map((err) => err.message)
93
+ .join("\n\t")}`);
94
+ }
95
+ }
96
+ }
62
97
  }
63
98
  exports.release = release;
64
99
  function printTriggerUrls(results) {
@@ -68,7 +103,7 @@ function printTriggerUrls(results) {
68
103
  }
69
104
  for (const httpsFunc of httpsFunctions) {
70
105
  if (!httpsFunc.uri) {
71
- logger_1.logger.debug("Missing URI for HTTPS function in printTriggerUrls. This shouldn't happen");
106
+ logger_1.logger.debug("Not printing URL for HTTPS function. Typically this means it didn't match a filter or we failed deployment");
72
107
  continue;
73
108
  }
74
109
  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, options) {
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) => options.deleteAll || (0, deploymentTool_1.isFirebaseManaged)(have[id].labels || {}))
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,20 +48,25 @@ function calculateUpdate(want, have) {
48
48
  return update;
49
49
  }
50
50
  exports.calculateUpdate = calculateUpdate;
51
- function createDeploymentPlan(want, have, options = {}) {
51
+ function createDeploymentPlan(args) {
52
+ let { wantBackend, haveBackend, codebase, filters, deleteAll } = args;
52
53
  let deployment = {};
53
- want = backend.matchingBackend(want, (endpoint) => {
54
- return (0, functionsDeployHelper_1.functionMatchesAnyGroup)(endpoint, options.filters || []);
54
+ wantBackend = backend.matchingBackend(wantBackend, (endpoint) => {
55
+ return (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
55
56
  });
56
- have = backend.matchingBackend(have, (endpoint) => {
57
- return (0, functionsDeployHelper_1.functionMatchesAnyGroup)(endpoint, options.filters || []);
57
+ const wantedEndpoint = backend.hasEndpoint(wantBackend);
58
+ haveBackend = backend.matchingBackend(haveBackend, (endpoint) => {
59
+ return wantedEndpoint(endpoint) || (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
58
60
  });
59
- const regions = new Set([...Object.keys(want.endpoints), ...Object.keys(have.endpoints)]);
61
+ const regions = new Set([
62
+ ...Object.keys(wantBackend.endpoints),
63
+ ...Object.keys(haveBackend.endpoints),
64
+ ]);
60
65
  for (const region of regions) {
61
- const changesets = calculateChangesets(want.endpoints[region] || {}, have.endpoints[region] || {}, (e) => `${e.region}-${e.availableMemoryMb || "default"}`, options);
66
+ const changesets = calculateChangesets(wantBackend.endpoints[region] || {}, haveBackend.endpoints[region] || {}, (e) => `${codebase}-${e.region}-${e.availableMemoryMb || "default"}`, deleteAll);
62
67
  deployment = Object.assign(Object.assign({}, deployment), changesets);
63
68
  }
64
- if (upgradedToGCFv2WithoutSettingConcurrency(want, have)) {
69
+ if (upgradedToGCFv2WithoutSettingConcurrency(wantBackend, haveBackend)) {
65
70
  utils.logLabeledBullet("functions", "You are updating one or more functions to Google Cloud Functions v2, " +
66
71
  "which introduces support for concurrent execution. New functions " +
67
72
  "default to 80 concurrent executions, but existing functions keep the " +
@@ -103,7 +108,6 @@ function changedTriggerRegion(want, have) {
103
108
  }
104
109
  exports.changedTriggerRegion = changedTriggerRegion;
105
110
  function changedV2PubSubTopic(want, have) {
106
- var _a, _b;
107
111
  if (want.platform !== "gcfv2") {
108
112
  return false;
109
113
  }
@@ -122,7 +126,7 @@ function changedV2PubSubTopic(want, have) {
122
126
  if (have.eventTrigger.eventType !== v2events.PUBSUB_PUBLISH_EVENT) {
123
127
  return false;
124
128
  }
125
- return (((_a = backend.findEventFilter(have, "topic")) === null || _a === void 0 ? void 0 : _a.value) !== ((_b = backend.findEventFilter(want, "topic")) === null || _b === void 0 ? void 0 : _b.value));
129
+ return have.eventTrigger.eventFilters.topic !== want.eventTrigger.eventFilters.topic;
126
130
  }
127
131
  exports.changedV2PubSubTopic = changedV2PubSubTopic;
128
132
  function upgradedScheduleFromV1ToV2(want, have) {
@@ -158,6 +162,9 @@ function checkForIllegalUpdate(want, have) {
158
162
  else if (backend.isTaskQueueTriggered(e)) {
159
163
  return "a task queue";
160
164
  }
165
+ else if (backend.isBlockingTriggered(e)) {
166
+ return e.blockingTrigger.eventType;
167
+ }
161
168
  throw Error("Functions release planner is not able to handle an unknown trigger type");
162
169
  };
163
170
  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 track = require("../../../track");
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(track.track("function_deploy_success", tag, result.durationMs));
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(track.track("function_deploy_abort", tag, result.durationMs));
43
+ reports.push((0, track_1.track)("function_deploy_abort", tag, result.durationMs));
44
44
  }
45
45
  else {
46
46
  totalErrors++;
47
- reports.push(track.track("function_deploy_failure", tag, result.durationMs));
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(track.track("functions_region_count", regionCountTag, 1));
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(track.track("functions_codebase_deploy", tag, summary.results.length));
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(track.track("functions_deploy_result", "success", totalSuccesses));
64
+ reports.push((0, track_1.track)("functions_deploy_result", "success", totalSuccesses));
65
65
  }
66
66
  else if (totalSuccesses > 0) {
67
- reports.push(track.track("functions_deploy_result", "partial_success", totalSuccesses));
68
- reports.push(track.track("functions_deploy_result", "partial_failure", totalErrors));
69
- reports.push(track.track("functions_deploy_result", "partial_error_ratio", totalErrors / (totalSuccesses + totalErrors)));
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(track.track("functions_deploy_result", "failure", totalErrors));
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 + "." + key : key;
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
- if (schema[key] === "string") {
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 (schema[key] === "number") {
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 (schema[key] === "boolean") {
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 (schema[key] === "array") {
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 (schema[key] === "object") {
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
  }