firebase-tools 10.4.2 → 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.
Files changed (108) hide show
  1. package/lib/bin/firebase.js +1 -1
  2. package/lib/command.js +4 -4
  3. package/lib/commands/deploy.js +1 -1
  4. package/lib/commands/emulators-start.js +13 -3
  5. package/lib/commands/ext-configure.js +15 -5
  6. package/lib/commands/ext-dev-emulators-start.js +5 -1
  7. package/lib/commands/ext-export.js +6 -5
  8. package/lib/commands/ext-install.js +28 -44
  9. package/lib/commands/ext-update.js +9 -1
  10. package/lib/commands/functions-delete.js +2 -5
  11. package/lib/commands/functions-secrets-destroy.js +23 -3
  12. package/lib/commands/functions-secrets-prune.js +15 -12
  13. package/lib/commands/functions-secrets-set.js +51 -4
  14. package/lib/commands/hosting-channel-deploy.js +2 -2
  15. package/lib/deploy/database/deploy.js +4 -0
  16. package/lib/deploy/database/index.js +1 -0
  17. package/lib/deploy/extensions/deploy.js +4 -4
  18. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  19. package/lib/deploy/extensions/planner.js +36 -9
  20. package/lib/deploy/extensions/prepare.js +1 -1
  21. package/lib/deploy/extensions/secrets.js +2 -2
  22. package/lib/deploy/extensions/tasks.js +60 -21
  23. package/lib/deploy/functions/backend.js +17 -6
  24. package/lib/deploy/functions/build.js +162 -0
  25. package/lib/deploy/functions/checkIam.js +6 -5
  26. package/lib/deploy/functions/deploy.js +14 -15
  27. package/lib/deploy/functions/ensure.js +4 -4
  28. package/lib/deploy/functions/functionsDeployHelper.js +54 -23
  29. package/lib/deploy/functions/prepare.js +92 -39
  30. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  31. package/lib/deploy/functions/pricing.js +6 -3
  32. package/lib/deploy/functions/prompts.js +1 -7
  33. package/lib/deploy/functions/release/fabricator.js +44 -5
  34. package/lib/deploy/functions/release/index.js +31 -6
  35. package/lib/deploy/functions/release/planner.js +10 -8
  36. package/lib/deploy/functions/release/reporter.js +14 -11
  37. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  38. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
  39. package/lib/deploy/functions/runtimes/node/index.js +1 -1
  40. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  41. package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
  42. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  43. package/lib/deploy/functions/services/auth.js +95 -0
  44. package/lib/deploy/functions/services/index.js +41 -21
  45. package/lib/deploy/functions/services/storage.js +1 -6
  46. package/lib/deploy/functions/validate.js +8 -5
  47. package/lib/deploy/hosting/args.js +2 -0
  48. package/lib/deploy/hosting/convertConfig.js +37 -8
  49. package/lib/deploy/hosting/deploy.js +3 -3
  50. package/lib/deploy/hosting/prepare.js +2 -2
  51. package/lib/deploy/hosting/release.js +6 -2
  52. package/lib/deploy/index.js +82 -93
  53. package/lib/deploy/remoteconfig/deploy.js +4 -0
  54. package/lib/deploy/remoteconfig/index.js +3 -1
  55. package/lib/emulator/auth/operations.js +26 -20
  56. package/lib/emulator/auth/state.js +79 -43
  57. package/lib/emulator/auth/utils.js +3 -25
  58. package/lib/emulator/commandUtils.js +72 -2
  59. package/lib/emulator/controller.js +14 -5
  60. package/lib/emulator/downloadableEmulators.js +47 -24
  61. package/lib/emulator/extensions/postinstall.js +41 -0
  62. package/lib/emulator/extensions/validation.js +2 -2
  63. package/lib/emulator/extensionsEmulator.js +85 -21
  64. package/lib/emulator/functionsEmulator.js +79 -7
  65. package/lib/emulator/functionsEmulatorShared.js +36 -21
  66. package/lib/emulator/registry.js +34 -12
  67. package/lib/emulator/shared/request.js +19 -0
  68. package/lib/emulator/storage/apis/firebase.js +32 -35
  69. package/lib/emulator/storage/apis/gcloud.js +84 -66
  70. package/lib/emulator/storage/files.js +56 -52
  71. package/lib/emulator/storage/index.js +23 -3
  72. package/lib/emulator/storage/metadata.js +18 -8
  73. package/lib/emulator/storage/rules/manager.js +7 -17
  74. package/lib/emulator/storage/rules/utils.js +11 -3
  75. package/lib/emulator/storage/server.js +38 -12
  76. package/lib/ensureApiEnabled.js +8 -4
  77. package/lib/extensions/askUserForParam.js +14 -11
  78. package/lib/extensions/changelog.js +1 -1
  79. package/lib/extensions/emulator/optionsHelper.js +9 -10
  80. package/lib/extensions/emulator/specHelper.js +7 -1
  81. package/lib/extensions/emulator/triggerHelper.js +11 -14
  82. package/lib/extensions/extensionsApi.js +2 -1
  83. package/lib/extensions/extensionsHelper.js +30 -24
  84. package/lib/extensions/manifest.js +28 -8
  85. package/lib/extensions/paramHelper.js +19 -13
  86. package/lib/extensions/provisioningHelper.js +2 -2
  87. package/lib/extensions/warnings.js +3 -3
  88. package/lib/functions/env.js +10 -2
  89. package/lib/functions/events/index.js +7 -0
  90. package/lib/functions/events/v1.js +6 -0
  91. package/lib/functions/projectConfig.js +24 -3
  92. package/lib/functions/runtimeConfigExport.js +10 -6
  93. package/lib/functions/secrets.js +99 -6
  94. package/lib/gcp/cloudfunctions.js +37 -18
  95. package/lib/gcp/cloudfunctionsv2.js +41 -25
  96. package/lib/gcp/cloudtasks.js +5 -3
  97. package/lib/gcp/identityPlatform.js +44 -0
  98. package/lib/gcp/secretManager.js +2 -2
  99. package/lib/metaprogramming.js +2 -0
  100. package/lib/previews.js +1 -1
  101. package/lib/serve/hosting.js +25 -12
  102. package/lib/serve/index.js +6 -0
  103. package/lib/track.js +15 -21
  104. package/lib/utils.js +30 -1
  105. package/npm-shrinkwrap.json +44 -2
  106. package/package.json +4 -1
  107. package/schema/firebase-config.json +6 -0
  108. package/lib/emulator/storage/list.js +0 -18
@@ -1,40 +1,71 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFunctionLabel = exports.getFilterGroups = exports.functionMatchesGroup = exports.functionMatchesAnyGroup = void 0;
4
- function functionMatchesAnyGroup(func, filterGroups) {
5
- if (!filterGroups.length) {
3
+ exports.getFunctionLabel = exports.getEndpointFilters = exports.parseFunctionSelector = exports.endpointMatchesFilter = exports.endpointMatchesAnyFilter = void 0;
4
+ const projectConfig = require("../../functions/projectConfig");
5
+ function endpointMatchesAnyFilter(endpoint, filters) {
6
+ if (!filters) {
6
7
  return true;
7
8
  }
8
- return filterGroups.some((groupChunk) => functionMatchesGroup(func, groupChunk));
9
+ return filters.some((filter) => endpointMatchesFilter(endpoint, filter));
9
10
  }
10
- exports.functionMatchesAnyGroup = functionMatchesAnyGroup;
11
- function functionMatchesGroup(func, groupChunks) {
12
- const functionNameChunks = func.id.split("-").slice(0, groupChunks.length);
13
- if (functionNameChunks.length !== groupChunks.length) {
11
+ exports.endpointMatchesAnyFilter = endpointMatchesAnyFilter;
12
+ function endpointMatchesFilter(endpoint, filter) {
13
+ if (endpoint.codebase && filter.codebase) {
14
+ if (endpoint.codebase !== filter.codebase) {
15
+ return false;
16
+ }
17
+ }
18
+ if (!filter.idChunks) {
19
+ return true;
20
+ }
21
+ const idChunks = endpoint.id.split("-");
22
+ if (idChunks.length < filter.idChunks.length) {
14
23
  return false;
15
24
  }
16
- for (let i = 0; i < groupChunks.length; i += 1) {
17
- if (groupChunks[i] !== functionNameChunks[i]) {
25
+ for (let i = 0; i < filter.idChunks.length; i += 1) {
26
+ if (idChunks[i] !== filter.idChunks[i]) {
18
27
  return false;
19
28
  }
20
29
  }
21
30
  return true;
22
31
  }
23
- exports.functionMatchesGroup = functionMatchesGroup;
24
- function getFilterGroups(options) {
32
+ exports.endpointMatchesFilter = endpointMatchesFilter;
33
+ function parseFunctionSelector(selector) {
34
+ const fragments = selector.split(":");
35
+ if (fragments.length < 2) {
36
+ return [
37
+ { codebase: fragments[0] },
38
+ { codebase: projectConfig.DEFAULT_CODEBASE, idChunks: fragments[0].split(/[-.]/) },
39
+ ];
40
+ }
41
+ return [
42
+ {
43
+ codebase: fragments[0],
44
+ idChunks: fragments[1].split(/[-.]/),
45
+ },
46
+ ];
47
+ }
48
+ exports.parseFunctionSelector = parseFunctionSelector;
49
+ function getEndpointFilters(options) {
25
50
  if (!options.only) {
26
- return [];
27
- }
28
- const only = options.only.split(",");
29
- const onlyFunctions = only.filter((filter) => {
30
- const opts = filter.split(":");
31
- return opts[0] === "functions" && opts[1];
32
- });
33
- return onlyFunctions.map((filter) => {
34
- return filter.split(":")[1].split(/[.-]/);
35
- });
51
+ return undefined;
52
+ }
53
+ const selectors = options.only.split(",");
54
+ const filters = [];
55
+ for (let selector of selectors) {
56
+ if (selector.startsWith("functions:")) {
57
+ selector = selector.replace("functions:", "");
58
+ if (selector.length > 0) {
59
+ filters.push(...parseFunctionSelector(selector));
60
+ }
61
+ }
62
+ }
63
+ if (filters.length === 0) {
64
+ return undefined;
65
+ }
66
+ return filters;
36
67
  }
37
- exports.getFilterGroups = getFilterGroups;
68
+ exports.getEndpointFilters = getEndpointFilters;
38
69
  function getFunctionLabel(fn) {
39
70
  return `${fn.id}(${fn.region})`;
40
71
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.inferDetailsFromExisting = exports.prepare = void 0;
3
+ exports.inferBlockingDetails = exports.inferDetailsFromExisting = exports.prepare = void 0;
4
4
  const clc = require("cli-color");
5
5
  const backend = require("./backend");
6
6
  const ensureApiEnabled = require("../../ensureApiEnabled");
@@ -20,6 +20,8 @@ const triggerRegionHelper_1 = require("./triggerRegionHelper");
20
20
  const checkIam_1 = require("./checkIam");
21
21
  const error_1 = require("../../error");
22
22
  const projectConfig_1 = require("../../functions/projectConfig");
23
+ const previews_1 = require("../../previews");
24
+ const v1_1 = require("../../functions/events/v1");
23
25
  function hasUserConfig(config) {
24
26
  return Object.keys(config).length > 1;
25
27
  }
@@ -30,6 +32,25 @@ async function prepare(context, options, payload) {
30
32
  const projectId = (0, projectUtils_1.needProjectId)(options);
31
33
  const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
32
34
  context.config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
35
+ context.filters = (0, functionsDeployHelper_1.getEndpointFilters)(options);
36
+ if (context.filters &&
37
+ !context.filters.map((f) => f.codebase).includes(context.config.codebase)) {
38
+ throw new error_1.FirebaseError("No function matches given --only filters. Aborting deployment.");
39
+ }
40
+ const checkAPIsEnabled = await Promise.all([
41
+ ensureApiEnabled.ensure(projectId, "cloudfunctions.googleapis.com", "functions"),
42
+ ensureApiEnabled.check(projectId, "runtimeconfig.googleapis.com", "runtimeconfig", true),
43
+ ensure.cloudBuildEnabled(projectId),
44
+ ensure.maybeEnableAR(projectId),
45
+ ]);
46
+ context.artifactRegistryEnabled = checkAPIsEnabled[3];
47
+ const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
48
+ context.firebaseConfig = firebaseConfig;
49
+ let runtimeConfig = { firebase: firebaseConfig };
50
+ if (checkAPIsEnabled[1]) {
51
+ runtimeConfig = Object.assign(Object.assign({}, runtimeConfig), (await (0, prepareFunctionsUpload_1.getFunctionsConfig)(projectId)));
52
+ }
53
+ (0, utils_1.logLabeledBullet)("functions", `preparing codebase ${clc.bold(context.config.codebase)} for deployment`);
33
54
  const sourceDirName = context.config.source;
34
55
  if (!sourceDirName) {
35
56
  throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions source defined in firebase.json`);
@@ -46,17 +67,6 @@ async function prepare(context, options, payload) {
46
67
  await runtimeDelegate.validate();
47
68
  logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
48
69
  await runtimeDelegate.build();
49
- const checkAPIsEnabled = await Promise.all([
50
- ensureApiEnabled.ensure(projectId, "cloudfunctions.googleapis.com", "functions"),
51
- ensureApiEnabled.check(projectId, "runtimeconfig.googleapis.com", "runtimeconfig", true),
52
- ensure.cloudBuildEnabled(projectId),
53
- ensure.maybeEnableAR(projectId),
54
- ]);
55
- context.runtimeConfigEnabled = checkAPIsEnabled[1];
56
- context.artifactRegistryEnabled = checkAPIsEnabled[3];
57
- const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
58
- context.firebaseConfig = firebaseConfig;
59
- const runtimeConfig = await (0, prepareFunctionsUpload_1.getFunctionsConfig)(context);
60
70
  const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
61
71
  const userEnvOpt = {
62
72
  functionsSource: sourceDir,
@@ -76,7 +86,46 @@ async function prepare(context, options, payload) {
76
86
  logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
77
87
  const wantBackend = await runtimeDelegate.discoverSpec(runtimeConfig, firebaseEnvs);
78
88
  wantBackend.environmentVariables = Object.assign(Object.assign({}, userEnvs), firebaseEnvs);
79
- payload.functions = { backend: wantBackend };
89
+ for (const endpoint of backend.allEndpoints(wantBackend)) {
90
+ endpoint.environmentVariables = wantBackend.environmentVariables;
91
+ endpoint.codebase = context.config.codebase;
92
+ }
93
+ const source = {};
94
+ if (backend.someEndpoint(wantBackend, () => true)) {
95
+ (0, utils_1.logLabeledBullet)("functions", `preparing ${clc.bold(sourceDirName)} directory for uploading...`);
96
+ }
97
+ if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
98
+ if (!previews_1.previews.functionsv2) {
99
+ throw new error_1.FirebaseError("This version of firebase-tools does not support Google Cloud " +
100
+ "Functions gen 2\n" +
101
+ "If Cloud Functions for Firebase gen 2 is still in alpha, sign up " +
102
+ "for the alpha program at " +
103
+ "https://services.google.com/fb/forms/firebasealphaprogram/\n" +
104
+ "If Cloud Functions for Firebase gen 2 is in beta, get the latest " +
105
+ "version of Firebse Tools with `npm i -g firebase-tools@latest`");
106
+ }
107
+ source.functionsSourceV2 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, context.config);
108
+ }
109
+ if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv1")) {
110
+ source.functionsSourceV1 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, context.config, runtimeConfig);
111
+ }
112
+ context.source = source;
113
+ const wantEndpointNames = backend.allEndpoints(wantBackend).map((e) => backend.functionName(e));
114
+ const haveBackend = backend.matchingBackend(await backend.existingBackend(context), (endpoint) => {
115
+ var _a;
116
+ if (endpoint.codebase === ((_a = context.config) === null || _a === void 0 ? void 0 : _a.codebase)) {
117
+ return true;
118
+ }
119
+ return wantEndpointNames.includes(backend.functionName(endpoint));
120
+ });
121
+ inferDetailsFromExisting(wantBackend, haveBackend, usedDotenv);
122
+ await (0, triggerRegionHelper_1.ensureTriggerRegions)(wantBackend);
123
+ validate.endpointsAreValid(wantBackend);
124
+ inferBlockingDetails(wantBackend);
125
+ payload.functions = { wantBackend: wantBackend, haveBackend: haveBackend };
126
+ await Promise.all(Object.values(wantBackend.requiredAPIs).map(({ api }) => {
127
+ return ensureApiEnabled.ensure(projectId, api, "functions", false);
128
+ }));
80
129
  if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
81
130
  const V2_APIS = [
82
131
  "artifactregistry.googleapis.com",
@@ -90,36 +139,13 @@ async function prepare(context, options, payload) {
90
139
  });
91
140
  await Promise.all(enablements);
92
141
  }
93
- if (backend.someEndpoint(wantBackend, () => true)) {
94
- (0, utils_1.logBullet)(clc.cyan.bold("functions:") +
95
- " preparing " +
96
- clc.bold(sourceDirName) +
97
- " directory for uploading...");
98
- }
99
- if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv1")) {
100
- context.functionsSourceV1 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, context.config, runtimeConfig);
101
- }
102
- if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
103
- context.functionsSourceV2 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, context.config);
104
- }
105
- for (const endpoint of backend.allEndpoints(wantBackend)) {
106
- endpoint.environmentVariables = wantBackend.environmentVariables;
107
- }
108
- await Promise.all(Object.values(wantBackend.requiredAPIs).map(({ api }) => {
109
- return ensureApiEnabled.ensure(projectId, api, "functions", false);
110
- }));
111
- validate.endpointsAreValid(wantBackend);
112
- context.filters = (0, functionsDeployHelper_1.getFilterGroups)(options);
113
142
  const matchingBackend = backend.matchingBackend(wantBackend, (endpoint) => {
114
- return (0, functionsDeployHelper_1.functionMatchesAnyGroup)(endpoint, context.filters);
143
+ return (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, context.filters);
115
144
  });
116
- const haveBackend = await backend.existingBackend(context);
117
- await (0, checkIam_1.ensureServiceAgentRoles)(projectNumber, wantBackend, haveBackend);
118
- inferDetailsFromExisting(wantBackend, haveBackend, usedDotenv);
119
- await (0, triggerRegionHelper_1.ensureTriggerRegions)(wantBackend);
120
145
  await (0, prompts_1.promptForFailurePolicies)(options, matchingBackend, haveBackend);
121
146
  await (0, prompts_1.promptForMinInstances)(options, matchingBackend, haveBackend);
122
- await backend.checkAvailability(context, wantBackend);
147
+ await backend.checkAvailability(context, matchingBackend);
148
+ await (0, checkIam_1.ensureServiceAgentRoles)(projectNumber, matchingBackend, haveBackend);
123
149
  await validate.secretsAreValid(projectId, matchingBackend);
124
150
  await ensure.secretAccess(projectId, matchingBackend, haveBackend);
125
151
  }
@@ -155,3 +181,30 @@ function maybeCopyTriggerRegion(wantE, haveE) {
155
181
  }
156
182
  wantE.eventTrigger.region = haveE.eventTrigger.region;
157
183
  }
184
+ function inferBlockingDetails(want) {
185
+ var _a, _b, _c;
186
+ const authBlockingEndpoints = backend
187
+ .allEndpoints(want)
188
+ .filter((ep) => backend.isBlockingTriggered(ep) &&
189
+ v1_1.AUTH_BLOCKING_EVENTS.includes(ep.blockingTrigger.eventType));
190
+ if (authBlockingEndpoints.length === 0) {
191
+ return;
192
+ }
193
+ let accessToken = false;
194
+ let idToken = false;
195
+ let refreshToken = false;
196
+ for (const blockingEp of authBlockingEndpoints) {
197
+ accessToken || (accessToken = !!((_a = blockingEp.blockingTrigger.options) === null || _a === void 0 ? void 0 : _a.accessToken));
198
+ idToken || (idToken = !!((_b = blockingEp.blockingTrigger.options) === null || _b === void 0 ? void 0 : _b.idToken));
199
+ refreshToken || (refreshToken = !!((_c = blockingEp.blockingTrigger.options) === null || _c === void 0 ? void 0 : _c.refreshToken));
200
+ }
201
+ for (const blockingEp of authBlockingEndpoints) {
202
+ if (!blockingEp.blockingTrigger.options) {
203
+ blockingEp.blockingTrigger.options = {};
204
+ }
205
+ blockingEp.blockingTrigger.options.accessToken = accessToken;
206
+ blockingEp.blockingTrigger.options.idToken = idToken;
207
+ blockingEp.blockingTrigger.options.refreshToken = refreshToken;
208
+ }
209
+ }
210
+ exports.inferBlockingDetails = inferBlockingDetails;
@@ -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(context) {
17
+ async function getFunctionsConfig(projectId) {
18
18
  var _a, _b;
19
- let config = {};
20
- if (context.runtimeConfigEnabled) {
21
- try {
22
- config = await functionsConfig.materializeAll(context.firebaseConfig.projectId);
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
- catch (err) {
25
- logger_1.logger.debug(err);
26
- 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;
27
- if (!errorCode) {
28
- logger_1.logger.debug("Got unexpected error from Runtime Config; it has no status code:", err);
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
- config.firebase = context.firebaseConfig;
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: 2.4,
102
- 4096: 4.8,
103
- 8192: 4.8,
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 {
@@ -124,9 +127,7 @@ class Fabricator {
124
127
  await this.setTrigger(endpoint);
125
128
  }
126
129
  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
- }
130
+ update.endpoint.labels = Object.assign(Object.assign({}, update.endpoint.labels), deploymentTool.labels());
130
131
  if (update.deleteAndRecreate) {
131
132
  await this.deleteEndpoint(update.deleteAndRecreate);
132
133
  await this.createEndpoint(update.endpoint, scraper);
@@ -197,6 +198,14 @@ 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
211
  var _a;
@@ -204,7 +213,7 @@ class Fabricator {
204
213
  logger_1.logger.debug("Precondition failed. Cannot create a GCFv2 function without storage");
205
214
  throw new Error("Precondition failed");
206
215
  }
207
- const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage[endpoint.region]);
216
+ const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage);
208
217
  const topic = (_a = apiFunction.eventTrigger) === null || _a === void 0 ? void 0 : _a.pubsubTopic;
209
218
  if (topic) {
210
219
  await this.executor
@@ -254,6 +263,12 @@ class Fabricator {
254
263
  .catch(rethrowAs(endpoint, "set invoker"));
255
264
  }
256
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
+ }
257
272
  const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
258
273
  if (mem >= backend.MIN_MEMORY_FOR_CONCURRENCY && endpoint.concurrency !== 1) {
259
274
  await this.setConcurrency(endpoint, serviceName, endpoint.concurrency || DEFAULT_GCFV2_CONCURRENCY);
@@ -281,6 +296,10 @@ class Fabricator {
281
296
  else if (backend.isTaskQueueTriggered(endpoint)) {
282
297
  invoker = endpoint.taskQueueTrigger.invoker;
283
298
  }
299
+ else if (backend.isBlockingTriggered(endpoint) &&
300
+ v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
301
+ invoker = ["public"];
302
+ }
284
303
  if (invoker) {
285
304
  await this.executor
286
305
  .run(() => gcf.setInvokerUpdate(endpoint.project, backend.functionName(endpoint), invoker))
@@ -293,7 +312,7 @@ class Fabricator {
293
312
  logger_1.logger.debug("Precondition failed. Cannot update a GCFv2 function without storage");
294
313
  throw new Error("Precondition failed");
295
314
  }
296
- const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage[endpoint.region]);
315
+ const apiFunction = gcfV2.functionFromEndpoint(endpoint, this.storage);
297
316
  if ((_a = apiFunction.eventTrigger) === null || _a === void 0 ? void 0 : _a.pubsubTopic) {
298
317
  delete apiFunction.eventTrigger.pubsubTopic;
299
318
  }
@@ -312,6 +331,10 @@ class Fabricator {
312
331
  else if (backend.isTaskQueueTriggered(endpoint)) {
313
332
  invoker = endpoint.taskQueueTrigger.invoker;
314
333
  }
334
+ else if (backend.isBlockingTriggered(endpoint) &&
335
+ v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
336
+ invoker = ["public"];
337
+ }
315
338
  if (invoker) {
316
339
  await this.executor
317
340
  .run(() => run.setInvokerUpdate(endpoint.project, serviceName, invoker))
@@ -371,6 +394,9 @@ class Fabricator {
371
394
  else if (backend.isTaskQueueTriggered(endpoint)) {
372
395
  await this.upsertTaskQueue(endpoint);
373
396
  }
397
+ else if (backend.isBlockingTriggered(endpoint)) {
398
+ await this.registerBlockingTrigger(endpoint);
399
+ }
374
400
  }
375
401
  async deleteTrigger(endpoint) {
376
402
  if (backend.isScheduleTriggered(endpoint)) {
@@ -387,6 +413,9 @@ class Fabricator {
387
413
  else if (backend.isTaskQueueTriggered(endpoint)) {
388
414
  await this.disableTaskQueue(endpoint);
389
415
  }
416
+ else if (backend.isBlockingTriggered(endpoint)) {
417
+ await this.unregisterBlockingTrigger(endpoint);
418
+ }
390
419
  }
391
420
  async upsertScheduleV1(endpoint) {
392
421
  const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
@@ -408,6 +437,11 @@ class Fabricator {
408
437
  .catch(rethrowAs(endpoint, "set invoker"));
409
438
  }
410
439
  }
440
+ async registerBlockingTrigger(endpoint) {
441
+ await this.executor
442
+ .run(() => services.serviceForEndpoint(endpoint).registerTrigger(endpoint))
443
+ .catch(rethrowAs(endpoint, "register blocking trigger"));
444
+ }
411
445
  async deleteScheduleV1(endpoint) {
412
446
  const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
413
447
  await this.executor
@@ -429,6 +463,11 @@ class Fabricator {
429
463
  .run(() => cloudtasks.updateQueue(update))
430
464
  .catch(rethrowAs(endpoint, "disable task queue"));
431
465
  }
466
+ async unregisterBlockingTrigger(endpoint) {
467
+ await this.executor
468
+ .run(() => services.serviceForEndpoint(endpoint).unregisterTrigger(endpoint))
469
+ .catch(rethrowAs(endpoint, "unregister blocking trigger"));
470
+ }
432
471
  logOpStart(op, endpoint) {
433
472
  const runtime = (0, runtimes_1.getHumanFriendlyRuntimeName)(endpoint.runtime);
434
473
  const label = helper.getFunctionLabel(endpoint);
@@ -11,14 +11,21 @@ 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
+ const { wantBackend, haveBackend } = payload.functions;
28
+ const plan = planner.createDeploymentPlan(wantBackend, haveBackend, context.filters);
22
29
  const fnsToDelete = Object.values(plan)
23
30
  .map((regionalChanges) => regionalChanges.endpointsToDelete)
24
31
  .reduce(functional_1.reduceFlat, []);
@@ -37,15 +44,15 @@ async function release(context, options, payload) {
37
44
  const fab = new fabricator.Fabricator({
38
45
  functionExecutor,
39
46
  executor: new executor.QueueExecutor({}),
40
- sourceUrl: context.sourceUrl,
41
- storage: context.storage,
47
+ sourceUrl: context.source.sourceUrl,
48
+ storage: context.source.storage,
42
49
  appEngineLocation: (0, functionsConfig_1.getAppEngineLocation)(context.firebaseConfig),
43
50
  });
44
51
  const summary = await fab.applyPlan(plan);
45
52
  await reporter.logAndTrackDeployStats(summary);
46
53
  reporter.printErrors(summary);
47
- printTriggerUrls(payload.functions.backend);
48
- const haveEndpoints = backend.allEndpoints(payload.functions.backend);
54
+ printTriggerUrls(payload.functions.wantBackend);
55
+ const haveEndpoints = backend.allEndpoints(payload.functions.wantBackend);
49
56
  const deletedEndpoints = Object.values(plan)
50
57
  .map((r) => r.endpointsToDelete)
51
58
  .reduce(functional_1.reduceFlat, []);
@@ -59,6 +66,24 @@ async function release(context, options, payload) {
59
66
  const opts = allErrors.length === 1 ? { original: allErrors[0] } : { children: allErrors };
60
67
  throw new error_1.FirebaseError("There was an error deploying functions", Object.assign(Object.assign({}, opts), { exit: 2 }));
61
68
  }
69
+ else {
70
+ if (secrets.of(haveEndpoints).length > 0) {
71
+ const projectId = (0, projectUtils_1.needProjectId)(options);
72
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
73
+ const reloadedBackend = await backend.existingBackend({ projectId });
74
+ const prunedResult = await secrets.pruneAndDestroySecrets({ projectId, projectNumber }, backend.allEndpoints(reloadedBackend));
75
+ if (prunedResult.destroyed.length > 0) {
76
+ (0, utils_1.logLabeledBullet)("functions", `Destroyed unused secret versions: ${prunedResult.destroyed
77
+ .map((s) => `${s.secret}@${s.version}`)
78
+ .join(", ")}`);
79
+ }
80
+ if (prunedResult.erred.length > 0) {
81
+ (0, utils_1.logLabeledWarning)("functions", `Failed to destroy unused secret versions:\n\t${prunedResult.erred
82
+ .map((err) => err.message)
83
+ .join("\n\t")}`);
84
+ }
85
+ }
86
+ }
62
87
  }
63
88
  exports.release = release;
64
89
  function printTriggerUrls(results) {
@@ -68,7 +93,7 @@ function printTriggerUrls(results) {
68
93
  }
69
94
  for (const httpsFunc of httpsFunctions) {
70
95
  if (!httpsFunc.uri) {
71
- logger_1.logger.debug("Missing URI for HTTPS function in printTriggerUrls. This shouldn't happen");
96
+ logger_1.logger.debug("Not printing URL for HTTPS function. Typically this means it didn't match a filter or we failed deployment");
72
97
  continue;
73
98
  }
74
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, 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,17 +48,17 @@ function calculateUpdate(want, have) {
48
48
  return update;
49
49
  }
50
50
  exports.calculateUpdate = calculateUpdate;
51
- function createDeploymentPlan(want, have, options = {}) {
51
+ function createDeploymentPlan(want, have, filters, deleteAll) {
52
52
  let deployment = {};
53
53
  want = backend.matchingBackend(want, (endpoint) => {
54
- return (0, functionsDeployHelper_1.functionMatchesAnyGroup)(endpoint, options.filters || []);
54
+ return (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
55
55
  });
56
56
  have = backend.matchingBackend(have, (endpoint) => {
57
- return (0, functionsDeployHelper_1.functionMatchesAnyGroup)(endpoint, options.filters || []);
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"}`, options);
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)) {
@@ -103,7 +103,6 @@ function changedTriggerRegion(want, have) {
103
103
  }
104
104
  exports.changedTriggerRegion = changedTriggerRegion;
105
105
  function changedV2PubSubTopic(want, have) {
106
- var _a, _b;
107
106
  if (want.platform !== "gcfv2") {
108
107
  return false;
109
108
  }
@@ -122,7 +121,7 @@ function changedV2PubSubTopic(want, have) {
122
121
  if (have.eventTrigger.eventType !== v2events.PUBSUB_PUBLISH_EVENT) {
123
122
  return false;
124
123
  }
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));
124
+ return have.eventTrigger.eventFilters.topic !== want.eventTrigger.eventFilters.topic;
126
125
  }
127
126
  exports.changedV2PubSubTopic = changedV2PubSubTopic;
128
127
  function upgradedScheduleFromV1ToV2(want, have) {
@@ -158,6 +157,9 @@ function checkForIllegalUpdate(want, have) {
158
157
  else if (backend.isTaskQueueTriggered(e)) {
159
158
  return "a task queue";
160
159
  }
160
+ else if (backend.isBlockingTriggered(e)) {
161
+ return e.blockingTrigger.eventType;
162
+ }
161
163
  throw Error("Functions release planner is not able to handle an unknown trigger type");
162
164
  };
163
165
  const wantType = triggerType(want);