firebase-tools 11.3.0 → 11.4.2

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 (50) hide show
  1. package/README.md +8 -15
  2. package/lib/apiv2.js +5 -0
  3. package/lib/checkValidTargetFilters.js +3 -2
  4. package/lib/command.js +1 -0
  5. package/lib/commands/hosting-clone.js +5 -0
  6. package/lib/commands/login-ci.js +2 -0
  7. package/lib/database/rulesConfig.js +35 -8
  8. package/lib/deploy/functions/backend.js +6 -4
  9. package/lib/deploy/functions/build.js +107 -95
  10. package/lib/deploy/functions/ensure.js +1 -1
  11. package/lib/deploy/functions/params.js +5 -2
  12. package/lib/deploy/functions/prepare.js +3 -3
  13. package/lib/deploy/functions/pricing.js +3 -2
  14. package/lib/deploy/functions/prompts.js +1 -1
  15. package/lib/deploy/functions/release/fabricator.js +8 -7
  16. package/lib/deploy/functions/release/index.js +4 -0
  17. package/lib/deploy/functions/runtimes/discovery/parsing.js +19 -8
  18. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +115 -107
  19. package/lib/deploy/functions/runtimes/node/parseTriggers.js +53 -21
  20. package/lib/deploy/functions/services/storage.js +6 -0
  21. package/lib/deploy/hosting/convertConfig.js +87 -16
  22. package/lib/deploy/hosting/deploy.js +1 -1
  23. package/lib/deploy/index.js +1 -1
  24. package/lib/deploy/storage/prepare.js +29 -6
  25. package/lib/emulator/controller.js +0 -1
  26. package/lib/emulator/functionsEmulator.js +3 -0
  27. package/lib/emulator/functionsEmulatorRuntime.js +1 -1
  28. package/lib/emulator/functionsEmulatorShared.js +6 -11
  29. package/lib/emulator/storage/files.js +19 -22
  30. package/lib/emulator/storage/metadata.js +6 -6
  31. package/lib/emulator/storage/persistence.js +26 -12
  32. package/lib/extensions/displayExtensionInfo.js +1 -101
  33. package/lib/extensions/emulator/triggerHelper.js +2 -2
  34. package/lib/extensions/updateHelper.js +1 -7
  35. package/lib/functional.js +16 -1
  36. package/lib/functions/env.js +47 -2
  37. package/lib/gcp/cloudfunctions.js +21 -8
  38. package/lib/gcp/cloudfunctionsv2.js +43 -19
  39. package/lib/gcp/cloudscheduler.js +25 -13
  40. package/lib/gcp/cloudtasks.js +4 -3
  41. package/lib/gcp/iam.js +20 -17
  42. package/lib/gcp/proto.js +18 -6
  43. package/lib/gcp/resourceManager.js +25 -3
  44. package/lib/index.js +1 -1
  45. package/lib/previews.js +1 -1
  46. package/lib/rc.js +3 -9
  47. package/lib/requireAuth.js +4 -0
  48. package/lib/rulesDeploy.js +40 -3
  49. package/npm-shrinkwrap.json +48 -37
  50. package/package.json +7 -4
package/README.md CHANGED
@@ -174,11 +174,11 @@ Use `firebase:deploy --only remoteconfig` to update and publish a project's Fire
174
174
 
175
175
  The Firebase CLI can use one of four authentication methods listed in descending priority:
176
176
 
177
- - **User Token** - provide an explicit long-lived Firebase user token generated from `firebase login:ci`. Note that these tokens are extremely sensitive long-lived credentials and are not the right option for most cases. Consider using service account authorization instead. The token can be set in one of two ways:
177
+ - **User Token** - **DEPRECATED: this authentication method will be removed in a future major version of `firebase-tools`; use a service account to authenticate instead** - provide an explicit long-lived Firebase user token generated from `firebase login:ci`. Note that these tokens are extremely sensitive long-lived credentials and are not the right option for most cases. Consider using service account authorization instead. The token can be set in one of two ways:
178
178
  - Set the `--token` flag on any command, for example `firebase --token="<token>" projects:list`.
179
179
  - Set the `FIREBASE_TOKEN` environment variable.
180
180
  - **Local Login** - run `firebase login` to log in to the CLI directly as yourself. The CLI will cache an authorized user credential on your machine.
181
- - **Service Account** - set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to point to the path of a JSON service account key file.
181
+ - **Service Account** - set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to point to the path of a JSON service account key file. For more details, see Google Cloud's [Getting started with authentication](https://cloud.google.com/docs/authentication/getting-started) guide.
182
182
  - **Application Default Credentials** - if you use the `gcloud` CLI and log in with `gcloud auth application-default login`, the Firebase CLI will use them if none of the above credentials are present.
183
183
 
184
184
  ### Multiple Accounts
@@ -224,21 +224,14 @@ or `HTTP_PROXY` value in your environment to the URL of your proxy (e.g.
224
224
  The Firebase CLI requires a browser to complete authentication, but is fully
225
225
  compatible with CI and other headless environments.
226
226
 
227
- 1. On a machine with a browser, install the Firebase CLI.
228
- 2. Run `firebase login:ci` to log in and print out a new [refresh token](https://developers.google.com/identity/protocols/OAuth2)
229
- (the current CLI session will not be affected).
230
- 3. Store the output token in a secure but accessible way in your CI system.
227
+ Complete the following steps to run Firebase commands in a CI environment. Find detailed instructions for each step in Google Cloud's [Getting started with authentication](https://cloud.google.com/docs/authentication/getting-started) guide.
231
228
 
232
- There are two ways to use this token when running Firebase commands:
229
+ 1. Create a service account and grant it the appropriate level of access to your project.
230
+ 1. Create a service account key (JSON file) for that service account.
231
+ 1. Store the key file in a secure, accessible way in your CI system.
232
+ 1. Set `GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json` in your CI system when running Firebase commands.
233
233
 
234
- 1. Store the token as the environment variable `FIREBASE_TOKEN` and it will
235
- automatically be utilized.
236
- 2. Run all commands with the `--token <token>` flag in your CI system.
237
-
238
- The order of precedence for token loading is flag, environment variable, active project.
239
-
240
- On any machine with the Firebase CLI, running `firebase logout --token <token>`
241
- will immediately revoke access for the specified token.
234
+ To disable access for the service account, [find the service account](https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts) for your project in the Google Cloud Console, and then either remove the key, or disable or delete the service account.
242
235
 
243
236
  ## Using as a Module
244
237
 
package/lib/apiv2.js CHANGED
@@ -15,6 +15,7 @@ const responseToError_1 = require("./responseToError");
15
15
  const FormData = require("form-data");
16
16
  const pkg = require("../package.json");
17
17
  const CLI_VERSION = pkg.version;
18
+ const GOOG_QUOTA_USER = "x-goog-quota-user";
18
19
  let accessToken = "";
19
20
  let refreshToken = "";
20
21
  function setRefreshToken(token = "") {
@@ -309,6 +310,10 @@ class Client {
309
310
  }
310
311
  const logURL = this.requestURL(options);
311
312
  logger_1.logger.debug(`>>> [apiv2][query] ${options.method} ${logURL} ${queryParamsLog}`);
313
+ const headers = options.headers;
314
+ if (headers && headers.has(GOOG_QUOTA_USER)) {
315
+ logger_1.logger.debug(`>>> [apiv2][(partial)header] ${options.method} ${logURL} x-goog-quota-user=${headers.get(GOOG_QUOTA_USER) || ""}`);
316
+ }
312
317
  if (options.body !== undefined) {
313
318
  let logBody = "[omitted]";
314
319
  if (!((_b = options.skipLog) === null || _b === void 0 ? void 0 : _b.body)) {
@@ -19,6 +19,7 @@ function targetsHaveFilters(...targets) {
19
19
  function targetsHaveNoFilters(...targets) {
20
20
  return targets.some((t) => !t.includes(":"));
21
21
  }
22
+ const FILTERABLE_TARGETS = new Set(["hosting", "functions", "firestore", "storage", "database"]);
22
23
  async function checkValidTargetFilters(options) {
23
24
  const only = !options.only ? [] : options.only.split(",");
24
25
  return new Promise((resolve, reject) => {
@@ -28,10 +29,10 @@ async function checkValidTargetFilters(options) {
28
29
  if (options.except) {
29
30
  return reject(new error_1.FirebaseError("Cannot specify both --only and --except"));
30
31
  }
31
- const nonFilteredTypes = deploy_1.VALID_DEPLOY_TARGETS.filter((t) => !["hosting", "functions", "firestore"].includes(t));
32
+ const nonFilteredTypes = deploy_1.VALID_DEPLOY_TARGETS.filter((t) => !FILTERABLE_TARGETS.has(t));
32
33
  const targetsForNonFilteredTypes = targetsForTypes(only, ...nonFilteredTypes);
33
34
  if (targetsForNonFilteredTypes.length && targetsHaveFilters(...targetsForNonFilteredTypes)) {
34
- return reject(new error_1.FirebaseError("Filters specified with colons (e.g. --only functions:func1,functions:func2) are only supported for functions, hosting, and firestore"));
35
+ return reject(new error_1.FirebaseError("Filters specified with colons (e.g. --only functions:func1,functions:func2) are only supported for functions, hosting, storage, and firestore"));
35
36
  }
36
37
  const targetsForFunctions = targetsForTypes(only, "functions");
37
38
  if (targetsForFunctions.length &&
package/lib/command.js CHANGED
@@ -64,6 +64,7 @@ class Command {
64
64
  });
65
65
  if (this.helpText) {
66
66
  cmd.on("--help", () => {
67
+ console.log();
67
68
  console.log(this.helpText);
68
69
  });
69
70
  }
@@ -12,6 +12,11 @@ const { marked } = require("marked");
12
12
  const logger_1 = require("../logger");
13
13
  exports.command = new command_1.Command("hosting:clone <source> <targetChannel>")
14
14
  .description("clone a version from one site to another")
15
+ .help(`<source> and <targetChannel> accept the following format: <siteId>:<channelId>
16
+
17
+ For example, to copy the content for a site \`my-site\` from a preview channel \`staging\` to a \`live\` channel, the command would look be:
18
+
19
+ firebase hosting:clone my-site:foo my-site:live`)
15
20
  .before(requireAuth_1.requireAuth)
16
21
  .action(async (source = "", targetChannel = "") => {
17
22
  var _a, _b, _c, _d;
@@ -14,6 +14,8 @@ exports.command = new command_1.Command("login:ci")
14
14
  if (options.nonInteractive) {
15
15
  throw new error_1.FirebaseError("Cannot run login:ci in non-interactive mode.");
16
16
  }
17
+ utils.logWarning("Authenticating with a `login:ci` token is deprecated and will be removed in a future major version of `firebase-tools`. " +
18
+ "Instead, use a service account key with `GOOGLE_APPLICATION_CREDENTIALS`: https://cloud.google.com/docs/authentication/getting-started");
17
19
  const userCredentials = await auth.loginGoogle(options.localhost);
18
20
  logger_1.logger.info();
19
21
  utils.logSuccess("Success! Use this token to login on a CI server:\n\n" +
@@ -19,6 +19,23 @@ function getRulesConfig(projectId, options) {
19
19
  if (dbConfig === undefined) {
20
20
  return [];
21
21
  }
22
+ const rc = options.rc;
23
+ let allDatabases = !options.only;
24
+ const onlyDatabases = new Set();
25
+ if (options.only) {
26
+ const split = options.only.split(",");
27
+ if (split.includes("database")) {
28
+ allDatabases = true;
29
+ }
30
+ else {
31
+ for (const value of split) {
32
+ if (value.startsWith("database:")) {
33
+ const target = value.split(":")[1];
34
+ onlyDatabases.add(target);
35
+ }
36
+ }
37
+ }
38
+ }
22
39
  if (!Array.isArray(dbConfig)) {
23
40
  if (dbConfig && dbConfig.rules) {
24
41
  utils.assertIsStringOrUndefined(options.instance);
@@ -31,22 +48,32 @@ function getRulesConfig(projectId, options) {
31
48
  }
32
49
  }
33
50
  const results = [];
34
- const rc = options.rc;
35
51
  for (const c of dbConfig) {
36
- if (c.target) {
37
- rc.requireTarget(projectId, "database", c.target);
38
- const instances = rc.target(projectId, "database", c.target);
39
- for (const i of instances) {
40
- results.push({ instance: i, rules: c.rules });
52
+ const { instance, target } = c;
53
+ if (target) {
54
+ if (allDatabases || onlyDatabases.has(target)) {
55
+ rc.requireTarget(projectId, "database", target);
56
+ const instances = rc.target(projectId, "database", target);
57
+ for (const i of instances) {
58
+ results.push({ instance: i, rules: c.rules });
59
+ }
60
+ onlyDatabases.delete(target);
41
61
  }
42
62
  }
43
- else if (c.instance) {
44
- results.push(c);
63
+ else if (instance) {
64
+ if (allDatabases) {
65
+ results.push(c);
66
+ }
45
67
  }
46
68
  else {
47
69
  throw new error_1.FirebaseError('Must supply either "target" or "instance" in database config');
48
70
  }
49
71
  }
72
+ if (!allDatabases && onlyDatabases.size !== 0) {
73
+ throw new error_1.FirebaseError(`Could not find configurations in firebase.json for the following database targets: ${[
74
+ ...onlyDatabases,
75
+ ].join(", ")}`);
76
+ }
50
77
  return results;
51
78
  }
52
79
  exports.getRulesConfig = getRulesConfig;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.merge = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_CPU_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.DEFAULT_CONCURRENCY = exports.memoryToGen2Cpu = exports.memoryToGen1Cpu = exports.memoryOptionDisplayName = exports.AllMemoryOptions = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.endpointTriggerType = void 0;
3
+ exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.merge = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_CPU_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.DEFAULT_CONCURRENCY = exports.memoryToGen2Cpu = exports.memoryToGen1Cpu = exports.memoryOptionDisplayName = exports.isValidMemoryOption = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.endpointTriggerType = void 0;
4
4
  const gcf = require("../../gcp/cloudfunctions");
5
5
  const gcfV2 = require("../../gcp/cloudfunctionsv2");
6
6
  const run = require("../../gcp/run");
@@ -37,9 +37,11 @@ exports.AllIngressSettings = [
37
37
  "ALLOW_INTERNAL_ONLY",
38
38
  "ALLOW_INTERNAL_AND_GCLB",
39
39
  ];
40
- exports.AllMemoryOptions = [
41
- 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
42
- ];
40
+ const allMemoryOptions = [128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768];
41
+ function isValidMemoryOption(mem) {
42
+ return allMemoryOptions.includes(mem);
43
+ }
44
+ exports.isValidMemoryOption = isValidMemoryOption;
43
45
  function memoryOptionDisplayName(option) {
44
46
  return {
45
47
  128: "128MB",
@@ -8,6 +8,7 @@ const params = require("./params");
8
8
  const previews_1 = require("../../previews");
9
9
  const error_1 = require("../../error");
10
10
  const functional_1 = require("../../functional");
11
+ const logger_1 = require("../../logger");
11
12
  function empty() {
12
13
  return {
13
14
  requiredAPIs: [],
@@ -22,9 +23,6 @@ function of(endpoints) {
22
23
  return build;
23
24
  }
24
25
  exports.of = of;
25
- function isMemoryOption(value) {
26
- return value == null || [128, 256, 512, 1024, 2048, 4096, 8192].includes(value);
27
- }
28
26
  async function resolveBackend(build, userEnvOpt, userEnvs) {
29
27
  const projectId = userEnvOpt.projectId;
30
28
  let paramValues = {};
@@ -34,7 +32,49 @@ async function resolveBackend(build, userEnvOpt, userEnvs) {
34
32
  return toBackend(build, paramValues);
35
33
  }
36
34
  exports.resolveBackend = resolveBackend;
35
+ class Resolver {
36
+ constructor(paramValues) {
37
+ this.paramValues = paramValues;
38
+ this.resolveInt = (i) => {
39
+ if (i === null) {
40
+ return i;
41
+ }
42
+ return params.resolveInt(i, this.paramValues);
43
+ };
44
+ this.resolveBoolean = (i) => {
45
+ if (i === null) {
46
+ return i;
47
+ }
48
+ return params.resolveBoolean(i, this.paramValues);
49
+ };
50
+ this.resolveString = (i) => {
51
+ if (i === null) {
52
+ return i;
53
+ }
54
+ return params.resolveString(i, this.paramValues);
55
+ };
56
+ }
57
+ resolveStrings(dest, src, ...keys) {
58
+ for (const key of keys) {
59
+ const orig = src[key];
60
+ if (typeof orig === "undefined") {
61
+ continue;
62
+ }
63
+ dest[key] = orig === null ? null : params.resolveString(orig, this.paramValues);
64
+ }
65
+ }
66
+ resolveInts(dest, src, ...keys) {
67
+ for (const key of keys) {
68
+ const orig = src[key];
69
+ if (typeof orig === "undefined") {
70
+ continue;
71
+ }
72
+ dest[key] = orig === null ? null : params.resolveInt(orig, this.paramValues);
73
+ }
74
+ }
75
+ }
37
76
  function toBackend(build, paramValues) {
77
+ const r = new Resolver(paramValues);
38
78
  const bkEndpoints = [];
39
79
  for (const endpointId of Object.keys(build.endpoints)) {
40
80
  const bdEndpoint = build.endpoints[endpointId];
@@ -43,39 +83,37 @@ function toBackend(build, paramValues) {
43
83
  regions = [api.functionsDefaultRegion];
44
84
  }
45
85
  for (const region of regions) {
46
- const trigger = discoverTrigger(bdEndpoint, paramValues);
86
+ const trigger = discoverTrigger(bdEndpoint, region, r);
47
87
  if (typeof bdEndpoint.platform === "undefined") {
48
88
  throw new error_1.FirebaseError("platform can't be undefined");
49
89
  }
50
- if (!isMemoryOption(bdEndpoint.availableMemoryMb)) {
90
+ if (bdEndpoint.availableMemoryMb != null &&
91
+ !backend.isValidMemoryOption(bdEndpoint.availableMemoryMb)) {
51
92
  throw new error_1.FirebaseError("available memory must be a supported value, if present");
52
93
  }
53
- let timeout;
54
- if (bdEndpoint.timeoutSeconds) {
55
- timeout = params.resolveInt(bdEndpoint.timeoutSeconds, paramValues);
56
- }
57
- else {
58
- timeout = 60;
59
- }
60
- const bkEndpoint = Object.assign({ id: endpointId, project: bdEndpoint.project, region: region, entryPoint: bdEndpoint.entryPoint, platform: bdEndpoint.platform, runtime: bdEndpoint.runtime, timeoutSeconds: timeout }, trigger);
61
- proto.renameIfPresent(bkEndpoint, bdEndpoint, "maxInstances", "maxInstances", (from) => {
62
- return params.resolveInt(from, paramValues);
94
+ const bkEndpoint = Object.assign({ id: endpointId, project: bdEndpoint.project, region: region, entryPoint: bdEndpoint.entryPoint, platform: bdEndpoint.platform, runtime: bdEndpoint.runtime }, trigger);
95
+ proto.copyIfPresent(bkEndpoint, bdEndpoint, "environmentVariables", "labels", "secretEnvironmentVariables", "serviceAccount");
96
+ proto.convertIfPresent(bkEndpoint, bdEndpoint, "ingressSettings", (from) => {
97
+ if (from !== null && !backend.AllIngressSettings.includes(from)) {
98
+ throw new error_1.FirebaseError(`Cannot set ingress settings to invalid value ${from}`);
99
+ }
100
+ return from;
63
101
  });
64
- proto.renameIfPresent(bkEndpoint, bdEndpoint, "minInstances", "minInstances", (from) => {
65
- return params.resolveInt(from, paramValues);
102
+ proto.convertIfPresent(bkEndpoint, bdEndpoint, "availableMemoryMb", (from) => {
103
+ const mem = r.resolveInt(from);
104
+ if (mem !== null && !backend.isValidMemoryOption(mem)) {
105
+ logger_1.logger.debug("Warning; setting memory to unexpected value", mem);
106
+ }
107
+ return mem;
66
108
  });
67
- proto.renameIfPresent(bkEndpoint, bdEndpoint, "concurrency", "concurrency", (from) => {
68
- return params.resolveInt(from, paramValues);
69
- });
70
- proto.copyIfPresent(bkEndpoint, bdEndpoint, "ingressSettings", "availableMemoryMb", "environmentVariables", "labels");
71
- proto.copyIfPresent(bkEndpoint, bdEndpoint, "secretEnvironmentVariables");
109
+ r.resolveInts(bkEndpoint, bdEndpoint, "timeoutSeconds", "maxInstances", "minInstances", "concurrency");
110
+ proto.convertIfPresent(bkEndpoint, bdEndpoint, "cpu", (0, functional_1.nullsafeVisitor)((cpu) => (cpu === "gcf_gen1" ? cpu : r.resolveInt(cpu))));
72
111
  if (bdEndpoint.vpc) {
73
112
  bkEndpoint.vpc = { connector: params.resolveString(bdEndpoint.vpc.connector, paramValues) };
74
113
  proto.copyIfPresent(bkEndpoint.vpc, bdEndpoint.vpc, "egressSettings");
75
114
  }
76
- proto.renameIfPresent(bkEndpoint, bdEndpoint, "serviceAccountEmail", "serviceAccount");
77
- if ("serviceAccountEmail" in bkEndpoint && !bdEndpoint.serviceAccount) {
78
- delete bkEndpoint.serviceAccountEmail;
115
+ else if (bdEndpoint.vpc === null) {
116
+ bkEndpoint.vpc = null;
79
117
  }
80
118
  bkEndpoints.push(bkEndpoint);
81
119
  }
@@ -85,101 +123,75 @@ function toBackend(build, paramValues) {
85
123
  return bkend;
86
124
  }
87
125
  exports.toBackend = toBackend;
88
- function discoverTrigger(endpoint, paramValues) {
89
- const resolveInt = (from) => params.resolveInt(from, paramValues);
90
- const resolveString = (from) => params.resolveString(from, paramValues);
91
- const resolveBoolean = (from) => params.resolveBoolean(from, paramValues);
92
- let trigger;
126
+ function discoverTrigger(endpoint, region, r) {
93
127
  if ("httpsTrigger" in endpoint) {
94
- const bkHttps = {};
95
- if (endpoint.httpsTrigger.invoker) {
96
- bkHttps.invoker = endpoint.httpsTrigger.invoker;
128
+ const httpsTrigger = {};
129
+ if (endpoint.httpsTrigger.invoker === null) {
130
+ httpsTrigger.invoker = null;
131
+ }
132
+ else if (typeof endpoint.httpsTrigger.invoker !== "undefined") {
133
+ httpsTrigger.invoker = endpoint.httpsTrigger.invoker.map(r.resolveString);
97
134
  }
98
- trigger = { httpsTrigger: bkHttps };
135
+ return { httpsTrigger };
99
136
  }
100
137
  else if ("callableTrigger" in endpoint) {
101
- trigger = { callableTrigger: {} };
138
+ return { callableTrigger: {} };
102
139
  }
103
140
  else if ("blockingTrigger" in endpoint) {
104
- trigger = { blockingTrigger: endpoint.blockingTrigger };
141
+ return { blockingTrigger: endpoint.blockingTrigger };
105
142
  }
106
143
  else if ("eventTrigger" in endpoint) {
107
- const bkEventFilters = {};
108
- for (const [key, value] of Object.entries(endpoint.eventTrigger.eventFilters)) {
109
- bkEventFilters[key] = params.resolveString(value, paramValues);
110
- }
111
- const bkEvent = {
144
+ const eventTrigger = {
112
145
  eventType: endpoint.eventTrigger.eventType,
113
- eventFilters: bkEventFilters,
114
- retry: resolveBoolean(endpoint.eventTrigger.retry || false),
146
+ retry: r.resolveBoolean(endpoint.eventTrigger.retry) || false,
115
147
  };
116
- if (endpoint.eventTrigger.eventFilterPathPatterns) {
117
- const bkEventFiltersPathPatterns = {};
118
- for (const [key, value] of Object.entries(endpoint.eventTrigger.eventFilterPathPatterns)) {
119
- bkEventFiltersPathPatterns[key] = params.resolveString(value, paramValues);
120
- }
121
- bkEvent.eventFilterPathPatterns = bkEventFiltersPathPatterns;
122
- }
123
- if (endpoint.eventTrigger.serviceAccount) {
124
- bkEvent.serviceAccountEmail = endpoint.eventTrigger.serviceAccount;
148
+ if (endpoint.eventTrigger.eventFilters) {
149
+ eventTrigger.eventFilters = (0, functional_1.mapObject)(endpoint.eventTrigger.eventFilters, r.resolveString);
125
150
  }
126
- if (endpoint.eventTrigger.region) {
127
- bkEvent.region = resolveString(endpoint.eventTrigger.region);
128
- }
129
- if (endpoint.eventTrigger.channel) {
130
- bkEvent.channel = endpoint.eventTrigger.channel;
151
+ if (endpoint.eventTrigger.eventFilterPathPatterns) {
152
+ eventTrigger.eventFilterPathPatterns = (0, functional_1.mapObject)(endpoint.eventTrigger.eventFilterPathPatterns, r.resolveString);
131
153
  }
132
- trigger = { eventTrigger: bkEvent };
154
+ r.resolveStrings(eventTrigger, endpoint.eventTrigger, "serviceAccount", "region", "channel");
155
+ return { eventTrigger };
133
156
  }
134
157
  else if ("scheduleTrigger" in endpoint) {
135
158
  const bkSchedule = {
136
- schedule: resolveString(endpoint.scheduleTrigger.schedule),
137
- timeZone: resolveString(endpoint.scheduleTrigger.timeZone),
159
+ schedule: r.resolveString(endpoint.scheduleTrigger.schedule),
160
+ timeZone: r.resolveString(endpoint.scheduleTrigger.timeZone),
138
161
  };
139
- const bkRetry = {};
140
- if (endpoint.scheduleTrigger.retryConfig.maxBackoffSeconds) {
141
- bkRetry.maxBackoffDuration = proto.durationFromSeconds(resolveInt(endpoint.scheduleTrigger.retryConfig.maxBackoffSeconds));
162
+ if (endpoint.scheduleTrigger.retryConfig) {
163
+ const bkRetry = {};
164
+ r.resolveInts(bkRetry, endpoint.scheduleTrigger.retryConfig, "maxBackoffSeconds", "minBackoffSeconds", "maxRetrySeconds", "retryCount", "maxDoublings");
165
+ bkSchedule.retryConfig = bkRetry;
142
166
  }
143
- if (endpoint.scheduleTrigger.retryConfig.minBackoffSeconds) {
144
- bkRetry.minBackoffDuration = proto.durationFromSeconds(resolveInt(endpoint.scheduleTrigger.retryConfig.minBackoffSeconds));
167
+ else if (endpoint.scheduleTrigger.retryConfig === null) {
168
+ bkSchedule.retryConfig = null;
145
169
  }
146
- if (endpoint.scheduleTrigger.retryConfig.maxRetrySeconds) {
147
- bkRetry.maxRetryDuration = proto.durationFromSeconds(resolveInt(endpoint.scheduleTrigger.retryConfig.maxRetrySeconds));
148
- }
149
- proto.copyIfPresent(bkRetry, endpoint.scheduleTrigger.retryConfig, "retryCount", "maxDoublings");
150
- bkSchedule.retryConfig = bkRetry;
151
- trigger = { scheduleTrigger: bkSchedule };
170
+ return { scheduleTrigger: bkSchedule };
152
171
  }
153
172
  else if ("taskQueueTrigger" in endpoint) {
154
- const bkTaskQueue = {};
173
+ const taskQueueTrigger = {};
155
174
  if (endpoint.taskQueueTrigger.rateLimits) {
156
- const bkRateLimits = {};
157
- proto.renameIfPresent(bkRateLimits, endpoint.taskQueueTrigger.rateLimits, "maxConcurrentDispatches", "maxConcurrentDispatches", resolveInt);
158
- proto.renameIfPresent(bkRateLimits, endpoint.taskQueueTrigger.rateLimits, "maxDispatchesPerSecond", "maxDispatchesPerSecond", resolveInt);
159
- bkTaskQueue.rateLimits = bkRateLimits;
175
+ taskQueueTrigger.rateLimits = {};
176
+ r.resolveInts(taskQueueTrigger.rateLimits, endpoint.taskQueueTrigger.rateLimits, "maxConcurrentDispatches", "maxDispatchesPerSecond");
177
+ }
178
+ else if (endpoint.taskQueueTrigger.rateLimits === null) {
179
+ taskQueueTrigger.rateLimits = null;
160
180
  }
161
181
  if (endpoint.taskQueueTrigger.retryConfig) {
162
- const bkRetryConfig = {};
163
- proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxAttempts", resolveInt);
164
- proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "maxBackoffSeconds", "maxBackoffSeconds", (from) => {
165
- return proto.durationFromSeconds(resolveInt(from));
166
- });
167
- proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "minBackoffSeconds", "minBackoffSeconds", (from) => {
168
- return proto.durationFromSeconds(resolveInt(from));
169
- });
170
- proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "maxRetrySeconds", "maxRetryDurationSeconds", (from) => {
171
- return proto.durationFromSeconds(resolveInt(from));
172
- });
173
- proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "maxDoublings", "maxDoublings", resolveInt);
174
- bkTaskQueue.retryConfig = bkRetryConfig;
182
+ taskQueueTrigger.retryConfig = {};
183
+ r.resolveInts(taskQueueTrigger.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxBackoffSeconds", "minBackoffSeconds", "maxRetrySeconds", "maxDoublings");
184
+ }
185
+ else if (endpoint.taskQueueTrigger.retryConfig === null) {
186
+ taskQueueTrigger.retryConfig = null;
175
187
  }
176
188
  if (endpoint.taskQueueTrigger.invoker) {
177
- bkTaskQueue.invoker = endpoint.taskQueueTrigger.invoker.map((sa) => resolveString(sa));
189
+ taskQueueTrigger.invoker = endpoint.taskQueueTrigger.invoker.map(r.resolveString);
178
190
  }
179
- trigger = { taskQueueTrigger: bkTaskQueue };
180
- }
181
- else {
182
- (0, functional_1.assertExhaustive)(endpoint);
191
+ else if (endpoint.taskQueueTrigger.invoker === null) {
192
+ taskQueueTrigger.invoker = null;
193
+ }
194
+ return { taskQueueTrigger };
183
195
  }
184
- return trigger;
196
+ (0, functional_1.assertExhaustive)(endpoint);
185
197
  }
@@ -67,7 +67,7 @@ exports.cloudBuildEnabled = cloudBuildEnabled;
67
67
  async function secretsToServiceAccounts(b) {
68
68
  const secretsToSa = {};
69
69
  for (const e of backend.allEndpoints(b)) {
70
- const sa = e.serviceAccountEmail || (await module.exports.defaultServiceAccount(e));
70
+ const sa = e.serviceAccount || (await module.exports.defaultServiceAccount(e));
71
71
  for (const s of e.secretEnvironmentVariables || []) {
72
72
  const serviceAccounts = secretsToSa[s.secret] || new Set();
73
73
  serviceAccounts.add(sa);
@@ -104,7 +104,10 @@ function canSatisfyParam(param, value) {
104
104
  }
105
105
  async function resolveParams(params, projectId, userEnvs) {
106
106
  const paramValues = {};
107
- for (const param of params.filter((param) => userEnvs.hasOwnProperty(param.param))) {
107
+ const [provided, outstanding] = (0, functional_1.partition)(params, (param) => {
108
+ return {}.hasOwnProperty.call(userEnvs, param.param);
109
+ });
110
+ for (const param of provided) {
108
111
  if (!canSatisfyParam(param, userEnvs[param.param])) {
109
112
  throw new error_1.FirebaseError("Parameter " +
110
113
  param.param +
@@ -114,7 +117,7 @@ async function resolveParams(params, projectId, userEnvs) {
114
117
  }
115
118
  paramValues[param.param] = userEnvs[param.param];
116
119
  }
117
- for (const param of params.filter((param) => !userEnvs.hasOwnProperty(param.param))) {
120
+ for (const param of outstanding) {
118
121
  let paramDefault = param.default;
119
122
  if (paramDefault && isCEL(paramDefault)) {
120
123
  paramDefault = resolveDefaultCEL(param.type, paramDefault, paramValues);
@@ -172,13 +172,13 @@ function inferDetailsFromExisting(want, have, usedDotenv) {
172
172
  if (!usedDotenv) {
173
173
  wantE.environmentVariables = Object.assign(Object.assign({}, haveE.environmentVariables), wantE.environmentVariables);
174
174
  }
175
- if (!wantE.availableMemoryMb && haveE.availableMemoryMb) {
175
+ if (typeof wantE.availableMemoryMb === "undefined" && haveE.availableMemoryMb) {
176
176
  wantE.availableMemoryMb = haveE.availableMemoryMb;
177
177
  }
178
- if (!wantE.concurrency && haveE.concurrency) {
178
+ if (typeof wantE.concurrency === "undefined" && haveE.concurrency) {
179
179
  wantE.concurrency = haveE.concurrency;
180
180
  }
181
- if (!wantE.cpu && haveE.cpu) {
181
+ if (typeof wantE.cpu === "undefined" && haveE.cpu) {
182
182
  wantE.cpu = haveE.cpu;
183
183
  }
184
184
  wantE.securityLevel = haveE.securityLevel ? haveE.securityLevel : "SECURE_ALWAYS";
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.monthlyMinInstanceCost = exports.canCalculateMinInstanceCost = exports.V2_FREE_TIER = exports.V1_FREE_TIER = exports.V2_RATES = exports.V1_RATES = void 0;
4
+ const backend = require("./backend");
4
5
  const V1_REGION_TO_TIER = {
5
6
  "us-central1": 1,
6
7
  "us-east1": 1,
@@ -110,7 +111,7 @@ function canCalculateMinInstanceCost(endpoint) {
110
111
  return true;
111
112
  }
112
113
  if (endpoint.platform === "gcfv1") {
113
- if (!MB_TO_GHZ[endpoint.availableMemoryMb || 256]) {
114
+ if (!MB_TO_GHZ[endpoint.availableMemoryMb || backend.DEFAULT_MEMORY]) {
114
115
  return false;
115
116
  }
116
117
  if (!V1_REGION_TO_TIER[endpoint.region]) {
@@ -134,7 +135,7 @@ function monthlyMinInstanceCost(endpoints) {
134
135
  if (!endpoint.minInstances) {
135
136
  continue;
136
137
  }
137
- const ramMb = endpoint.availableMemoryMb || 256;
138
+ const ramMb = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
138
139
  const ramGb = ramMb / 1024;
139
140
  if (endpoint.platform === "gcfv1") {
140
141
  const cpu = MB_TO_GHZ[ramMb];
@@ -122,7 +122,7 @@ async function promptForMinInstances(options, want, have) {
122
122
  .sort(backend.compareFunctions)
123
123
  .map((fn) => {
124
124
  return (`\t${(0, functionsDeployHelper_1.getFunctionLabel)(fn)}: ${fn.minInstances} instances, ` +
125
- backend.memoryOptionDisplayName(fn.availableMemoryMb || 256) +
125
+ backend.memoryOptionDisplayName(fn.availableMemoryMb || backend.DEFAULT_MEMORY) +
126
126
  " of memory each");
127
127
  })
128
128
  .join("\n");
@@ -301,10 +301,10 @@ class Fabricator {
301
301
  endpoint.uri = (_b = resultFunction === null || resultFunction === void 0 ? void 0 : resultFunction.httpsTrigger) === null || _b === void 0 ? void 0 : _b.url;
302
302
  let invoker;
303
303
  if (backend.isHttpsTriggered(endpoint)) {
304
- invoker = endpoint.httpsTrigger.invoker;
304
+ invoker = endpoint.httpsTrigger.invoker === null ? ["public"] : endpoint.httpsTrigger.invoker;
305
305
  }
306
306
  else if (backend.isTaskQueueTriggered(endpoint)) {
307
- invoker = endpoint.taskQueueTrigger.invoker;
307
+ invoker = endpoint.taskQueueTrigger.invoker === null ? [] : endpoint.taskQueueTrigger.invoker;
308
308
  }
309
309
  else if (backend.isBlockingTriggered(endpoint) &&
310
310
  v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
@@ -337,10 +337,10 @@ class Fabricator {
337
337
  const serviceName = resultFunction.serviceConfig.service;
338
338
  let invoker;
339
339
  if (backend.isHttpsTriggered(endpoint)) {
340
- invoker = endpoint.httpsTrigger.invoker;
340
+ invoker = endpoint.httpsTrigger.invoker === null ? ["public"] : endpoint.httpsTrigger.invoker;
341
341
  }
342
342
  else if (backend.isTaskQueueTriggered(endpoint)) {
343
- invoker = endpoint.taskQueueTrigger.invoker;
343
+ invoker = endpoint.taskQueueTrigger.invoker === null ? [] : endpoint.taskQueueTrigger.invoker;
344
344
  }
345
345
  else if (backend.isBlockingTriggered(endpoint) &&
346
346
  v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
@@ -477,12 +477,13 @@ class Fabricator {
477
477
  .catch(rethrowAs(endpoint, "register blocking trigger"));
478
478
  }
479
479
  async deleteScheduleV1(endpoint) {
480
- const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
480
+ const jobName = scheduler.jobNameForEndpoint(endpoint, this.appEngineLocation);
481
481
  await this.executor
482
- .run(() => scheduler.deleteJob(job.name))
482
+ .run(() => scheduler.deleteJob(jobName))
483
483
  .catch(rethrowAs(endpoint, "delete schedule"));
484
+ const topicName = scheduler.topicNameForEndpoint(endpoint);
484
485
  await this.executor
485
- .run(() => pubsub.deleteTopic(job.pubsubTarget.topicName))
486
+ .run(() => pubsub.deleteTopic(topicName))
486
487
  .catch(rethrowAs(endpoint, "delete topic"));
487
488
  }
488
489
  deleteScheduleV2(endpoint) {
@@ -67,6 +67,10 @@ async function release(context, options, payload) {
67
67
  const allErrors = summary.results.filter((r) => r.error).map((r) => r.error);
68
68
  if (allErrors.length) {
69
69
  const opts = allErrors.length === 1 ? { original: allErrors[0] } : { children: allErrors };
70
+ logger_1.logger.debug("Functions deploy failed.");
71
+ for (const error of allErrors) {
72
+ logger_1.logger.debug(JSON.stringify(error, null, 2));
73
+ }
70
74
  throw new error_1.FirebaseError("There was an error deploying functions", Object.assign(Object.assign({}, opts), { exit: 2 }));
71
75
  }
72
76
  }