firebase-tools 9.19.0 → 9.23.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 (101) hide show
  1. package/CHANGELOG.md +1 -3
  2. package/lib/api.js +3 -0
  3. package/lib/apiv2.js +7 -4
  4. package/lib/commands/crashlytics-symbols-upload.js +146 -0
  5. package/lib/commands/deploy.js +9 -1
  6. package/lib/commands/ext-configure.js +3 -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 +24 -3
  12. package/lib/commands/ext-uninstall.js +6 -0
  13. package/lib/commands/ext-update.js +10 -3
  14. package/lib/commands/functions-config-export.js +115 -0
  15. package/lib/commands/functions-delete.js +47 -25
  16. package/lib/commands/functions-list.js +12 -12
  17. package/lib/commands/index.js +9 -0
  18. package/lib/commands/init.js +3 -0
  19. package/lib/config.js +3 -2
  20. package/lib/deploy/extensions/args.js +2 -0
  21. package/lib/deploy/extensions/deploy.js +49 -0
  22. package/lib/deploy/extensions/deploymentSummary.js +52 -0
  23. package/lib/deploy/extensions/errors.js +31 -0
  24. package/lib/deploy/extensions/index.js +8 -0
  25. package/lib/deploy/extensions/params.js +39 -0
  26. package/lib/deploy/extensions/planner.js +94 -0
  27. package/lib/deploy/extensions/prepare.js +111 -0
  28. package/lib/deploy/extensions/release.js +43 -0
  29. package/lib/deploy/extensions/secrets.js +150 -0
  30. package/lib/deploy/extensions/tasks.js +98 -0
  31. package/lib/deploy/extensions/validate.js +17 -0
  32. package/lib/deploy/functions/backend.js +93 -115
  33. package/lib/deploy/functions/checkIam.js +8 -8
  34. package/lib/deploy/functions/containerCleaner.js +71 -14
  35. package/lib/deploy/functions/deploy.js +4 -10
  36. package/lib/deploy/functions/functionsDeployHelper.js +3 -68
  37. package/lib/deploy/functions/prepare.js +63 -27
  38. package/lib/deploy/functions/pricing.js +17 -17
  39. package/lib/deploy/functions/prompts.js +22 -21
  40. package/lib/deploy/functions/release/executor.js +39 -0
  41. package/lib/deploy/functions/release/fabricator.js +422 -0
  42. package/lib/deploy/functions/release/index.js +73 -0
  43. package/lib/deploy/functions/release/planner.js +162 -0
  44. package/lib/deploy/functions/release/reporter.js +165 -0
  45. package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
  46. package/lib/deploy/functions/release/timer.js +14 -0
  47. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
  48. package/lib/deploy/functions/runtimes/node/parseTriggers.js +41 -45
  49. package/lib/deploy/functions/triggerRegionHelper.js +40 -0
  50. package/lib/deploy/functions/validate.js +1 -24
  51. package/lib/deploy/index.js +10 -1
  52. package/lib/downloadUtils.js +37 -0
  53. package/lib/emulator/auth/apiSpec.js +549 -6
  54. package/lib/emulator/auth/handlers.js +4 -3
  55. package/lib/emulator/auth/operations.js +154 -14
  56. package/lib/emulator/auth/server.js +26 -15
  57. package/lib/emulator/auth/state.js +151 -13
  58. package/lib/emulator/download.js +2 -31
  59. package/lib/emulator/downloadableEmulators.js +7 -7
  60. package/lib/emulator/functionsEmulator.js +18 -4
  61. package/lib/emulator/functionsEmulatorRuntime.js +29 -7
  62. package/lib/emulator/storage/cloudFunctions.js +37 -7
  63. package/lib/extensions/askUserForConsent.js +14 -1
  64. package/lib/extensions/askUserForParam.js +81 -4
  65. package/lib/extensions/checkProjectBilling.js +7 -7
  66. package/lib/extensions/export.js +107 -0
  67. package/lib/extensions/extensionsApi.js +104 -21
  68. package/lib/extensions/extensionsHelper.js +6 -2
  69. package/lib/extensions/listExtensions.js +16 -11
  70. package/lib/extensions/paramHelper.js +9 -6
  71. package/lib/extensions/provisioningHelper.js +16 -3
  72. package/lib/extensions/refs.js +9 -1
  73. package/lib/extensions/secretsUtils.js +59 -0
  74. package/lib/extensions/updateHelper.js +12 -2
  75. package/lib/extensions/versionHelper.js +14 -0
  76. package/lib/extensions/warnings.js +33 -1
  77. package/lib/functional.js +8 -1
  78. package/lib/functions/env.js +10 -4
  79. package/lib/functions/runtimeConfigExport.js +137 -0
  80. package/lib/gcp/artifactregistry.js +16 -0
  81. package/lib/gcp/cloudfunctions.js +20 -74
  82. package/lib/gcp/cloudfunctionsv2.js +12 -90
  83. package/lib/gcp/cloudscheduler.js +22 -16
  84. package/lib/gcp/cloudtasks.js +143 -0
  85. package/lib/gcp/docker.js +7 -1
  86. package/lib/gcp/proto.js +2 -2
  87. package/lib/gcp/pubsub.js +1 -9
  88. package/lib/gcp/secretManager.js +132 -0
  89. package/lib/gcp/storage.js +16 -0
  90. package/lib/projectUtils.js +10 -1
  91. package/lib/requireInteractive.js +12 -0
  92. package/lib/utils.js +30 -1
  93. package/package.json +5 -4
  94. package/schema/firebase-config.json +9 -0
  95. package/lib/deploy/functions/deploymentPlanner.js +0 -113
  96. package/lib/deploy/functions/deploymentTimer.js +0 -23
  97. package/lib/deploy/functions/errorHandler.js +0 -75
  98. package/lib/deploy/functions/release.js +0 -116
  99. package/lib/deploy/functions/tasks.js +0 -324
  100. package/lib/functions/listFunctions.js +0 -10
  101. package/lib/functionsDelete.js +0 -60
@@ -1,38 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compareFunctions = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.topicName = exports.scheduleName = exports.sameFunctionName = exports.functionName = exports.isEmptyBackend = exports.empty = exports.isScheduleTriggered = exports.isEventTriggered = exports.isHttpsTriggered = exports.SCHEDULED_FUNCTION_LABEL = exports.memoryOptionDisplayName = exports.triggerTag = exports.isEventTrigger = void 0;
3
+ exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.of = exports.empty = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isHttpsTriggered = exports.SCHEDULED_FUNCTION_LABEL = exports.memoryOptionDisplayName = exports.endpointTriggerType = void 0;
4
4
  const gcf = require("../../gcp/cloudfunctions");
5
5
  const gcfV2 = require("../../gcp/cloudfunctionsv2");
6
6
  const utils = require("../../utils");
7
7
  const error_1 = require("../../error");
8
8
  const previews_1 = require("../../previews");
9
- function isEventTrigger(trigger) {
10
- return "eventType" in trigger;
11
- }
12
- exports.isEventTrigger = isEventTrigger;
13
- function triggerTag(fn) {
14
- var _a, _b;
15
- if ((_a = fn.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) {
16
- if (fn.platform === "gcfv1") {
17
- return "v1.scheduled";
18
- }
19
- return "v2.scheduled";
9
+ function endpointTriggerType(endpoint) {
10
+ if (isScheduleTriggered(endpoint)) {
11
+ return "scheduled";
20
12
  }
21
- if ((_b = fn.labels) === null || _b === void 0 ? void 0 : _b["deployment-callable"]) {
22
- if (fn.platform === "gcfv1") {
23
- return "v1.callable";
24
- }
25
- return "v2.callable";
13
+ else if (isHttpsTriggered(endpoint)) {
14
+ return "https";
26
15
  }
27
- if (!isEventTrigger(fn.trigger)) {
28
- if (fn.platform === "gcfv1") {
29
- return "v1.https";
30
- }
31
- return "v2.https";
16
+ else if (isEventTriggered(endpoint)) {
17
+ return endpoint.eventTrigger.eventType;
18
+ }
19
+ else if (isTaskQueueTriggered(endpoint)) {
20
+ return "taskQueue";
21
+ }
22
+ else {
23
+ throw new Error("Unexpected trigger type for endpoint " + JSON.stringify(endpoint));
32
24
  }
33
- return fn.trigger.eventType;
34
25
  }
35
- exports.triggerTag = triggerTag;
26
+ exports.endpointTriggerType = endpointTriggerType;
36
27
  function memoryOptionDisplayName(option) {
37
28
  return {
38
29
  128: "128MB",
@@ -58,39 +49,38 @@ function isScheduleTriggered(triggered) {
58
49
  return {}.hasOwnProperty.call(triggered, "scheduleTrigger");
59
50
  }
60
51
  exports.isScheduleTriggered = isScheduleTriggered;
52
+ function isTaskQueueTriggered(triggered) {
53
+ return {}.hasOwnProperty.call(triggered, "taskQueueTrigger");
54
+ }
55
+ exports.isTaskQueueTriggered = isTaskQueueTriggered;
61
56
  function empty() {
62
57
  return {
63
58
  requiredAPIs: {},
64
- endpoints: [],
65
- cloudFunctions: [],
66
- schedules: [],
67
- topics: [],
59
+ endpoints: {},
68
60
  environmentVariables: {},
69
61
  };
70
62
  }
71
63
  exports.empty = empty;
64
+ function of(...endpoints) {
65
+ const bkend = Object.assign({}, empty());
66
+ for (const endpoint of endpoints) {
67
+ bkend.endpoints[endpoint.region] = bkend.endpoints[endpoint.region] || {};
68
+ if (bkend.endpoints[endpoint.region][endpoint.id]) {
69
+ throw new Error("Trying to create a backend with the same endpiont twice");
70
+ }
71
+ bkend.endpoints[endpoint.region][endpoint.id] = endpoint;
72
+ }
73
+ return bkend;
74
+ }
75
+ exports.of = of;
72
76
  function isEmptyBackend(backend) {
73
- return (Object.keys(backend.requiredAPIs).length == 0 &&
74
- backend.cloudFunctions.length === 0 &&
75
- backend.schedules.length === 0 &&
76
- backend.topics.length === 0);
77
+ return (Object.keys(backend.requiredAPIs).length == 0 && Object.keys(backend.endpoints).length === 0);
77
78
  }
78
79
  exports.isEmptyBackend = isEmptyBackend;
79
80
  function functionName(cloudFunction) {
80
81
  return `projects/${cloudFunction.project}/locations/${cloudFunction.region}/functions/${cloudFunction.id}`;
81
82
  }
82
83
  exports.functionName = functionName;
83
- exports.sameFunctionName = (func) => (test) => {
84
- return func.id === test.id && func.region === test.region && func.project == test.project;
85
- };
86
- function scheduleName(schedule, appEngineLocation) {
87
- return `projects/${schedule.project}/locations/${appEngineLocation}/jobs/${schedule.id}`;
88
- }
89
- exports.scheduleName = scheduleName;
90
- function topicName(topic) {
91
- return `projects/${topic.project}/topics/${topic.id}`;
92
- }
93
- exports.topicName = topicName;
94
84
  function scheduleIdForFunction(cloudFunction) {
95
85
  return `firebase-schedule-${cloudFunction.id}-${cloudFunction.region}`;
96
86
  }
@@ -104,7 +94,7 @@ async function existingBackend(context, forceRefresh) {
104
94
  }
105
95
  exports.existingBackend = existingBackend;
106
96
  async function loadExistingBackend(ctx) {
107
- var _a, _b, _c;
97
+ var _a;
108
98
  ctx.loadedExistingBackend = true;
109
99
  ctx.existingBackend = Object.assign({}, empty());
110
100
  ctx.unreachableRegions = {
@@ -113,79 +103,30 @@ async function loadExistingBackend(ctx) {
113
103
  };
114
104
  const gcfV1Results = await gcf.listAllFunctions(ctx.projectId);
115
105
  for (const apiFunction of gcfV1Results.functions) {
116
- const specFunction = gcf.specFromFunction(apiFunction);
117
- ctx.existingBackend.cloudFunctions.push(specFunction);
118
- const isScheduled = ((_a = apiFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true";
119
- if (isScheduled) {
120
- const id = scheduleIdForFunction(specFunction);
121
- ctx.existingBackend.schedules.push({
122
- id,
123
- project: specFunction.project,
124
- transport: "pubsub",
125
- targetService: {
126
- id: specFunction.id,
127
- region: specFunction.region,
128
- project: specFunction.project,
129
- },
130
- });
131
- ctx.existingBackend.topics.push({
132
- id,
133
- project: specFunction.project,
134
- labels: exports.SCHEDULED_FUNCTION_LABEL,
135
- targetService: {
136
- id: specFunction.id,
137
- region: specFunction.region,
138
- project: specFunction.project,
139
- },
140
- });
141
- }
106
+ const endpoint = gcf.endpointFromFunction(apiFunction);
107
+ ctx.existingBackend.endpoints[endpoint.region] =
108
+ ctx.existingBackend.endpoints[endpoint.region] || {};
109
+ ctx.existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint;
142
110
  }
143
111
  ctx.unreachableRegions.gcfV1 = gcfV1Results.unreachable;
144
112
  if (!previews_1.previews.functionsv2) {
145
113
  return;
146
114
  }
147
- const gcfV2Results = await gcfV2.listAllFunctions(ctx.projectId);
148
- for (const apiFunction of gcfV2Results.functions) {
149
- const specFunction = gcfV2.specFromFunction(apiFunction);
150
- ctx.existingBackend.cloudFunctions.push(specFunction);
151
- const pubsubScheduled = ((_b = apiFunction.labels) === null || _b === void 0 ? void 0 : _b["deployment-scheduled"]) === "true";
152
- const httpsScheduled = ((_c = apiFunction.labels) === null || _c === void 0 ? void 0 : _c["deployment-scheduled"]) === "https";
153
- if (pubsubScheduled) {
154
- const id = scheduleIdForFunction(specFunction);
155
- ctx.existingBackend.schedules.push({
156
- id,
157
- project: specFunction.project,
158
- transport: "pubsub",
159
- targetService: {
160
- id: specFunction.id,
161
- region: specFunction.region,
162
- project: specFunction.project,
163
- },
164
- });
165
- ctx.existingBackend.topics.push({
166
- id,
167
- project: specFunction.project,
168
- labels: exports.SCHEDULED_FUNCTION_LABEL,
169
- targetService: {
170
- id: specFunction.id,
171
- region: specFunction.region,
172
- project: specFunction.project,
173
- },
174
- });
175
- }
176
- if (httpsScheduled) {
177
- const id = scheduleIdForFunction(specFunction);
178
- ctx.existingBackend.schedules.push({
179
- id,
180
- project: specFunction.project,
181
- transport: "https",
182
- targetService: {
183
- id: specFunction.id,
184
- region: specFunction.region,
185
- project: specFunction.project,
186
- },
187
- });
115
+ let gcfV2Results;
116
+ try {
117
+ gcfV2Results = await gcfV2.listAllFunctions(ctx.projectId);
118
+ }
119
+ catch (err) {
120
+ if (err.status === 404 && ((_a = err.message) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes("method not found"))) {
121
+ return;
188
122
  }
123
+ throw err;
124
+ }
125
+ for (const apiFunction of gcfV2Results.functions) {
126
+ const endpoint = gcfV2.endpointFromFunction(apiFunction);
127
+ ctx.existingBackend.endpoints[endpoint.region] =
128
+ ctx.existingBackend.endpoints[endpoint.region] || {};
129
+ ctx.existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint;
189
130
  }
190
131
  ctx.unreachableRegions.gcfV2 = gcfV2Results.unreachable;
191
132
  }
@@ -196,12 +137,12 @@ async function checkAvailability(context, want) {
196
137
  }
197
138
  const gcfV1Regions = new Set();
198
139
  const gcfV2Regions = new Set();
199
- for (const fn of want.cloudFunctions) {
200
- if (fn.platform == "gcfv1") {
201
- gcfV1Regions.add(fn.region);
140
+ for (const ep of allEndpoints(want)) {
141
+ if (ep.platform == "gcfv1") {
142
+ gcfV1Regions.add(ep.region);
202
143
  }
203
144
  else {
204
- gcfV2Regions.add(fn.region);
145
+ gcfV2Regions.add(ep.region);
205
146
  }
206
147
  }
207
148
  const neededUnreachableV1 = ctx.unreachableRegions.gcfV1.filter((region) => gcfV1Regions.has(region));
@@ -228,6 +169,43 @@ async function checkAvailability(context, want) {
228
169
  }
229
170
  }
230
171
  exports.checkAvailability = checkAvailability;
172
+ function allEndpoints(backend) {
173
+ return Object.values(backend.endpoints).reduce((accum, perRegion) => {
174
+ return [...accum, ...Object.values(perRegion)];
175
+ }, []);
176
+ }
177
+ exports.allEndpoints = allEndpoints;
178
+ function someEndpoint(backend, predicate) {
179
+ for (const endpoints of Object.values(backend.endpoints)) {
180
+ if (Object.values(endpoints).some(predicate)) {
181
+ return true;
182
+ }
183
+ }
184
+ return false;
185
+ }
186
+ exports.someEndpoint = someEndpoint;
187
+ function matchingBackend(backend, predicate) {
188
+ const filtered = Object.assign({}, empty());
189
+ for (const endpoint of allEndpoints(backend)) {
190
+ if (!predicate(endpoint)) {
191
+ continue;
192
+ }
193
+ filtered.endpoints[endpoint.region] = filtered.endpoints[endpoint.region] || {};
194
+ filtered.endpoints[endpoint.region][endpoint.id] = endpoint;
195
+ }
196
+ return filtered;
197
+ }
198
+ exports.matchingBackend = matchingBackend;
199
+ function regionalEndpoints(backend, region) {
200
+ return backend.endpoints[region] ? Object.values(backend.endpoints[region]) : [];
201
+ }
202
+ exports.regionalEndpoints = regionalEndpoints;
203
+ exports.hasEndpoint = (backend) => (endpoint) => {
204
+ return !!backend.endpoints[endpoint.region] && !!backend.endpoints[endpoint.region][endpoint.id];
205
+ };
206
+ exports.missingEndpoint = (backend) => (endpoint) => {
207
+ return !exports.hasEndpoint(backend)(endpoint);
208
+ };
231
209
  function compareFunctions(left, right) {
232
210
  if (left.platform != right.platform) {
233
211
  return right.platform < left.platform ? -1 : 1;
@@ -28,17 +28,17 @@ async function checkServiceAccountIam(projectId) {
28
28
  }
29
29
  exports.checkServiceAccountIam = checkServiceAccountIam;
30
30
  async function checkHttpIam(context, options, payload) {
31
- const functions = payload.functions.backend.cloudFunctions;
32
31
  const filterGroups = context.filters || functionsDeployHelper_1.getFilterGroups(options);
33
- const httpFunctions = functions
34
- .filter((f) => !backend.isEventTrigger(f.trigger))
32
+ const httpEndpoints = backend
33
+ .allEndpoints(payload.functions.backend)
34
+ .filter(backend.isHttpsTriggered)
35
35
  .filter((f) => functionsDeployHelper_1.functionMatchesAnyGroup(f, filterGroups));
36
- const existingFunctions = (await backend.existingBackend(context)).cloudFunctions;
37
- const newHttpFunctions = httpFunctions.filter((func) => !existingFunctions.find(backend.sameFunctionName(func)));
38
- if (newHttpFunctions.length === 0) {
36
+ const existing = await backend.existingBackend(context);
37
+ const newHttpsEndpoints = httpEndpoints.filter(backend.missingEndpoint(existing));
38
+ if (newHttpsEndpoints.length === 0) {
39
39
  return;
40
40
  }
41
- logger_1.logger.debug("[functions] found", newHttpFunctions.length, "new HTTP functions, testing setIamPolicy permission...");
41
+ logger_1.logger.debug("[functions] found", newHttpsEndpoints.length, "new HTTP functions, testing setIamPolicy permission...");
42
42
  let passed = true;
43
43
  try {
44
44
  const iamResult = await iam_1.testIamPermissions(context.projectId, [PERMISSION]);
@@ -51,7 +51,7 @@ async function checkHttpIam(context, options, payload) {
51
51
  if (!passed) {
52
52
  track("Error (User)", "deploy:functions:http_create_missing_iam");
53
53
  throw new error_1.FirebaseError(`Missing required permission on project ${cli_color_1.bold(context.projectId)} to deploy new HTTPS functions. The permission ${cli_color_1.bold(PERMISSION)} is required to deploy the following functions:\n\n- ` +
54
- newHttpFunctions.map((func) => func.id).join("\n- ") +
54
+ newHttpsEndpoints.map((func) => func.id).join("\n- ") +
55
55
  `\n\nTo address this error, please ask a project Owner to assign your account the "Cloud Functions Admin" role at the following URL:\n\nhttps://console.cloud.google.com/iam-admin/iam?project=${context.projectId}`);
56
56
  }
57
57
  logger_1.logger.debug("[functions] found setIamPolicy permission, proceeding with deploy");
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DockerHelper = exports.deleteGcfArtifacts = exports.listGcfPaths = exports.ContainerRegistryCleaner = exports.cleanupBuildImages = exports.SUBDOMAIN_MAPPING = void 0;
3
+ exports.DockerHelper = exports.deleteGcfArtifacts = exports.listGcfPaths = exports.ContainerRegistryCleaner = exports.ArtifactRegistryCleaner = exports.cleanupBuildImages = exports.SUBDOMAIN_MAPPING = void 0;
4
4
  const clc = require("cli-color");
5
+ const error_1 = require("../../error");
5
6
  const api_1 = require("../../api");
6
7
  const logger_1 = require("../../logger");
7
- const docker = require("../../gcp/docker");
8
+ const artifactregistry = require("../../gcp/artifactregistry");
8
9
  const backend = require("./backend");
10
+ const docker = require("../../gcp/docker");
9
11
  const utils = require("../../utils");
10
- const error_1 = require("../../error");
12
+ const poller = require("../../operation-poller");
11
13
  exports.SUBDOMAIN_MAPPING = {
12
14
  "us-west2": "us",
13
15
  "us-west3": "us",
@@ -47,7 +49,7 @@ async function retry(func) {
47
49
  return await Promise.race([func(), timeout]);
48
50
  }
49
51
  catch (error) {
50
- logger_1.logger.debug("Failed docker command with error", error);
52
+ logger_1.logger.debug("Failed docker command with error ", error);
51
53
  retry += 1;
52
54
  if (retry >= MAX_RETRIES) {
53
55
  throw new error_1.FirebaseError("Failed to clean up artifacts", { original: error });
@@ -56,11 +58,31 @@ async function retry(func) {
56
58
  }
57
59
  }
58
60
  }
59
- async function cleanupBuildImages(functions) {
61
+ async function cleanupBuildImages(haveFunctions, deletedFunctions, cleaners = {}) {
60
62
  utils.logBullet(clc.bold.cyan("functions: ") + "cleaning up build files...");
61
- const gcrCleaner = new ContainerRegistryCleaner();
62
63
  const failedDomains = new Set();
63
- await Promise.all(functions.map((func) => (async () => {
64
+ const cleanup = [];
65
+ const arCleaner = cleaners.ar || new ArtifactRegistryCleaner();
66
+ cleanup.push(...haveFunctions.map(async (func) => {
67
+ try {
68
+ await arCleaner.cleanupFunction(func);
69
+ }
70
+ catch (err) {
71
+ const path = `${func.project}/${func.region}/gcf-artifacts`;
72
+ failedDomains.add(`https://console.cloud.google.com/artifacts/docker/${path}`);
73
+ }
74
+ }));
75
+ cleanup.push(...deletedFunctions.map(async (func) => {
76
+ try {
77
+ await Promise.all([arCleaner.cleanupFunction(func), arCleaner.cleanupFunctionCache(func)]);
78
+ }
79
+ catch (err) {
80
+ const path = `${func.project}/${func.region}/gcf-artifacts`;
81
+ failedDomains.add(`https://console.cloud.google.com/artifacts/docker/${path}`);
82
+ }
83
+ }));
84
+ const gcrCleaner = cleaners.gcr || new ContainerRegistryCleaner();
85
+ cleanup.push(...[...haveFunctions, ...deletedFunctions].map(async (func) => {
64
86
  try {
65
87
  await gcrCleaner.cleanupFunction(func);
66
88
  }
@@ -68,7 +90,8 @@ async function cleanupBuildImages(functions) {
68
90
  const path = `${func.project}/${exports.SUBDOMAIN_MAPPING[func.region]}/gcf`;
69
91
  failedDomains.add(`https://console.cloud.google.com/gcr/images/${path}`);
70
92
  }
71
- })()));
93
+ }));
94
+ await Promise.all(cleanup);
72
95
  if (failedDomains.size) {
73
96
  let message = "Unhandled error cleaning up build images. This could result in a small monthly bill if not corrected. ";
74
97
  message +=
@@ -83,6 +106,40 @@ async function cleanupBuildImages(functions) {
83
106
  }
84
107
  }
85
108
  exports.cleanupBuildImages = cleanupBuildImages;
109
+ class ArtifactRegistryCleaner {
110
+ static packagePath(func) {
111
+ return `projects/${func.project}/locations/${func.region}/repositories/gcf-artifacts/packages/${func.id}`;
112
+ }
113
+ async cleanupFunction(func) {
114
+ let op;
115
+ try {
116
+ op = await artifactregistry.deletePackage(ArtifactRegistryCleaner.packagePath(func));
117
+ }
118
+ catch (err) {
119
+ if (err.status === 404) {
120
+ return;
121
+ }
122
+ throw err;
123
+ }
124
+ if (op.done) {
125
+ return;
126
+ }
127
+ await poller.pollOperation(Object.assign(Object.assign({}, ArtifactRegistryCleaner.POLLER_OPTIONS), { pollerName: `cleanup-${func.region}-${func.id}`, operationResourceName: op.name }));
128
+ }
129
+ async cleanupFunctionCache(func) {
130
+ const op = await artifactregistry.deletePackage(`${ArtifactRegistryCleaner.packagePath(func)}%2Fcache`);
131
+ if (op.done) {
132
+ return;
133
+ }
134
+ await poller.pollOperation(Object.assign(Object.assign({}, ArtifactRegistryCleaner.POLLER_OPTIONS), { pollerName: `cleanup-cache-${func.region}-${func.id}`, operationResourceName: op.name }));
135
+ }
136
+ }
137
+ exports.ArtifactRegistryCleaner = ArtifactRegistryCleaner;
138
+ ArtifactRegistryCleaner.POLLER_OPTIONS = {
139
+ apiOrigin: api_1.artifactRegistryDomain,
140
+ apiVersion: artifactregistry.API_VERSION,
141
+ masterTimeout: 5 * 60 * 1000,
142
+ };
86
143
  class ContainerRegistryCleaner {
87
144
  constructor() {
88
145
  this.helpers = {};
@@ -217,7 +274,7 @@ class DockerHelper {
217
274
  async rm(path) {
218
275
  let toThrowLater = undefined;
219
276
  const stat = await this.ls(path);
220
- const recursive = stat.children.map((child) => (async () => {
277
+ const recursive = stat.children.map(async (child) => {
221
278
  try {
222
279
  await this.rm(`${path}/${child}`);
223
280
  stat.children.splice(stat.children.indexOf(child), 1);
@@ -225,8 +282,8 @@ class DockerHelper {
225
282
  catch (err) {
226
283
  toThrowLater = err;
227
284
  }
228
- })());
229
- const deleteTags = stat.tags.map((tag) => (async () => {
285
+ });
286
+ const deleteTags = stat.tags.map(async (tag) => {
230
287
  try {
231
288
  await retry(() => this.client.deleteTag(path, tag));
232
289
  stat.tags.splice(stat.tags.indexOf(tag), 1);
@@ -235,9 +292,9 @@ class DockerHelper {
235
292
  logger_1.logger.debug("Got error trying to remove docker tag:", err);
236
293
  toThrowLater = err;
237
294
  }
238
- })());
295
+ });
239
296
  await Promise.all(deleteTags);
240
- const deleteImages = stat.digests.map((digest) => (async () => {
297
+ const deleteImages = stat.digests.map(async (digest) => {
241
298
  try {
242
299
  await retry(() => this.client.deleteImage(path, digest));
243
300
  stat.digests.splice(stat.digests.indexOf(digest), 1);
@@ -246,7 +303,7 @@ class DockerHelper {
246
303
  logger_1.logger.debug("Got error trying to remove docker image:", err);
247
304
  toThrowLater = err;
248
305
  }
249
- })());
306
+ });
250
307
  await Promise.all(deleteImages);
251
308
  await Promise.all(recursive);
252
309
  if (toThrowLater) {
@@ -11,6 +11,7 @@ const gcs = require("../../gcp/storage");
11
11
  const gcf = require("../../gcp/cloudfunctions");
12
12
  const gcfv2 = require("../../gcp/cloudfunctionsv2");
13
13
  const utils = require("../../utils");
14
+ const backend = require("./backend");
14
15
  const GCP_REGION = api_1.functionsUploadRegion;
15
16
  tmp_1.setGracefulCleanup();
16
17
  async function uploadSourceV1(context) {
@@ -44,18 +45,11 @@ async function deploy(context, options, payload) {
44
45
  try {
45
46
  const want = payload.functions.backend;
46
47
  const uploads = [];
47
- if (want.cloudFunctions.some((fn) => fn.platform === "gcfv1")) {
48
+ if (backend.allEndpoints(want).some((endpoint) => endpoint.platform === "gcfv1")) {
48
49
  uploads.push(uploadSourceV1(context));
49
50
  }
50
- if (want.cloudFunctions.some((fn) => fn.platform === "gcfv2")) {
51
- const functions = payload.functions.backend.cloudFunctions;
52
- const regions = [];
53
- for (const func of functions) {
54
- if (func.platform === "gcfv2" && -1 === regions.indexOf(func.region)) {
55
- regions.push(func.region);
56
- }
57
- }
58
- for (const region of regions) {
51
+ for (const region of Object.keys(want.endpoints)) {
52
+ if (backend.regionalEndpoints(want, region).some((e) => e.platform === "gcfv2")) {
59
53
  uploads.push(uploadSourceV2(context, region));
60
54
  }
61
55
  }
@@ -1,11 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.printTriggerUrls = exports.printSuccess = exports.logAndTrackDeployStats = exports.getFunctionLabel = exports.getFunctionId = exports.getFilterGroups = exports.functionMatchesGroup = exports.functionMatchesAnyGroup = void 0;
4
- const clc = require("cli-color");
5
- const logger_1 = require("../../logger");
6
- const backend = require("./backend");
7
- const track = require("../../track");
8
- const utils = require("../../utils");
3
+ exports.getFunctionLabel = exports.getFilterGroups = exports.functionMatchesGroup = exports.functionMatchesAnyGroup = void 0;
9
4
  function functionMatchesAnyGroup(func, filterGroups) {
10
5
  if (!filterGroups.length) {
11
6
  return true;
@@ -40,67 +35,7 @@ function getFilterGroups(options) {
40
35
  });
41
36
  }
42
37
  exports.getFilterGroups = getFilterGroups;
43
- function getFunctionId(fullName) {
44
- return fullName.split("/")[5];
45
- }
46
- exports.getFunctionId = getFunctionId;
47
- function getRegion(fullName) {
48
- return fullName.split("/")[3];
49
- }
50
- function getFunctionLabel(fnOrName) {
51
- if (typeof fnOrName === "string") {
52
- return getFunctionId(fnOrName) + "(" + getRegion(fnOrName) + ")";
53
- }
54
- else {
55
- return `${fnOrName.id}(${fnOrName.region})`;
56
- }
38
+ function getFunctionLabel(fn) {
39
+ return `${fn.id}(${fn.region})`;
57
40
  }
58
41
  exports.getFunctionLabel = getFunctionLabel;
59
- function logAndTrackDeployStats(queue, errorHandler) {
60
- const stats = queue.stats();
61
- logger_1.logger.debug(`Total Function Deployment time: ${stats.elapsed}`);
62
- logger_1.logger.debug(`${stats.total} Functions Deployed`);
63
- logger_1.logger.debug(`${errorHandler.errors.length} Functions Errored`);
64
- logger_1.logger.debug(`Average Function Deployment time: ${stats.avg}`);
65
- if (stats.total > 0) {
66
- if (errorHandler.errors.length === 0) {
67
- track("functions_deploy_result", "success", stats.total);
68
- }
69
- else if (errorHandler.errors.length < stats.total) {
70
- track("functions_deploy_result", "partial_success", stats.total - errorHandler.errors.length);
71
- track("functions_deploy_result", "partial_failure", errorHandler.errors.length);
72
- track("functions_deploy_result", "partial_error_ratio", errorHandler.errors.length / stats.total);
73
- }
74
- else {
75
- track("functions_deploy_result", "failure", stats.total);
76
- }
77
- }
78
- }
79
- exports.logAndTrackDeployStats = logAndTrackDeployStats;
80
- function printSuccess(func, type) {
81
- utils.logSuccess(clc.bold.green("functions[" + getFunctionLabel(func) + "]: ") +
82
- "Successful " +
83
- type +
84
- " operation. ");
85
- }
86
- exports.printSuccess = printSuccess;
87
- async function printTriggerUrls(context, want) {
88
- const have = await backend.existingBackend(context, true);
89
- const httpsFunctions = have.cloudFunctions.filter((fn) => {
90
- if (backend.isEventTrigger(fn.trigger)) {
91
- return false;
92
- }
93
- return want.cloudFunctions.some(backend.sameFunctionName(fn));
94
- });
95
- if (httpsFunctions.length === 0) {
96
- return;
97
- }
98
- for (const httpsFunc of httpsFunctions) {
99
- if (!httpsFunc.uri) {
100
- logger_1.logger.debug("Missing URI for HTTPS function in printTriggerUrls. This shouldn't happen");
101
- continue;
102
- }
103
- logger_1.logger.info(clc.bold("Function URL"), `(${getFunctionLabel(httpsFunc)}):`, httpsFunc.uri);
104
- }
105
- }
106
- exports.printTriggerUrls = printTriggerUrls;