firebase-tools 9.20.0 → 9.23.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 (98) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/lib/api.js +2 -0
  3. package/lib/apiv2.js +7 -4
  4. package/lib/commands/crashlytics-symbols-upload.js +2 -2
  5. package/lib/commands/deploy.js +9 -1
  6. package/lib/commands/ext-configure.js +1 -1
  7. package/lib/commands/ext-dev-deprecate.js +63 -0
  8. package/lib/commands/ext-dev-undeprecate.js +56 -0
  9. package/lib/commands/ext-dev-unpublish.js +10 -3
  10. package/lib/commands/ext-export.js +44 -0
  11. package/lib/commands/ext-install.js +1 -1
  12. package/lib/commands/ext-update.js +1 -1
  13. package/lib/commands/functions-delete.js +55 -42
  14. package/lib/commands/functions-list.js +11 -11
  15. package/lib/commands/index.js +6 -5
  16. package/lib/commands/init.js +3 -0
  17. package/lib/config.js +3 -2
  18. package/lib/deploy/extensions/args.js +2 -0
  19. package/lib/deploy/extensions/deploy.js +49 -0
  20. package/lib/deploy/extensions/deploymentSummary.js +52 -0
  21. package/lib/deploy/extensions/errors.js +31 -0
  22. package/lib/deploy/extensions/index.js +8 -0
  23. package/lib/deploy/extensions/params.js +39 -0
  24. package/lib/deploy/extensions/planner.js +94 -0
  25. package/lib/deploy/extensions/prepare.js +111 -0
  26. package/lib/deploy/extensions/release.js +43 -0
  27. package/lib/deploy/extensions/secrets.js +150 -0
  28. package/lib/deploy/extensions/tasks.js +98 -0
  29. package/lib/deploy/extensions/validate.js +17 -0
  30. package/lib/deploy/functions/backend.js +84 -115
  31. package/lib/deploy/functions/checkIam.js +73 -12
  32. package/lib/deploy/functions/containerCleaner.js +97 -50
  33. package/lib/deploy/functions/deploy.js +4 -10
  34. package/lib/deploy/functions/eventTypes.js +10 -0
  35. package/lib/deploy/functions/functionsDeployHelper.js +3 -68
  36. package/lib/deploy/functions/prepare.js +72 -29
  37. package/lib/deploy/functions/pricing.js +17 -17
  38. package/lib/deploy/functions/prompts.js +22 -21
  39. package/lib/deploy/functions/release/executor.js +39 -0
  40. package/lib/deploy/functions/release/fabricator.js +425 -0
  41. package/lib/deploy/functions/release/index.js +73 -0
  42. package/lib/deploy/functions/release/planner.js +162 -0
  43. package/lib/deploy/functions/release/reporter.js +165 -0
  44. package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
  45. package/lib/deploy/functions/release/timer.js +14 -0
  46. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
  47. package/lib/deploy/functions/runtimes/node/parseTriggers.js +32 -54
  48. package/lib/deploy/functions/services/index.js +38 -0
  49. package/lib/deploy/functions/services/storage.js +43 -0
  50. package/lib/deploy/functions/triggerRegionHelper.js +9 -25
  51. package/lib/deploy/functions/validate.js +1 -24
  52. package/lib/deploy/index.js +10 -1
  53. package/lib/emulator/auth/apiSpec.js +37 -6
  54. package/lib/emulator/auth/operations.js +45 -17
  55. package/lib/emulator/auth/server.js +16 -2
  56. package/lib/emulator/auth/state.js +34 -15
  57. package/lib/emulator/auth/widget_ui.js +14 -0
  58. package/lib/emulator/downloadableEmulators.js +7 -7
  59. package/lib/emulator/functionsEmulator.js +18 -4
  60. package/lib/emulator/storage/cloudFunctions.js +37 -7
  61. package/lib/ensureApiEnabled.js +10 -12
  62. package/lib/extensions/askUserForParam.js +14 -6
  63. package/lib/extensions/checkProjectBilling.js +7 -7
  64. package/lib/extensions/export.js +107 -0
  65. package/lib/extensions/extensionsApi.js +103 -21
  66. package/lib/extensions/extensionsHelper.js +5 -2
  67. package/lib/extensions/listExtensions.js +16 -11
  68. package/lib/extensions/paramHelper.js +6 -4
  69. package/lib/extensions/provisioningHelper.js +16 -3
  70. package/lib/extensions/refs.js +9 -1
  71. package/lib/extensions/secretsUtils.js +10 -9
  72. package/lib/extensions/updateHelper.js +12 -2
  73. package/lib/extensions/versionHelper.js +14 -0
  74. package/lib/extensions/warnings.js +33 -1
  75. package/lib/gcp/artifactregistry.js +16 -0
  76. package/lib/gcp/cloudfunctions.js +25 -72
  77. package/lib/gcp/cloudfunctionsv2.js +46 -98
  78. package/lib/gcp/cloudscheduler.js +22 -16
  79. package/lib/gcp/cloudtasks.js +143 -0
  80. package/lib/gcp/docker.js +36 -2
  81. package/lib/gcp/location.js +44 -0
  82. package/lib/gcp/proto.js +2 -2
  83. package/lib/gcp/pubsub.js +1 -9
  84. package/lib/gcp/secretManager.js +27 -6
  85. package/lib/gcp/storage.js +48 -32
  86. package/lib/init/features/project.js +2 -1
  87. package/lib/previews.js +1 -1
  88. package/lib/projectUtils.js +10 -1
  89. package/lib/utils.js +30 -1
  90. package/package.json +5 -4
  91. package/schema/firebase-config.json +9 -0
  92. package/lib/deploy/functions/deploymentPlanner.js +0 -113
  93. package/lib/deploy/functions/deploymentTimer.js +0 -23
  94. package/lib/deploy/functions/errorHandler.js +0 -75
  95. package/lib/deploy/functions/release.js +0 -116
  96. package/lib/deploy/functions/tasks.js +0 -324
  97. package/lib/functions/listFunctions.js +0 -10
  98. package/lib/functionsDelete.js +0 -60
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.jobFromSpec = exports.createOrReplaceJob = exports.updateJob = exports.getJob = exports.deleteJob = exports.createJob = exports.assertValidJob = void 0;
3
+ exports.jobFromEndpoint = exports.createOrReplaceJob = exports.updateJob = exports.getJob = exports.deleteJob = exports.createJob = exports.assertValidJob = void 0;
4
4
  const _ = require("lodash");
5
5
  const error_1 = require("../error");
6
6
  const logger_1 = require("../logger");
7
7
  const api = require("../api");
8
8
  const backend = require("../deploy/functions/backend");
9
9
  const proto = require("./proto");
10
+ const functional_1 = require("../functional");
10
11
  const VERSION = "v1beta1";
11
12
  const DEFAULT_TIME_ZONE = "America/Los_Angeles";
12
13
  function assertValidJob(job) {
@@ -86,21 +87,26 @@ function isIdentical(job, otherJob) {
86
87
  job.timeZone === otherJob.timeZone &&
87
88
  _.isEqual(job.retryConfig, otherJob.retryConfig));
88
89
  }
89
- function jobFromSpec(schedule, appEngineLocation) {
90
- const job = {
91
- name: backend.scheduleName(schedule, appEngineLocation),
92
- schedule: schedule.schedule,
93
- };
94
- proto.copyIfPresent(job, schedule, "timeZone", "retryConfig");
95
- if (schedule.transport === "https") {
96
- throw new error_1.FirebaseError("HTTPS transport for scheduled functions is not yet supported");
90
+ function jobFromEndpoint(endpoint, appEngineLocation) {
91
+ const job = {};
92
+ if (endpoint.platform === "gcfv1") {
93
+ const id = backend.scheduleIdForFunction(endpoint);
94
+ const region = appEngineLocation;
95
+ job.name = `projects/${endpoint.project}/locations/${region}/jobs/${id}`;
96
+ job.pubsubTarget = {
97
+ topicName: `projects/${endpoint.project}/topics/${id}`,
98
+ attributes: {
99
+ scheduled: "true",
100
+ },
101
+ };
97
102
  }
98
- job.pubsubTarget = {
99
- topicName: backend.topicName(schedule),
100
- attributes: {
101
- scheduled: "true",
102
- },
103
- };
103
+ else if (endpoint.platform === "gcfv2") {
104
+ throw new error_1.FirebaseError("Do not know how to create a scheduled GCFv2 function");
105
+ }
106
+ else {
107
+ functional_1.assertExhaustive(endpoint.platform);
108
+ }
109
+ proto.copyIfPresent(job, endpoint.scheduleTrigger, "schedule", "retryConfig", "timeZone");
104
110
  return job;
105
111
  }
106
- exports.jobFromSpec = jobFromSpec;
112
+ exports.jobFromEndpoint = jobFromEndpoint;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queueFromEndpoint = exports.queueNameForEndpoint = exports.setEnqueuer = exports.getIamPolicy = exports.setIamPolicy = exports.deleteQueue = exports.purgeQueue = exports.upsertQueue = exports.updateQueue = exports.getQueue = exports.createQueue = exports.DEFAULT_SETTINGS = void 0;
4
+ const proto = require("./proto");
5
+ const apiv2_1 = require("../apiv2");
6
+ const api_1 = require("../api");
7
+ const API_VERSION = "v2";
8
+ const client = new apiv2_1.Client({
9
+ urlPrefix: api_1.cloudTasksOrigin,
10
+ auth: true,
11
+ apiVersion: API_VERSION,
12
+ });
13
+ exports.DEFAULT_SETTINGS = {
14
+ rateLimits: {
15
+ maxConcurrentDispatches: 1000,
16
+ maxBurstSize: 100,
17
+ maxDispatchesPerSecond: 500,
18
+ },
19
+ state: "RUNNING",
20
+ retryConfig: {
21
+ maxDoublings: 16,
22
+ maxAttempts: 3,
23
+ maxBackoff: "3600s",
24
+ minBackoff: "0.100s",
25
+ },
26
+ };
27
+ async function createQueue(queue) {
28
+ const path = queue.name.substring(0, queue.name.lastIndexOf("/"));
29
+ const res = await client.post(path, queue);
30
+ return res.body;
31
+ }
32
+ exports.createQueue = createQueue;
33
+ async function getQueue(name) {
34
+ const res = await client.get(name);
35
+ return res.body;
36
+ }
37
+ exports.getQueue = getQueue;
38
+ async function updateQueue(queue) {
39
+ const res = await client.patch(queue.name, queue, {
40
+ queryParams: { updateMask: proto.fieldMasks(queue).join(",") },
41
+ });
42
+ return res.body;
43
+ }
44
+ exports.updateQueue = updateQueue;
45
+ async function upsertQueue(queue) {
46
+ var _a, _b;
47
+ try {
48
+ const existing = await module.exports.getQueue(queue.name);
49
+ if (JSON.stringify(queue) === JSON.stringify(existing)) {
50
+ return false;
51
+ }
52
+ if (existing.state === "DISABLED") {
53
+ await module.exports.purgeQueue(queue.name);
54
+ }
55
+ await module.exports.updateQueue(queue);
56
+ return false;
57
+ }
58
+ catch (err) {
59
+ if (((_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) === 404) {
60
+ await module.exports.createQueue(queue);
61
+ return true;
62
+ }
63
+ throw err;
64
+ }
65
+ }
66
+ exports.upsertQueue = upsertQueue;
67
+ async function purgeQueue(name) {
68
+ await client.post(`${name}:purge`);
69
+ }
70
+ exports.purgeQueue = purgeQueue;
71
+ async function deleteQueue(name) {
72
+ await client.delete(name);
73
+ }
74
+ exports.deleteQueue = deleteQueue;
75
+ async function setIamPolicy(name, policy) {
76
+ const res = await client.post(`${name}:setIamPolicy`, {
77
+ policy,
78
+ });
79
+ return res.body;
80
+ }
81
+ exports.setIamPolicy = setIamPolicy;
82
+ async function getIamPolicy(name) {
83
+ const res = await client.post(`${name}:getIamPolicy`);
84
+ return res.body;
85
+ }
86
+ exports.getIamPolicy = getIamPolicy;
87
+ const ENQUEUER_ROLE = "roles/cloudtasks.enqueuer";
88
+ async function setEnqueuer(name, invoker, assumeEmpty = false) {
89
+ var _a, _b;
90
+ let existing;
91
+ if (assumeEmpty) {
92
+ existing = {
93
+ bindings: [],
94
+ etag: "",
95
+ version: 3,
96
+ };
97
+ }
98
+ else {
99
+ existing = await module.exports.getIamPolicy(name);
100
+ }
101
+ const [, project] = name.split("/");
102
+ const invokerMembers = proto.getInvokerMembers(invoker, project);
103
+ while (true) {
104
+ const policy = {
105
+ bindings: existing.bindings.filter((binding) => binding.role != ENQUEUER_ROLE),
106
+ etag: existing.etag,
107
+ version: existing.version,
108
+ };
109
+ if (invokerMembers.length) {
110
+ policy.bindings.push({ role: ENQUEUER_ROLE, members: invokerMembers });
111
+ }
112
+ if (JSON.stringify(policy) === JSON.stringify(existing)) {
113
+ return;
114
+ }
115
+ try {
116
+ await module.exports.setIamPolicy(name, policy);
117
+ return;
118
+ }
119
+ catch (err) {
120
+ if (((_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) === 429) {
121
+ existing = await module.exports.getIamPolicy(name);
122
+ continue;
123
+ }
124
+ throw err;
125
+ }
126
+ }
127
+ }
128
+ exports.setEnqueuer = setEnqueuer;
129
+ function queueNameForEndpoint(endpoint) {
130
+ return `projects/${endpoint.project}/locations/${endpoint.region}/queues/${endpoint.id}`;
131
+ }
132
+ exports.queueNameForEndpoint = queueNameForEndpoint;
133
+ function queueFromEndpoint(endpoint) {
134
+ const queue = Object.assign(Object.assign({}, JSON.parse(JSON.stringify(exports.DEFAULT_SETTINGS))), { name: queueNameForEndpoint(endpoint) });
135
+ if (endpoint.taskQueueTrigger.rateLimits) {
136
+ proto.copyIfPresent(queue.rateLimits, endpoint.taskQueueTrigger.rateLimits, "maxBurstSize", "maxConcurrentDispatches", "maxDispatchesPerSecond");
137
+ }
138
+ if (endpoint.taskQueueTrigger.retryConfig) {
139
+ proto.copyIfPresent(queue.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxBackoff", "maxDoublings", "maxRetryDuration", "minBackoff");
140
+ }
141
+ return queue;
142
+ }
143
+ exports.queueFromEndpoint = queueFromEndpoint;
package/lib/gcp/docker.js CHANGED
@@ -1,10 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Client = void 0;
3
+ exports.Client = exports.GCR_SUBDOMAIN_MAPPING = void 0;
4
4
  const error_1 = require("../error");
5
5
  const api = require("../apiv2");
6
+ exports.GCR_SUBDOMAIN_MAPPING = {
7
+ "us-west1": "us",
8
+ "us-west2": "us",
9
+ "us-west3": "us",
10
+ "us-west4": "us",
11
+ "us-central1": "us",
12
+ "us-central2": "us",
13
+ "us-east1": "us",
14
+ "us-east4": "us",
15
+ "northamerica-northeast1": "us",
16
+ "southamerica-east1": "us",
17
+ "europe-west1": "eu",
18
+ "europe-west2": "eu",
19
+ "europe-west3": "eu",
20
+ "europe-west4": "eu",
21
+ "europe-west5": "eu",
22
+ "europe-west6": "eu",
23
+ "europe-central2": "eu",
24
+ "europe-north1": "eu",
25
+ "asia-east1": "asia",
26
+ "asia-east2": "asia",
27
+ "asia-northeast1": "asia",
28
+ "asia-northeast2": "asia",
29
+ "asia-northeast3": "asia",
30
+ "asia-south1": "asia",
31
+ "asia-southeast2": "asia",
32
+ "australia-southeast1": "asia",
33
+ };
6
34
  function isErrors(response) {
7
- return Object.prototype.hasOwnProperty.call(response, "errors");
35
+ return !!response && Object.prototype.hasOwnProperty.call(response, "errors");
8
36
  }
9
37
  const API_VERSION = "v2";
10
38
  class Client {
@@ -27,6 +55,9 @@ class Client {
27
55
  async deleteTag(path, tag) {
28
56
  var _a;
29
57
  const response = await this.client.delete(`${path}/manifests/${tag}`);
58
+ if (!response.body) {
59
+ return;
60
+ }
30
61
  if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) != 0) {
31
62
  throw new error_1.FirebaseError(`Failed to delete tag ${tag} at path ${path}`, {
32
63
  children: response.body.errors,
@@ -36,6 +67,9 @@ class Client {
36
67
  async deleteImage(path, digest) {
37
68
  var _a;
38
69
  const response = await this.client.delete(`${path}/manifests/${digest}`);
70
+ if (!response.body) {
71
+ return;
72
+ }
39
73
  if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) != 0) {
40
74
  throw new error_1.FirebaseError(`Failed to delete image ${digest} at path ${path}`, {
41
75
  children: response.body.errors,
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.regionInLocation = exports.DUAL_REGION_MAPPING = exports.MULTI_REGION_MAPPING = void 0;
4
+ exports.MULTI_REGION_MAPPING = {
5
+ "us-central1": "us",
6
+ "us-east1": "us",
7
+ "us-east4": "us",
8
+ "us-west1": "us",
9
+ "us-west2": "us",
10
+ "us-west3": "us",
11
+ "us-west4": "us",
12
+ "europe-central2": "eu",
13
+ "europe-north1": "eu",
14
+ "europe-west1": "eu",
15
+ "europe-west3": "eu",
16
+ "europe-west4": "eu",
17
+ "europe-west5": "eu",
18
+ "asia-east1": "asia",
19
+ "asia-east2": "asia",
20
+ "asia-northeast1": "asia",
21
+ "asia-northeast2": "asia",
22
+ "asia-northeast3": "asia",
23
+ "asia-south1": "asia",
24
+ "asia-south2": "asia",
25
+ "asia-southeast1": "asia",
26
+ "asia-southeast2": "asia",
27
+ };
28
+ exports.DUAL_REGION_MAPPING = {
29
+ "asia-northeast1": "asia1",
30
+ "asia-northeast2": "asia1",
31
+ "europe-north1": "eur4",
32
+ "europe-west4": "eur4",
33
+ "us-central1": "nam4",
34
+ "us-east1": "nam4",
35
+ };
36
+ function regionInLocation(region, location) {
37
+ region = region.toLowerCase();
38
+ location = location.toLowerCase();
39
+ if (exports.MULTI_REGION_MAPPING[region] === location || exports.DUAL_REGION_MAPPING[region] === location) {
40
+ return true;
41
+ }
42
+ return false;
43
+ }
44
+ exports.regionInLocation = regionInLocation;
package/lib/gcp/proto.js CHANGED
@@ -67,10 +67,10 @@ function fieldMasksHelper(prefixes, cursor, doNotRecurseIn, masks) {
67
67
  }
68
68
  }
69
69
  function getInvokerMembers(invoker, projectId) {
70
- if (invoker[0] === "private") {
70
+ if (invoker.includes("private")) {
71
71
  return [];
72
72
  }
73
- if (invoker[0] === "public") {
73
+ if (invoker.includes("public")) {
74
74
  return ["allUsers"];
75
75
  }
76
76
  return invoker.map((inv) => formatServiceAccount(inv, projectId));
package/lib/gcp/pubsub.js CHANGED
@@ -1,9 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.topicFromSpec = exports.deleteTopic = exports.updateTopic = exports.getTopic = exports.createTopic = void 0;
3
+ exports.deleteTopic = exports.updateTopic = exports.getTopic = exports.createTopic = void 0;
4
4
  const apiv2_1 = require("../apiv2");
5
5
  const api_1 = require("../api");
6
- const backend = require("../deploy/functions/backend");
7
6
  const proto = require("./proto");
8
7
  const API_VERSION = "v1";
9
8
  const client = new apiv2_1.Client({
@@ -33,10 +32,3 @@ async function deleteTopic(name) {
33
32
  await client.delete(name);
34
33
  }
35
34
  exports.deleteTopic = deleteTopic;
36
- function topicFromSpec(spec) {
37
- return {
38
- name: backend.topicName(spec),
39
- labels: Object.assign({}, spec.labels),
40
- };
41
- }
42
- exports.topicFromSpec = topicFromSpec;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.grantServiceAgentRole = exports.addVersion = exports.createSecret = exports.parseSecretResourceName = exports.secretExists = exports.getSecretLabels = exports.getSecret = exports.listSecrets = void 0;
3
+ exports.grantServiceAgentRole = exports.addVersion = exports.createSecret = exports.toSecretVersionResourceName = exports.parseSecretVersionResourceName = exports.parseSecretResourceName = exports.secretExists = exports.getSecretVersion = exports.getSecret = exports.listSecrets = exports.secretManagerConsoleUri = void 0;
4
+ const utils_1 = require("../utils");
4
5
  const api = require("../api");
6
+ exports.secretManagerConsoleUri = (projectId) => `https://console.cloud.google.com/security/secret-manager?project=${projectId}`;
5
7
  async function listSecrets(projectId) {
6
8
  const listRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets`, {
7
9
  auth: true,
@@ -11,21 +13,24 @@ async function listSecrets(projectId) {
11
13
  }
12
14
  exports.listSecrets = listSecrets;
13
15
  async function getSecret(projectId, name) {
16
+ var _a;
14
17
  const getRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets/${name}`, {
15
18
  auth: true,
16
19
  origin: api.secretManagerOrigin,
17
20
  });
18
- return parseSecretResourceName(getRes.body.name);
21
+ const secret = parseSecretResourceName(getRes.body.name);
22
+ secret.labels = (_a = getRes.body.labels) !== null && _a !== void 0 ? _a : {};
23
+ return secret;
19
24
  }
20
25
  exports.getSecret = getSecret;
21
- async function getSecretLabels(projectId, name) {
22
- const getRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets/${name}`, {
26
+ async function getSecretVersion(projectId, name, version) {
27
+ const getRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets/${name}/versions/${version}`, {
23
28
  auth: true,
24
29
  origin: api.secretManagerOrigin,
25
30
  });
26
- return getRes.body.labels;
31
+ return parseSecretVersionResourceName(getRes.body.name);
27
32
  }
28
- exports.getSecretLabels = getSecretLabels;
33
+ exports.getSecretVersion = getSecretVersion;
29
34
  async function secretExists(projectId, name) {
30
35
  try {
31
36
  await getSecret(projectId, name);
@@ -47,6 +52,21 @@ function parseSecretResourceName(resourceName) {
47
52
  };
48
53
  }
49
54
  exports.parseSecretResourceName = parseSecretResourceName;
55
+ function parseSecretVersionResourceName(resourceName) {
56
+ const nameTokens = resourceName.split("/");
57
+ return {
58
+ secret: {
59
+ projectId: nameTokens[1],
60
+ name: nameTokens[3],
61
+ },
62
+ versionId: nameTokens[5],
63
+ };
64
+ }
65
+ exports.parseSecretVersionResourceName = parseSecretVersionResourceName;
66
+ function toSecretVersionResourceName(secretVersion) {
67
+ return `projects/${secretVersion.secret.projectId}/secrets/${secretVersion.secret.name}/versions/${secretVersion.versionId}`;
68
+ }
69
+ exports.toSecretVersionResourceName = toSecretVersionResourceName;
50
70
  async function createSecret(projectId, name, labels) {
51
71
  const createRes = await api.request("POST", `/v1beta1/projects/${projectId}/secrets?secretId=${name}`, {
52
72
  auth: true,
@@ -107,5 +127,6 @@ async function grantServiceAgentRole(secret, serviceAccountEmail, role) {
107
127
  },
108
128
  },
109
129
  });
130
+ utils_1.logLabeledSuccess("SecretManager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmail}`);
110
131
  }
111
132
  exports.grantServiceAgentRole = grantServiceAgentRole;
@@ -1,26 +1,29 @@
1
1
  "use strict";
2
- var path = require("path");
3
- var api = require("../api");
4
- const { logger } = require("../logger");
5
- var { FirebaseError } = require("../error");
6
- function _getDefaultBucket(projectId) {
7
- return api
8
- .request("GET", "/v1/apps/" + projectId, {
9
- auth: true,
10
- origin: api.appengineOrigin,
11
- })
12
- .then(function (resp) {
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getServiceAccount = exports.getBucket = exports.deleteObject = exports.uploadObject = exports.upload = exports.getDefaultBucket = void 0;
4
+ const path = require("path");
5
+ const api = require("../api");
6
+ const logger_1 = require("../logger");
7
+ const error_1 = require("../error");
8
+ async function getDefaultBucket(projectId) {
9
+ try {
10
+ const resp = await api.request("GET", "/v1/apps/" + projectId, {
11
+ auth: true,
12
+ origin: api.appengineOrigin,
13
+ });
13
14
  if (resp.body.defaultBucket === "undefined") {
14
- logger.debug("Default storage bucket is undefined.");
15
- return Promise.reject(new FirebaseError("Your project is being set up. Please wait a minute before deploying again."));
15
+ logger_1.logger.debug("Default storage bucket is undefined.");
16
+ throw new error_1.FirebaseError("Your project is being set up. Please wait a minute before deploying again.");
16
17
  }
17
- return Promise.resolve(resp.body.defaultBucket);
18
- }, function (err) {
19
- logger.info("\n\nThere was an issue deploying your functions. Verify that your project has a Google App Engine instance setup at https://console.cloud.google.com/appengine and try again. If this issue persists, please contact support.");
20
- return Promise.reject(err);
21
- });
18
+ return resp.body.defaultBucket;
19
+ }
20
+ catch (err) {
21
+ logger_1.logger.info("\n\nThere was an issue deploying your functions. Verify that your project has a Google App Engine instance setup at https://console.cloud.google.com/appengine and try again. If this issue persists, please contact support.");
22
+ throw err;
23
+ }
22
24
  }
23
- async function _uploadSource(source, uploadUrl, extraHeaders) {
25
+ exports.getDefaultBucket = getDefaultBucket;
26
+ async function upload(source, uploadUrl, extraHeaders) {
24
27
  const url = new URL(uploadUrl);
25
28
  const result = await api.request("PUT", url.pathname + url.search, {
26
29
  data: source.stream,
@@ -33,9 +36,10 @@ async function _uploadSource(source, uploadUrl, extraHeaders) {
33
36
  generation: result.response.headers["x-goog-generation"],
34
37
  };
35
38
  }
36
- async function _uploadObject(source, bucketName) {
39
+ exports.upload = upload;
40
+ async function uploadObject(source, bucketName) {
37
41
  if (path.extname(source.file) !== ".zip") {
38
- throw new FirebaseError(`Expected a file name ending in .zip, got ${source.file}`);
42
+ throw new error_1.FirebaseError(`Expected a file name ending in .zip, got ${source.file}`);
39
43
  }
40
44
  const location = `/${bucketName}/${path.basename(source.file)}`;
41
45
  const result = await api.request("PUT", location, {
@@ -55,13 +59,15 @@ async function _uploadObject(source, bucketName) {
55
59
  generation: result.response.headers["x-goog-generation"],
56
60
  };
57
61
  }
58
- function _deleteObject(location) {
62
+ exports.uploadObject = uploadObject;
63
+ function deleteObject(location) {
59
64
  return api.request("DELETE", location, {
60
65
  auth: true,
61
66
  origin: api.storageOrigin,
62
67
  });
63
68
  }
64
- async function _getBucket(bucketName) {
69
+ exports.deleteObject = deleteObject;
70
+ async function getBucket(bucketName) {
65
71
  try {
66
72
  const result = await api.request("GET", `/storage/v1/b/${bucketName}`, {
67
73
  auth: true,
@@ -70,16 +76,26 @@ async function _getBucket(bucketName) {
70
76
  return result.body;
71
77
  }
72
78
  catch (err) {
73
- logger.debug(err);
74
- throw new FirebaseError("Failed to obtain the storage bucket", {
79
+ logger_1.logger.debug(err);
80
+ throw new error_1.FirebaseError("Failed to obtain the storage bucket", {
81
+ original: err,
82
+ });
83
+ }
84
+ }
85
+ exports.getBucket = getBucket;
86
+ async function getServiceAccount(projectId) {
87
+ try {
88
+ const response = await api.request("GET", `/storage/v1/projects/${projectId}/serviceAccount`, {
89
+ auth: true,
90
+ origin: api.storageOrigin,
91
+ });
92
+ return response.body;
93
+ }
94
+ catch (err) {
95
+ logger_1.logger.debug(err);
96
+ throw new error_1.FirebaseError("Failed to obtain the Cloud Storage service agent", {
75
97
  original: err,
76
98
  });
77
99
  }
78
100
  }
79
- module.exports = {
80
- getDefaultBucket: _getDefaultBucket,
81
- deleteObject: _deleteObject,
82
- upload: _uploadSource,
83
- uploadObject: _uploadObject,
84
- getBucket: _getBucket,
85
- };
101
+ exports.getServiceAccount = getServiceAccount;
@@ -72,7 +72,7 @@ async function doSetup(setup, config, options) {
72
72
  logger_1.logger.info(`but for now we'll just set up a default project.`);
73
73
  logger_1.logger.info();
74
74
  const projectFromRcFile = _.get(setup.rcfile, "projects.default");
75
- if (projectFromRcFile) {
75
+ if (projectFromRcFile && !options.project) {
76
76
  utils.logBullet(`.firebaserc already has a default project, using ${projectFromRcFile}.`);
77
77
  const rcProject = await projects_1.getFirebaseProject(projectFromRcFile);
78
78
  setup.projectId = rcProject.projectId;
@@ -81,6 +81,7 @@ async function doSetup(setup, config, options) {
81
81
  }
82
82
  let projectMetaData;
83
83
  if (options.project) {
84
+ logger_1.logger.debug(`Using project from CLI flag: ${options.project}`);
84
85
  projectMetaData = await projects_1.getFirebaseProject(options.project);
85
86
  }
86
87
  else {
package/lib/previews.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.previews = void 0;
4
4
  const lodash_1 = require("lodash");
5
5
  const configstore_1 = require("./configstore");
6
- exports.previews = Object.assign({ rtdbrules: false, ext: false, extdev: false, rtdbmanagement: false, functionsv2: false, golang: false, deletegcfartifacts: false, dotenv: false, crashlyticsSymbolsUpload: false }, configstore_1.configstore.get("previews"));
6
+ exports.previews = Object.assign({ rtdbrules: false, ext: false, extdev: false, rtdbmanagement: false, functionsv2: false, golang: false, deletegcfartifacts: false, dotenv: false, artifactregistry: false }, configstore_1.configstore.get("previews"));
7
7
  if (process.env.FIREBASE_CLI_PREVIEWS) {
8
8
  process.env.FIREBASE_CLI_PREVIEWS.split(",").forEach((feature) => {
9
9
  if (lodash_1.has(exports.previews, feature)) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.needProjectNumber = exports.needProjectId = exports.getProjectId = void 0;
3
+ exports.getAliases = exports.needProjectNumber = exports.needProjectId = exports.getProjectId = void 0;
4
4
  const projects_1 = require("./management/projects");
5
5
  const clc = require("cli-color");
6
6
  const marked = require("marked");
@@ -48,3 +48,12 @@ async function needProjectNumber(options) {
48
48
  return options.projectNumber;
49
49
  }
50
50
  exports.needProjectNumber = needProjectNumber;
51
+ function getAliases(options, projectId) {
52
+ if (options.rc.hasProjects) {
53
+ return Object.entries(options.rc.projects)
54
+ .filter((entry) => entry[1] === projectId)
55
+ .map((entry) => entry[0]);
56
+ }
57
+ return [];
58
+ }
59
+ exports.getAliases = getAliases;
package/lib/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.assertDefined = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.reject = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
3
+ exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.assertDefined = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
4
4
  const _ = require("lodash");
5
5
  const url = require("url");
6
6
  const clc = require("cli-color");
@@ -108,6 +108,35 @@ function reject(message, options) {
108
108
  return Promise.reject(new error_1.FirebaseError(message, options));
109
109
  }
110
110
  exports.reject = reject;
111
+ function allSettled(promises) {
112
+ if (!promises.length) {
113
+ return Promise.resolve([]);
114
+ }
115
+ return new Promise((resolve) => {
116
+ let remaining = promises.length;
117
+ const results = [];
118
+ for (let i = 0; i < promises.length; i++) {
119
+ void Promise.resolve(promises[i])
120
+ .then((result) => {
121
+ results[i] = {
122
+ status: "fulfilled",
123
+ value: result,
124
+ };
125
+ }, (err) => {
126
+ results[i] = {
127
+ status: "rejected",
128
+ reason: err,
129
+ };
130
+ })
131
+ .then(() => {
132
+ if (!--remaining) {
133
+ resolve(results);
134
+ }
135
+ });
136
+ }
137
+ });
138
+ }
139
+ exports.allSettled = allSettled;
111
140
  function explainStdin() {
112
141
  if (IS_WINDOWS) {
113
142
  throw new error_1.FirebaseError("STDIN input is not available on Windows.", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "9.20.0",
3
+ "version": "9.23.1",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -26,6 +26,7 @@
26
26
  "test:client-integration": "./scripts/client-integration-tests/run.sh",
27
27
  "test:compile": "tsc --project tsconfig.compile.json",
28
28
  "test:emulator": "./scripts/emulator-tests/run.sh",
29
+ "test:extensions-deploy": "./scripts/extensions-deploy-tests/run.sh",
29
30
  "test:extensions-emulator": "./scripts/extensions-emulator-tests/run.sh",
30
31
  "test:hosting": "./scripts/hosting-tests/run.sh",
31
32
  "test:triggers-end-to-end": "./scripts/triggers-end-to-end-tests/run.sh",
@@ -93,7 +94,7 @@
93
94
  "chokidar": "^3.0.2",
94
95
  "cjson": "^0.3.1",
95
96
  "cli-color": "^1.2.0",
96
- "cli-table": "^0.3.1",
97
+ "cli-table": "^0.3.8",
97
98
  "commander": "^4.0.1",
98
99
  "configstore": "^5.0.1",
99
100
  "cors": "^2.8.5",
@@ -137,7 +138,7 @@
137
138
  "universal-analytics": "^0.4.16",
138
139
  "unzipper": "^0.10.10",
139
140
  "update-notifier": "^5.1.0",
140
- "uuid": "^3.0.0",
141
+ "uuid": "^8.3.2",
141
142
  "winston": "^3.0.0",
142
143
  "winston-transport": "^4.4.0",
143
144
  "ws": "^7.2.3"
@@ -181,7 +182,7 @@
181
182
  "@types/tmp": "^0.1.0",
182
183
  "@types/triple-beam": "^1.3.0",
183
184
  "@types/unzipper": "^0.10.0",
184
- "@types/uuid": "^3.4.4",
185
+ "@types/uuid": "^8.3.1",
185
186
  "@types/winston": "^2.4.4",
186
187
  "@types/ws": "^7.2.3",
187
188
  "@typescript-eslint/eslint-plugin": "^4.12.0",