firebase-tools 9.21.0 → 9.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +7 -3
  2. package/lib/api.js +2 -0
  3. package/lib/apiv2.js +3 -2
  4. package/lib/commands/crashlytics-symbols-upload.js +1 -1
  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-export.js +44 -0
  10. package/lib/commands/ext-install.js +1 -1
  11. package/lib/commands/ext-update.js +1 -1
  12. package/lib/commands/functions-delete.js +8 -0
  13. package/lib/commands/index.js +6 -5
  14. package/lib/commands/init.js +3 -0
  15. package/lib/commands/remoteconfig-get.js +6 -5
  16. package/lib/config.js +3 -2
  17. package/lib/deploy/extensions/args.js +2 -0
  18. package/lib/deploy/extensions/deploy.js +49 -0
  19. package/lib/deploy/extensions/deploymentSummary.js +52 -0
  20. package/lib/deploy/extensions/errors.js +31 -0
  21. package/lib/deploy/extensions/index.js +8 -0
  22. package/lib/deploy/extensions/params.js +39 -0
  23. package/lib/deploy/extensions/planner.js +94 -0
  24. package/lib/deploy/extensions/prepare.js +111 -0
  25. package/lib/deploy/extensions/release.js +43 -0
  26. package/lib/deploy/extensions/secrets.js +150 -0
  27. package/lib/deploy/extensions/tasks.js +98 -0
  28. package/lib/deploy/extensions/validate.js +17 -0
  29. package/lib/deploy/functions/backend.js +8 -1
  30. package/lib/deploy/functions/checkIam.js +65 -4
  31. package/lib/deploy/functions/containerCleaner.js +97 -50
  32. package/lib/deploy/functions/eventTypes.js +10 -0
  33. package/lib/deploy/functions/prepare.js +12 -1
  34. package/lib/deploy/functions/release/fabricator.js +75 -10
  35. package/lib/deploy/functions/release/index.js +9 -1
  36. package/lib/deploy/functions/release/planner.js +3 -0
  37. package/lib/deploy/functions/release/reporter.js +8 -1
  38. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +28 -0
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +10 -11
  40. package/lib/deploy/functions/services/index.js +38 -0
  41. package/lib/deploy/functions/services/storage.js +43 -0
  42. package/lib/deploy/functions/triggerRegionHelper.js +6 -30
  43. package/lib/deploy/index.js +10 -1
  44. package/lib/emulator/auth/handlers.js +1 -1
  45. package/lib/emulator/auth/operations.js +27 -9
  46. package/lib/emulator/auth/widget_ui.js +17 -3
  47. package/lib/emulator/functionsEmulator.js +18 -2
  48. package/lib/emulator/functionsEmulatorShared.js +1 -0
  49. package/lib/emulator/pubsubEmulator.js +58 -45
  50. package/lib/emulator/storage/cloudFunctions.js +13 -6
  51. package/lib/ensureApiEnabled.js +11 -14
  52. package/lib/extensions/askUserForParam.js +42 -10
  53. package/lib/extensions/checkProjectBilling.js +7 -7
  54. package/lib/extensions/emulator/triggerHelper.js +1 -0
  55. package/lib/extensions/export.js +107 -0
  56. package/lib/extensions/extensionsApi.js +103 -21
  57. package/lib/extensions/extensionsHelper.js +4 -1
  58. package/lib/extensions/listExtensions.js +16 -11
  59. package/lib/extensions/paramHelper.js +6 -4
  60. package/lib/extensions/provisioningHelper.js +16 -3
  61. package/lib/extensions/refs.js +9 -1
  62. package/lib/extensions/secretsUtils.js +10 -9
  63. package/lib/extensions/updateHelper.js +12 -2
  64. package/lib/extensions/versionHelper.js +14 -0
  65. package/lib/extensions/warnings.js +33 -1
  66. package/lib/functions/env.js +2 -2
  67. package/lib/gcp/artifactregistry.js +16 -0
  68. package/lib/gcp/cloudfunctions.js +27 -7
  69. package/lib/gcp/cloudfunctionsv2.js +45 -5
  70. package/lib/gcp/cloudtasks.js +143 -0
  71. package/lib/gcp/docker.js +36 -2
  72. package/lib/gcp/location.js +44 -0
  73. package/lib/gcp/proto.js +2 -2
  74. package/lib/gcp/secretManager.js +27 -6
  75. package/lib/gcp/storage.js +48 -32
  76. package/lib/init/features/functions/index.js +3 -3
  77. package/lib/init/features/hosting/github.js +3 -0
  78. package/lib/init/features/project.js +2 -1
  79. package/lib/previews.js +1 -1
  80. package/lib/projectUtils.js +10 -1
  81. package/package.json +5 -4
  82. package/schema/firebase-config.json +9 -0
@@ -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;
@@ -5,7 +5,7 @@ const logger_1 = require("../../../logger");
5
5
  const prompt_1 = require("../../../prompt");
6
6
  const requirePermissions_1 = require("../../../requirePermissions");
7
7
  const previews_1 = require("../../../previews");
8
- const ensureApiEnabled = require("../../../ensureApiEnabled");
8
+ const ensureApiEnabled_1 = require("../../../ensureApiEnabled");
9
9
  module.exports = async function (setup, config, options) {
10
10
  var _a, _b;
11
11
  logger_1.logger.info();
@@ -17,8 +17,8 @@ module.exports = async function (setup, config, options) {
17
17
  if (projectId) {
18
18
  await requirePermissions_1.requirePermissions(Object.assign(Object.assign({}, options), { project: projectId }));
19
19
  await Promise.all([
20
- ensureApiEnabled.enable(projectId, "cloudfunctions.googleapis.com"),
21
- ensureApiEnabled.enable(projectId, "runtimeconfig.googleapis.com"),
20
+ ensureApiEnabled_1.ensure(projectId, "cloudfunctions.googleapis.com", "unused", true),
21
+ ensureApiEnabled_1.ensure(projectId, "runtimeconfig.googleapis.com", "unused", true),
22
22
  ]);
23
23
  }
24
24
  const choices = [
@@ -31,6 +31,9 @@ async function initGitHub(setup, config, options) {
31
31
  if (!setup.projectId) {
32
32
  return utils_1.reject("Could not determine Project ID, can't set up GitHub workflow.", { exit: 1 });
33
33
  }
34
+ if (!setup.config.hosting) {
35
+ return utils_1.reject(`Didn't find a Hosting config in firebase.json. Run ${cli_color_1.bold("firebase init hosting")} instead.`);
36
+ }
34
37
  logger_1.logger.info();
35
38
  const gitRoot = getGitFolderPath();
36
39
  GIT_DIR = path.join(gitRoot, ".git");
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "9.21.0",
3
+ "version": "9.23.2",
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.11",
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",
@@ -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": {