firebase-tools 9.18.0 → 9.22.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 (114) hide show
  1. package/CHANGELOG.md +3 -6
  2. package/lib/api.js +3 -0
  3. package/lib/apiv2.js +8 -5
  4. package/lib/command.js +1 -1
  5. package/lib/commands/crashlytics-symbols-upload.js +146 -0
  6. package/lib/commands/deploy.js +9 -1
  7. package/lib/commands/ext-configure.js +9 -2
  8. package/lib/commands/ext-dev-deprecate.js +63 -0
  9. package/lib/commands/ext-dev-extension-delete.js +2 -1
  10. package/lib/commands/ext-dev-publish.js +10 -4
  11. package/lib/commands/ext-dev-undeprecate.js +56 -0
  12. package/lib/commands/ext-dev-unpublish.js +12 -4
  13. package/lib/commands/ext-export.js +44 -0
  14. package/lib/commands/ext-install.js +50 -13
  15. package/lib/commands/ext-uninstall.js +6 -0
  16. package/lib/commands/ext-update.js +60 -18
  17. package/lib/commands/functions-config-export.js +115 -0
  18. package/lib/commands/functions-delete.js +47 -25
  19. package/lib/commands/functions-list.js +12 -12
  20. package/lib/commands/index.js +9 -0
  21. package/lib/commands/init.js +3 -0
  22. package/lib/config.js +3 -2
  23. package/lib/deploy/extensions/args.js +2 -0
  24. package/lib/deploy/extensions/deploy.js +49 -0
  25. package/lib/deploy/extensions/deploymentSummary.js +52 -0
  26. package/lib/deploy/extensions/errors.js +31 -0
  27. package/lib/deploy/extensions/index.js +8 -0
  28. package/lib/deploy/extensions/planner.js +95 -0
  29. package/lib/deploy/extensions/prepare.js +103 -0
  30. package/lib/deploy/extensions/release.js +43 -0
  31. package/lib/deploy/extensions/secrets.js +150 -0
  32. package/lib/deploy/extensions/tasks.js +98 -0
  33. package/lib/deploy/extensions/validate.js +17 -0
  34. package/lib/deploy/functions/backend.js +93 -115
  35. package/lib/deploy/functions/checkIam.js +8 -8
  36. package/lib/deploy/functions/containerCleaner.js +82 -22
  37. package/lib/deploy/functions/deploy.js +4 -10
  38. package/lib/deploy/functions/functionsDeployHelper.js +3 -68
  39. package/lib/deploy/functions/prepare.js +62 -27
  40. package/lib/deploy/functions/pricing.js +17 -17
  41. package/lib/deploy/functions/prompts.js +22 -21
  42. package/lib/deploy/functions/release/executor.js +39 -0
  43. package/lib/deploy/functions/release/fabricator.js +422 -0
  44. package/lib/deploy/functions/release/index.js +73 -0
  45. package/lib/deploy/functions/release/planner.js +162 -0
  46. package/lib/deploy/functions/release/reporter.js +165 -0
  47. package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
  48. package/lib/deploy/functions/release/timer.js +14 -0
  49. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
  50. package/lib/deploy/functions/runtimes/node/parseTriggers.js +41 -45
  51. package/lib/deploy/functions/triggerRegionHelper.js +40 -0
  52. package/lib/deploy/functions/validate.js +1 -24
  53. package/lib/deploy/index.js +1 -0
  54. package/lib/downloadUtils.js +37 -0
  55. package/lib/emulator/auth/apiSpec.js +1788 -403
  56. package/lib/emulator/auth/handlers.js +6 -5
  57. package/lib/emulator/auth/operations.js +439 -40
  58. package/lib/emulator/auth/server.js +32 -11
  59. package/lib/emulator/auth/state.js +205 -5
  60. package/lib/emulator/auth/widget_ui.js +2 -2
  61. package/lib/emulator/download.js +2 -31
  62. package/lib/emulator/downloadableEmulators.js +7 -7
  63. package/lib/emulator/emulatorLogger.js +0 -3
  64. package/lib/emulator/events/types.js +16 -0
  65. package/lib/emulator/functionsEmulator.js +120 -21
  66. package/lib/emulator/functionsEmulatorRuntime.js +46 -121
  67. package/lib/emulator/functionsEmulatorShared.js +51 -7
  68. package/lib/emulator/functionsEmulatorShell.js +1 -1
  69. package/lib/emulator/pubsubEmulator.js +61 -40
  70. package/lib/emulator/storage/cloudFunctions.js +37 -7
  71. package/lib/extensions/askUserForConsent.js +14 -1
  72. package/lib/extensions/askUserForParam.js +81 -4
  73. package/lib/extensions/billingMigrationHelper.js +1 -11
  74. package/lib/extensions/changelog.js +2 -1
  75. package/lib/extensions/checkProjectBilling.js +7 -7
  76. package/lib/extensions/displayExtensionInfo.js +35 -33
  77. package/lib/extensions/emulator/optionsHelper.js +3 -3
  78. package/lib/extensions/emulator/triggerHelper.js +2 -32
  79. package/lib/extensions/export.js +107 -0
  80. package/lib/extensions/extensionsApi.js +149 -97
  81. package/lib/extensions/extensionsHelper.js +36 -32
  82. package/lib/extensions/listExtensions.js +16 -11
  83. package/lib/extensions/paramHelper.js +73 -40
  84. package/lib/extensions/provisioningHelper.js +16 -3
  85. package/lib/extensions/refs.js +67 -0
  86. package/lib/extensions/secretsUtils.js +59 -0
  87. package/lib/extensions/updateHelper.js +33 -47
  88. package/lib/extensions/versionHelper.js +14 -0
  89. package/lib/extensions/warnings.js +33 -1
  90. package/lib/functional.js +64 -0
  91. package/lib/functions/env.js +26 -13
  92. package/lib/functions/runtimeConfigExport.js +137 -0
  93. package/lib/gcp/artifactregistry.js +16 -0
  94. package/lib/gcp/cloudfunctions.js +65 -35
  95. package/lib/gcp/cloudfunctionsv2.js +56 -43
  96. package/lib/gcp/cloudscheduler.js +22 -16
  97. package/lib/gcp/cloudtasks.js +143 -0
  98. package/lib/gcp/docker.js +7 -1
  99. package/lib/gcp/proto.js +2 -2
  100. package/lib/gcp/pubsub.js +1 -9
  101. package/lib/gcp/secretManager.js +132 -0
  102. package/lib/gcp/storage.js +16 -0
  103. package/lib/previews.js +1 -1
  104. package/lib/requireInteractive.js +12 -0
  105. package/lib/utils.js +30 -1
  106. package/package.json +6 -4
  107. package/schema/firebase-config.json +9 -0
  108. package/lib/deploy/functions/deploymentPlanner.js +0 -113
  109. package/lib/deploy/functions/deploymentTimer.js +0 -23
  110. package/lib/deploy/functions/errorHandler.js +0 -75
  111. package/lib/deploy/functions/release.js +0 -116
  112. package/lib/deploy/functions/tasks.js +0 -324
  113. package/lib/functions/listFunctions.js +0 -10
  114. package/lib/functionsDelete.js +0 -60
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
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");
5
+ const api = require("../api");
6
+ exports.secretManagerConsoleUri = (projectId) => `https://console.cloud.google.com/security/secret-manager?project=${projectId}`;
7
+ async function listSecrets(projectId) {
8
+ const listRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets`, {
9
+ auth: true,
10
+ origin: api.secretManagerOrigin,
11
+ });
12
+ return listRes.body.secrets.map((s) => parseSecretResourceName(s.name));
13
+ }
14
+ exports.listSecrets = listSecrets;
15
+ async function getSecret(projectId, name) {
16
+ var _a;
17
+ const getRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets/${name}`, {
18
+ auth: true,
19
+ origin: api.secretManagerOrigin,
20
+ });
21
+ const secret = parseSecretResourceName(getRes.body.name);
22
+ secret.labels = (_a = getRes.body.labels) !== null && _a !== void 0 ? _a : {};
23
+ return secret;
24
+ }
25
+ exports.getSecret = getSecret;
26
+ async function getSecretVersion(projectId, name, version) {
27
+ const getRes = await api.request("GET", `/v1beta1/projects/${projectId}/secrets/${name}/versions/${version}`, {
28
+ auth: true,
29
+ origin: api.secretManagerOrigin,
30
+ });
31
+ return parseSecretVersionResourceName(getRes.body.name);
32
+ }
33
+ exports.getSecretVersion = getSecretVersion;
34
+ async function secretExists(projectId, name) {
35
+ try {
36
+ await getSecret(projectId, name);
37
+ return true;
38
+ }
39
+ catch (err) {
40
+ if (err.status === 404) {
41
+ return false;
42
+ }
43
+ throw err;
44
+ }
45
+ }
46
+ exports.secretExists = secretExists;
47
+ function parseSecretResourceName(resourceName) {
48
+ const nameTokens = resourceName.split("/");
49
+ return {
50
+ projectId: nameTokens[1],
51
+ name: nameTokens[3],
52
+ };
53
+ }
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;
70
+ async function createSecret(projectId, name, labels) {
71
+ const createRes = await api.request("POST", `/v1beta1/projects/${projectId}/secrets?secretId=${name}`, {
72
+ auth: true,
73
+ origin: api.secretManagerOrigin,
74
+ data: {
75
+ replication: {
76
+ automatic: {},
77
+ },
78
+ labels,
79
+ },
80
+ });
81
+ return parseSecretResourceName(createRes.body.name);
82
+ }
83
+ exports.createSecret = createSecret;
84
+ async function addVersion(secret, payloadData) {
85
+ const res = await api.request("POST", `/v1beta1/projects/${secret.projectId}/secrets/${secret.name}:addVersion`, {
86
+ auth: true,
87
+ origin: api.secretManagerOrigin,
88
+ data: {
89
+ payload: {
90
+ data: Buffer.from(payloadData).toString("base64"),
91
+ },
92
+ },
93
+ });
94
+ const nameTokens = res.body.name.split("/");
95
+ return {
96
+ secret: {
97
+ projectId: nameTokens[1],
98
+ name: nameTokens[3],
99
+ },
100
+ versionId: nameTokens[5],
101
+ };
102
+ }
103
+ exports.addVersion = addVersion;
104
+ async function grantServiceAgentRole(secret, serviceAccountEmail, role) {
105
+ const getPolicyRes = await api.request("GET", `/v1beta1/projects/${secret.projectId}/secrets/${secret.name}:getIamPolicy`, {
106
+ auth: true,
107
+ origin: api.secretManagerOrigin,
108
+ });
109
+ const bindings = getPolicyRes.body.bindings || [];
110
+ if (bindings.find((b) => b.role == role &&
111
+ b.members.find((m) => m == `serviceAccount:${serviceAccountEmail}`))) {
112
+ return;
113
+ }
114
+ bindings.push({
115
+ role: role,
116
+ members: [`serviceAccount:${serviceAccountEmail}`],
117
+ });
118
+ await api.request("POST", `/v1beta1/projects/${secret.projectId}/secrets/${secret.name}:setIamPolicy`, {
119
+ auth: true,
120
+ origin: api.secretManagerOrigin,
121
+ data: {
122
+ policy: {
123
+ bindings,
124
+ },
125
+ updateMask: {
126
+ paths: "bindings",
127
+ },
128
+ },
129
+ });
130
+ utils_1.logLabeledSuccess("SecretManager", `Granted ${role} on projects/${secret.projectId}/secrets/${secret.name} to ${serviceAccountEmail}`);
131
+ }
132
+ exports.grantServiceAgentRole = grantServiceAgentRole;
@@ -61,9 +61,25 @@ function _deleteObject(location) {
61
61
  origin: api.storageOrigin,
62
62
  });
63
63
  }
64
+ async function _getBucket(bucketName) {
65
+ try {
66
+ const result = await api.request("GET", `/storage/v1/b/${bucketName}`, {
67
+ auth: true,
68
+ origin: api.storageOrigin,
69
+ });
70
+ return result.body;
71
+ }
72
+ catch (err) {
73
+ logger.debug(err);
74
+ throw new FirebaseError("Failed to obtain the storage bucket", {
75
+ original: err,
76
+ });
77
+ }
78
+ }
64
79
  module.exports = {
65
80
  getDefaultBucket: _getDefaultBucket,
66
81
  deleteObject: _deleteObject,
67
82
  upload: _uploadSource,
68
83
  uploadObject: _uploadObject,
84
+ getBucket: _getBucket,
69
85
  };
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 }, 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)) {
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const error_1 = require("./error");
4
+ function requireInteractive(options) {
5
+ if (options.nonInteractive) {
6
+ return Promise.reject(new error_1.FirebaseError("This command cannot run in non-interactive mode", {
7
+ exit: 1,
8
+ }));
9
+ }
10
+ return Promise.resolve();
11
+ }
12
+ exports.default = requireInteractive;
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.18.0",
3
+ "version": "9.22.0",
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",
@@ -85,6 +86,7 @@
85
86
  "dependencies": {
86
87
  "@google-cloud/pubsub": "^2.7.0",
87
88
  "@types/archiver": "^5.1.0",
89
+ "JSONStream": "^1.2.1",
88
90
  "abort-controller": "^3.0.0",
89
91
  "ajv": "^6.12.6",
90
92
  "archiver": "^5.0.0",
@@ -110,7 +112,6 @@
110
112
  "google-auth-library": "^6.1.3",
111
113
  "inquirer": "~6.3.1",
112
114
  "js-yaml": "^3.13.1",
113
- "JSONStream": "^1.2.1",
114
115
  "jsonwebtoken": "^8.5.1",
115
116
  "leven": "^3.1.0",
116
117
  "lodash": "^4.17.21",
@@ -124,7 +125,7 @@
124
125
  "ora": "^3.4.0",
125
126
  "portfinder": "^1.0.23",
126
127
  "progress": "^2.0.3",
127
- "proxy-agent": "^4.0.0",
128
+ "proxy-agent": "^5.0.0",
128
129
  "request": "^2.87.0",
129
130
  "rimraf": "^3.0.0",
130
131
  "semver": "^5.7.1",
@@ -143,6 +144,7 @@
143
144
  "ws": "^7.2.3"
144
145
  },
145
146
  "devDependencies": {
147
+ "@google/events": "^5.1.1",
146
148
  "@manifoldco/swagger-to-ts": "^2.0.0",
147
149
  "@types/body-parser": "^1.17.0",
148
150
  "@types/chai": "^4.2.12",
@@ -194,7 +196,7 @@
194
196
  "eslint-plugin-prettier": "^3.3.1",
195
197
  "firebase": "^7.24.0",
196
198
  "firebase-admin": "^9.4.2",
197
- "firebase-functions": "^3.11.0",
199
+ "firebase-functions": "^3.15.0",
198
200
  "google-discovery-to-swagger": "^2.1.0",
199
201
  "mocha": "^8.2.1",
200
202
  "nock": "^13.0.5",
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "additionalProperties": false,
4
+ "definitions": {
5
+ "ExtensionsConfig": {
6
+ "additionalProperties": false,
7
+ "type": "object"
8
+ }
9
+ },
4
10
  "properties": {
5
11
  "database": {
6
12
  "anyOf": [
@@ -273,6 +279,9 @@
273
279
  },
274
280
  "type": "object"
275
281
  },
282
+ "extensions": {
283
+ "$ref": "#/definitions/ExtensionsConfig"
284
+ },
276
285
  "firestore": {
277
286
  "additionalProperties": false,
278
287
  "properties": {
@@ -1,113 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createDeploymentPlan = exports.calculateRegionalFunctionChanges = exports.allRegions = exports.functionsByRegion = void 0;
4
- const functionsDeployHelper_1 = require("./functionsDeployHelper");
5
- const validate_1 = require("./validate");
6
- const deploymentTool_1 = require("../../deploymentTool");
7
- const utils_1 = require("../../utils");
8
- const backend = require("./backend");
9
- const gcfv2 = require("../../gcp/cloudfunctionsv2");
10
- function functionsByRegion(allFunctions) {
11
- const partitioned = {};
12
- for (const fn of allFunctions) {
13
- partitioned[fn.region] = partitioned[fn.region] || [];
14
- partitioned[fn.region].push(fn);
15
- }
16
- return partitioned;
17
- }
18
- exports.functionsByRegion = functionsByRegion;
19
- function allRegions(spec, existing) {
20
- return Object.keys(Object.assign(Object.assign({}, spec), existing));
21
- }
22
- exports.allRegions = allRegions;
23
- const matchesId = (hasId) => (test) => {
24
- return hasId.id === test.id;
25
- };
26
- function calculateRegionalFunctionChanges(want, have, options) {
27
- want = want.filter((fn) => functionsDeployHelper_1.functionMatchesAnyGroup(fn, options.filters));
28
- have = have.filter((fn) => functionsDeployHelper_1.functionMatchesAnyGroup(fn, options.filters));
29
- let upgradedToGCFv2WithoutSettingConcurrency = false;
30
- const functionsToCreate = want.filter((fn) => !have.some(matchesId(fn)));
31
- const functionsToUpdate = want
32
- .filter((fn) => {
33
- const haveFn = have.find(matchesId(fn));
34
- if (!haveFn) {
35
- return false;
36
- }
37
- validate_1.checkForInvalidChangeOfTrigger(fn, haveFn);
38
- if (!options.overwriteEnvs) {
39
- fn.environmentVariables = Object.assign(Object.assign({}, haveFn.environmentVariables), fn.environmentVariables);
40
- }
41
- if (haveFn.platform === "gcfv1" && fn.platform === "gcfv2" && !fn.concurrency) {
42
- upgradedToGCFv2WithoutSettingConcurrency = true;
43
- }
44
- return true;
45
- })
46
- .map((fn) => {
47
- const haveFn = have.find(matchesId(fn));
48
- const deleteAndRecreate = needsDeleteAndRecreate(haveFn, fn);
49
- return {
50
- func: fn,
51
- deleteAndRecreate,
52
- };
53
- });
54
- const functionsToDelete = have
55
- .filter((fn) => !want.some(matchesId(fn)))
56
- .filter((fn) => deploymentTool_1.isFirebaseManaged(fn.labels || {}));
57
- if (upgradedToGCFv2WithoutSettingConcurrency) {
58
- utils_1.logLabeledBullet("functions", "You are updating one or more functions to Google Cloud Functions v2, " +
59
- "which introduces support for concurrent execution. New functions " +
60
- "default to 80 concurrent executions, but existing functions keep the " +
61
- "old default of 1. You can change this with the 'concurrency' option.");
62
- }
63
- return { functionsToCreate, functionsToUpdate, functionsToDelete };
64
- }
65
- exports.calculateRegionalFunctionChanges = calculateRegionalFunctionChanges;
66
- function createDeploymentPlan(want, have, options) {
67
- const deployment = {
68
- regionalDeployments: {},
69
- schedulesToUpsert: [],
70
- schedulesToDelete: [],
71
- topicsToDelete: [],
72
- };
73
- const wantRegionalFunctions = functionsByRegion(want.cloudFunctions);
74
- const haveRegionalFunctions = functionsByRegion(have.cloudFunctions);
75
- for (const region of allRegions(wantRegionalFunctions, haveRegionalFunctions)) {
76
- const want = wantRegionalFunctions[region] || [];
77
- const have = haveRegionalFunctions[region] || [];
78
- deployment.regionalDeployments[region] = calculateRegionalFunctionChanges(want, have, options);
79
- }
80
- deployment.schedulesToUpsert = want.schedules.filter((schedule) => functionsDeployHelper_1.functionMatchesAnyGroup(schedule.targetService, options.filters));
81
- deployment.schedulesToDelete = have.schedules
82
- .filter((schedule) => !want.schedules.some(matchesId(schedule)))
83
- .filter((schedule) => functionsDeployHelper_1.functionMatchesAnyGroup(schedule.targetService, options.filters));
84
- deployment.topicsToDelete = have.topics
85
- .filter((topic) => !want.topics.some(matchesId(topic)))
86
- .filter((topic) => functionsDeployHelper_1.functionMatchesAnyGroup(topic.targetService, options.filters));
87
- return deployment;
88
- }
89
- exports.createDeploymentPlan = createDeploymentPlan;
90
- function needsDeleteAndRecreate(exFn, fn) {
91
- return changedV2PubSubTopic(exFn, fn);
92
- }
93
- function changedV2PubSubTopic(exFn, fn) {
94
- if (exFn.platform !== "gcfv2") {
95
- return false;
96
- }
97
- if (fn.platform !== "gcfv2") {
98
- return false;
99
- }
100
- if (!backend.isEventTrigger(exFn.trigger)) {
101
- return false;
102
- }
103
- if (!backend.isEventTrigger(fn.trigger)) {
104
- return false;
105
- }
106
- if (exFn.trigger.eventType !== gcfv2.PUBSUB_PUBLISH_EVENT) {
107
- return false;
108
- }
109
- if (fn.trigger.eventType != gcfv2.PUBSUB_PUBLISH_EVENT) {
110
- return false;
111
- }
112
- return exFn.trigger.eventFilters["resource"] != fn.trigger.eventFilters["resource"];
113
- }
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DeploymentTimer = void 0;
4
- const logger_1 = require("../../logger");
5
- const track = require("../../track");
6
- class DeploymentTimer {
7
- constructor() {
8
- this.timings = {};
9
- }
10
- startTimer(name, type) {
11
- this.timings[name] = { type: type, t0: process.hrtime() };
12
- }
13
- endTimer(name) {
14
- if (!this.timings[name]) {
15
- logger_1.logger.debug("[functions] no timer initialized for", name);
16
- return 0;
17
- }
18
- const duration = process.hrtime(this.timings[name].t0);
19
- track("Functions Deploy (Duration)", this.timings[name].type, duration[0] * 1000 + Math.round(duration[1] * 1e-6));
20
- return duration[0] * 1000 * Math.round(duration[1] * 1e-6);
21
- }
22
- }
23
- exports.DeploymentTimer = DeploymentTimer;
@@ -1,75 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ErrorHandler = void 0;
4
- const clc = require("cli-color");
5
- const logger_1 = require("../../logger");
6
- const functionsDeployHelper_1 = require("./functionsDeployHelper");
7
- const error_1 = require("../../error");
8
- class ErrorHandler {
9
- constructor() {
10
- this.errors = [];
11
- this.warnings = [];
12
- }
13
- record(level, functionName, operationType, message) {
14
- const info = {
15
- functionName,
16
- operationType,
17
- message,
18
- };
19
- if (level === "error") {
20
- this.errors.push(info);
21
- }
22
- else if (level === "warning") {
23
- this.warnings.push(info);
24
- }
25
- }
26
- printErrors() {
27
- if (this.errors.length === 0) {
28
- return;
29
- }
30
- logger_1.logger.info("");
31
- logger_1.logger.info("Functions deploy had errors with the following functions:");
32
- for (const failedDeployment of this.errors) {
33
- logger_1.logger.info(`\t${functionsDeployHelper_1.getFunctionLabel(failedDeployment.functionName)}`);
34
- }
35
- const failedIamCalls = this.errors.filter((e) => e.operationType === "set invoker");
36
- if (failedIamCalls.length) {
37
- logger_1.logger.info("");
38
- logger_1.logger.info("Unable to set the invoker for the IAM policy on the following functions:");
39
- for (const failedDep of failedIamCalls) {
40
- logger_1.logger.info(`\t${failedDep.functionName}`);
41
- }
42
- logger_1.logger.info("");
43
- logger_1.logger.info("Some common causes of this:");
44
- logger_1.logger.info("");
45
- logger_1.logger.info("- You may not have the roles/functions.admin IAM role. Note that roles/functions.developer does not allow you to change IAM policies.");
46
- logger_1.logger.info("");
47
- logger_1.logger.info("- An organization policy that restricts Network Access on your project.");
48
- }
49
- logger_1.logger.info("");
50
- logger_1.logger.info("To try redeploying those functions, run:");
51
- logger_1.logger.info(" " +
52
- clc.bold("firebase deploy --only ") +
53
- clc.bold('"') +
54
- clc.bold(this.errors
55
- .map((failedDeployment) => `functions:${functionsDeployHelper_1.getFunctionId(failedDeployment.functionName).replace(/-/g, ".")}`)
56
- .join(",")) +
57
- clc.bold('"'));
58
- logger_1.logger.info("");
59
- logger_1.logger.info("To continue deploying other features (such as database), run:");
60
- logger_1.logger.info(" " + clc.bold("firebase deploy --except functions"));
61
- for (const failedDeployment of this.errors) {
62
- logger_1.logger.debug(`\tError during ${failedDeployment.operationType} for ${failedDeployment.functionName}: ${failedDeployment.message}`);
63
- }
64
- throw new error_1.FirebaseError("Functions did not deploy properly.");
65
- }
66
- printWarnings() {
67
- if (this.warnings.length === 0) {
68
- return;
69
- }
70
- for (const failedDeployment of this.warnings) {
71
- logger_1.logger.debug(`\tWarning during${failedDeployment.operationType} for ${failedDeployment.functionName}: ${failedDeployment.message}`);
72
- }
73
- }
74
- }
75
- exports.ErrorHandler = ErrorHandler;
@@ -1,116 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.release = void 0;
4
- const queue_1 = require("../../throttler/queue");
5
- const deploymentPlanner_1 = require("./deploymentPlanner");
6
- const functionsConfig_1 = require("../../functionsConfig");
7
- const env_1 = require("../../functions/env");
8
- const prompts_1 = require("./prompts");
9
- const deploymentTimer_1 = require("./deploymentTimer");
10
- const errorHandler_1 = require("./errorHandler");
11
- const previews_1 = require("../../previews");
12
- const backend = require("./backend");
13
- const containerCleaner = require("./containerCleaner");
14
- const helper = require("./functionsDeployHelper");
15
- const tasks = require("./tasks");
16
- const utils = require("../../utils");
17
- const track_1 = require("../../track");
18
- async function release(context, options, payload) {
19
- if (!options.config.has("functions")) {
20
- return;
21
- }
22
- const projectId = context.projectId;
23
- const sourceUrl = context.uploadUrl;
24
- const functionsSource = options.config.get("functions.source");
25
- const appEngineLocation = functionsConfig_1.getAppEngineLocation(context.firebaseConfig);
26
- const timer = new deploymentTimer_1.DeploymentTimer();
27
- const errorHandler = new errorHandler_1.ErrorHandler();
28
- const fullDeployment = deploymentPlanner_1.createDeploymentPlan(payload.functions.backend, await backend.existingBackend(context), {
29
- filters: context.filters,
30
- overwriteEnvs: previews_1.previews.dotenv &&
31
- env_1.hasUserEnvs({
32
- functionsSource: options.config.path(functionsSource),
33
- projectId,
34
- projectAlias: options.projectAlias,
35
- }),
36
- });
37
- const cloudFunctionsQueue = new queue_1.default({
38
- retries: 30,
39
- backoff: 20000,
40
- concurrency: 40,
41
- maxBackoff: 40000,
42
- handler: tasks.functionsDeploymentHandler(timer, errorHandler),
43
- });
44
- const schedulerQueue = new queue_1.default({
45
- handler: tasks.schedulerDeploymentHandler(errorHandler),
46
- });
47
- const pubSubQueue = new queue_1.default({
48
- handler: tasks.schedulerDeploymentHandler(errorHandler),
49
- });
50
- const regionPromises = [];
51
- const taskParams = {
52
- projectId,
53
- sourceUrl,
54
- storage: context.storage,
55
- errorHandler,
56
- };
57
- const allFnsToDelete = Object.values(fullDeployment.regionalDeployments).reduce((accum, region) => [...accum, ...region.functionsToDelete], []);
58
- const shouldDeleteFunctions = await prompts_1.promptForFunctionDeletion(allFnsToDelete, options.force, options.nonInteractive);
59
- if (!shouldDeleteFunctions) {
60
- fullDeployment.schedulesToDelete = fullDeployment.schedulesToDelete.filter((schedule) => {
61
- return !allFnsToDelete.find(backend.sameFunctionName(schedule.targetService));
62
- });
63
- fullDeployment.topicsToDelete = fullDeployment.topicsToDelete.filter((topic) => {
64
- return !allFnsToDelete.find(backend.sameFunctionName(topic.targetService));
65
- });
66
- for (const regionalDeployment of Object.values(fullDeployment.regionalDeployments)) {
67
- regionalDeployment.functionsToDelete = [];
68
- }
69
- }
70
- for (const [region, deployment] of Object.entries(fullDeployment.regionalDeployments)) {
71
- regionPromises.push(tasks.runRegionalFunctionDeployment(taskParams, region, deployment, cloudFunctionsQueue));
72
- }
73
- for (const schedule of fullDeployment.schedulesToUpsert) {
74
- const task = tasks.upsertScheduleTask(taskParams, schedule, appEngineLocation);
75
- void schedulerQueue.run(task);
76
- }
77
- for (const schedule of fullDeployment.schedulesToDelete) {
78
- const task = tasks.deleteScheduleTask(taskParams, schedule, appEngineLocation);
79
- void schedulerQueue.run(task);
80
- }
81
- for (const topic of fullDeployment.topicsToDelete) {
82
- const task = tasks.deleteTopicTask(taskParams, topic);
83
- void pubSubQueue.run(task);
84
- }
85
- const queuePromises = [cloudFunctionsQueue.wait(), schedulerQueue.wait(), pubSubQueue.wait()];
86
- cloudFunctionsQueue.process();
87
- schedulerQueue.process();
88
- pubSubQueue.process();
89
- schedulerQueue.close();
90
- pubSubQueue.close();
91
- await Promise.all(regionPromises);
92
- cloudFunctionsQueue.close();
93
- try {
94
- await Promise.all(queuePromises);
95
- }
96
- catch (err) {
97
- utils.reject("Exceeded maximum retries while deploying functions. " +
98
- "If you are deploying a large number of functions, " +
99
- "please deploy your functions in batches by using the --only flag, " +
100
- "and wait a few minutes before deploying again. " +
101
- "Go to https://firebase.google.com/docs/cli/#partial_deploys to learn more.", {
102
- original: err,
103
- });
104
- }
105
- const functions = payload.functions.backend.cloudFunctions;
106
- const gcfv1 = functions.find((fn) => fn.platform === "gcfv1");
107
- const gcfv2 = functions.find((fn) => fn.platform === "gcfv2");
108
- const tag = gcfv1 && gcfv2 ? "v1+v2" : gcfv1 ? "v1" : "v2";
109
- track_1.track("functions_codebase_deploy", tag, functions.length);
110
- helper.logAndTrackDeployStats(cloudFunctionsQueue, errorHandler);
111
- await containerCleaner.cleanupBuildImages(payload.functions.backend.cloudFunctions);
112
- await helper.printTriggerUrls(context, payload.functions.backend);
113
- errorHandler.printWarnings();
114
- errorHandler.printErrors();
115
- }
116
- exports.release = release;