firebase-tools 9.22.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1 @@
1
- - Adds `firebase ext:export` command, and adds `extensions` to `firebase deploy`. See https://firebase.google.com/docs/extensions/reuse-project-config for more infomation on how to manage your extensions with these commands.
2
- - Fixes issue where `init` would crash with multiple Hosting items selected (#3742).
3
- - Adds a command (`crashlytics:symbols:upload`) to upload native symbol files, used in Android NDK crash symbolication.
1
+ - `firebase deploy --only extensions` now supports project specifc .env files. When deploying to multiple projects, param values that are different between projects can be put in `extensions/${extensionInstanceId}.env.${projectIdOrAlias}` and common param values can be put in `extensions/${extensionInstanceId}.env`.
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readParams = void 0;
4
+ const path = require("path");
5
+ const logger_1 = require("../../logger");
6
+ const paramHelper_1 = require("../../extensions/paramHelper");
7
+ const error_1 = require("../../error");
8
+ const ENV_DIRECTORY = "extensions";
9
+ function readParams(args) {
10
+ const filesToCheck = [
11
+ `${args.instanceId}.env`,
12
+ ...args.aliases.map((alias) => `${args.instanceId}.env.${alias}`),
13
+ `${args.instanceId}.env.${args.projectNumber}`,
14
+ `${args.instanceId}.env.${args.projectId}`,
15
+ ];
16
+ let noFilesFound = true;
17
+ const combinedParams = {};
18
+ for (const fileToCheck of filesToCheck) {
19
+ try {
20
+ const params = readParamsFile(args.projectDir, fileToCheck);
21
+ logger_1.logger.debug(`Successfully read params from ${fileToCheck}`);
22
+ noFilesFound = false;
23
+ Object.assign(combinedParams, params);
24
+ }
25
+ catch (err) {
26
+ logger_1.logger.debug(`${err}`);
27
+ }
28
+ }
29
+ if (noFilesFound) {
30
+ throw new error_1.FirebaseError(`No params file found for ${args.instanceId}`);
31
+ }
32
+ return combinedParams;
33
+ }
34
+ exports.readParams = readParams;
35
+ function readParamsFile(projectDir, fileName) {
36
+ const paramPath = path.join(projectDir, ENV_DIRECTORY, fileName);
37
+ const params = paramHelper_1.readEnvFile(paramPath);
38
+ return params;
39
+ }
@@ -1,13 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveVersion = exports.want = exports.have = exports.getExtension = exports.getExtensionVersion = void 0;
4
- const path = require("path");
5
4
  const semver = require("semver");
6
5
  const error_1 = require("../../error");
7
6
  const extensionsApi = require("../../extensions/extensionsApi");
8
7
  const extensionsHelper_1 = require("../../extensions/extensionsHelper");
9
8
  const refs = require("../../extensions/refs");
10
- const paramHelper_1 = require("../../extensions/paramHelper");
9
+ const params_1 = require("./params");
11
10
  const logger_1 = require("../../logger");
12
11
  async function getExtensionVersion(i) {
13
12
  if (!i.extensionVersion) {
@@ -29,7 +28,6 @@ async function getExtension(i) {
29
28
  return i.extension;
30
29
  }
31
30
  exports.getExtension = getExtension;
32
- const ENV_DIRECTORY = "extensions";
33
31
  async function have(projectId) {
34
32
  const instances = await extensionsApi.listInstances(projectId);
35
33
  return instances.map((i) => {
@@ -46,16 +44,22 @@ async function have(projectId) {
46
44
  });
47
45
  }
48
46
  exports.have = have;
49
- async function want(projectId, projectDir, extensions) {
47
+ async function want(args) {
50
48
  const instanceSpecs = [];
51
49
  const errors = [];
52
- for (const e of Object.entries(extensions)) {
50
+ for (const e of Object.entries(args.extensions)) {
53
51
  try {
54
52
  const instanceId = e[0];
55
53
  const ref = refs.parse(e[1]);
56
54
  ref.version = await resolveVersion(ref);
57
- const params = readParams(projectDir, instanceId);
58
- const autoPopulatedParams = await extensionsHelper_1.getFirebaseProjectParams(projectId);
55
+ const params = params_1.readParams({
56
+ projectDir: args.projectDir,
57
+ instanceId,
58
+ projectId: args.projectId,
59
+ projectNumber: args.projectNumber,
60
+ aliases: args.aliases,
61
+ });
62
+ const autoPopulatedParams = await extensionsHelper_1.getFirebaseProjectParams(args.projectId);
59
63
  const subbedParams = extensionsHelper_1.substituteParams(params, autoPopulatedParams);
60
64
  instanceSpecs.push({
61
65
  instanceId,
@@ -88,8 +92,3 @@ async function resolveVersion(ref) {
88
92
  return maxSatisfying;
89
93
  }
90
94
  exports.resolveVersion = resolveVersion;
91
- function readParams(projectDir, instanceId) {
92
- const paramPath = path.join(projectDir, ENV_DIRECTORY, `${instanceId}.env`);
93
- const params = paramHelper_1.readEnvFile(paramPath);
94
- return params;
95
- }
@@ -16,10 +16,18 @@ const warnings_1 = require("../../extensions/warnings");
16
16
  async function prepare(context, options, payload) {
17
17
  var _a;
18
18
  const projectId = projectUtils_1.needProjectId(options);
19
+ const projectNumber = await projectUtils_1.needProjectNumber(options);
20
+ const aliases = projectUtils_1.getAliases(options, projectId);
19
21
  await extensionsHelper_1.ensureExtensionsApiEnabled(options);
20
22
  await requirePermissions_1.requirePermissions(options, ["firebaseextensions.instances.list"]);
21
23
  context.have = await planner.have(projectId);
22
- context.want = await planner.want(projectId, options.config.projectDir, options.config.get("extensions"));
24
+ context.want = await planner.want({
25
+ projectId,
26
+ projectNumber,
27
+ aliases,
28
+ projectDir: options.config.projectDir,
29
+ extensions: options.config.get("extensions"),
30
+ });
23
31
  const usingSecrets = await Promise.all((_a = context.have) === null || _a === void 0 ? void 0 : _a.map(secrets_1.checkSpecForSecrets));
24
32
  if (usingSecrets.some((i) => i)) {
25
33
  await secretsUtils_1.ensureSecretManagerApiEnabled(options);
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
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
5
  const error_1 = require("../../error");
6
- const previews_1 = require("../../previews");
7
6
  const api_1 = require("../../api");
8
7
  const logger_1 = require("../../logger");
9
8
  const artifactregistry = require("../../gcp/artifactregistry");
@@ -62,44 +61,37 @@ async function retry(func) {
62
61
  async function cleanupBuildImages(haveFunctions, deletedFunctions, cleaners = {}) {
63
62
  utils.logBullet(clc.bold.cyan("functions: ") + "cleaning up build files...");
64
63
  const failedDomains = new Set();
65
- if (previews_1.previews.artifactregistry) {
66
- const arCleaner = cleaners.ar || new ArtifactRegistryCleaner();
67
- await Promise.all([
68
- ...haveFunctions.map(async (func) => {
69
- try {
70
- await arCleaner.cleanupFunction(func);
71
- }
72
- catch (err) {
73
- const path = `${func.project}/${func.region}/gcf-artifacts`;
74
- failedDomains.add(`https://console.cloud.google.com/artifacts/docker/${path}`);
75
- }
76
- }),
77
- ...deletedFunctions.map(async (func) => {
78
- try {
79
- await Promise.all([
80
- arCleaner.cleanupFunction(func),
81
- arCleaner.cleanupFunctionCache(func),
82
- ]);
83
- }
84
- catch (err) {
85
- const path = `${func.project}/${func.region}/gcf-artifacts`;
86
- failedDomains.add(`https://console.cloud.google.com/artifacts/docker/${path}`);
87
- }
88
- }),
89
- ]);
90
- }
91
- else {
92
- const gcrCleaner = cleaners.gcr || new ContainerRegistryCleaner();
93
- await Promise.all([...haveFunctions, ...deletedFunctions].map(async (func) => {
94
- try {
95
- await gcrCleaner.cleanupFunction(func);
96
- }
97
- catch (err) {
98
- const path = `${func.project}/${exports.SUBDOMAIN_MAPPING[func.region]}/gcf`;
99
- failedDomains.add(`https://console.cloud.google.com/gcr/images/${path}`);
100
- }
101
- }));
102
- }
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) => {
86
+ try {
87
+ await gcrCleaner.cleanupFunction(func);
88
+ }
89
+ catch (err) {
90
+ const path = `${func.project}/${exports.SUBDOMAIN_MAPPING[func.region]}/gcf`;
91
+ failedDomains.add(`https://console.cloud.google.com/gcr/images/${path}`);
92
+ }
93
+ }));
94
+ await Promise.all(cleanup);
103
95
  if (failedDomains.size) {
104
96
  let message = "Unhandled error cleaning up build images. This could result in a small monthly bill if not corrected. ";
105
97
  message +=
@@ -119,7 +111,16 @@ class ArtifactRegistryCleaner {
119
111
  return `projects/${func.project}/locations/${func.region}/repositories/gcf-artifacts/packages/${func.id}`;
120
112
  }
121
113
  async cleanupFunction(func) {
122
- const op = await artifactregistry.deletePackage(ArtifactRegistryCleaner.packagePath(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
+ }
123
124
  if (op.done) {
124
125
  return;
125
126
  }
@@ -39,6 +39,7 @@ async function prepare(context, options, payload) {
39
39
  ensureApiEnabled.ensure(projectId, "cloudfunctions.googleapis.com", "functions"),
40
40
  ensureApiEnabled.check(projectId, "runtimeconfig.googleapis.com", "runtimeconfig", true),
41
41
  ensureCloudBuildEnabled_1.ensureCloudBuildEnabled(projectId),
42
+ ensureApiEnabled.ensure(projectId, "artifactregistry.googleapis.com", "functions"),
42
43
  ]);
43
44
  context.runtimeConfigEnabled = checkAPIsEnabled[1];
44
45
  const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
@@ -38,6 +38,7 @@ var deploy = function (targetNames, options, customContext = {}) {
38
38
  var deploys = [];
39
39
  var releases = [];
40
40
  var postdeploys = [];
41
+ var startTime = Date.now();
41
42
  for (var i = 0; i < targetNames.length; i++) {
42
43
  var targetName = targetNames[i];
43
44
  var target = TARGETS[targetName];
@@ -75,8 +76,15 @@ var deploy = function (targetNames, options, customContext = {}) {
75
76
  })
76
77
  .then(function () {
77
78
  if (_.has(options, "config.notes.databaseRules")) {
78
- track("Rules Deploy", options.config.notes.databaseRules);
79
+ return track("Rules Deploy", options.config.notes.databaseRules);
79
80
  }
81
+ return;
82
+ })
83
+ .then(function () {
84
+ const duration = Date.now() - startTime;
85
+ return track("Product Deploy", [...targetNames].sort().join(","), duration);
86
+ })
87
+ .then(function () {
80
88
  logger.info();
81
89
  utils.logSuccess(clc.underline.bold("Deploy complete!"));
82
90
  logger.info();
@@ -4,7 +4,6 @@ exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFun
4
4
  const clc = require("cli-color");
5
5
  const error_1 = require("../error");
6
6
  const logger_1 = require("../logger");
7
- const previews_1 = require("../previews");
8
7
  const api = require("../api");
9
8
  const backend = require("../deploy/functions/backend");
10
9
  const utils = require("../utils");
@@ -51,11 +50,8 @@ async function createFunction(cloudFunction) {
51
50
  const apiPath = cloudFunction.name.substring(0, cloudFunction.name.lastIndexOf("/"));
52
51
  const endpoint = `/${exports.API_VERSION}/${apiPath}`;
53
52
  try {
54
- const headers = previews_1.previews.artifactregistry
55
- ? { "X-Firebase-Artifact-Registry": "optin" }
56
- : undefined;
57
53
  const res = await api.request("POST", endpoint, {
58
- headers,
54
+ headers: { "X-Firebase-Artifact-Registry": "optin" },
59
55
  auth: true,
60
56
  data: cloudFunction,
61
57
  origin: api.functionsOrigin,
@@ -150,11 +146,8 @@ async function updateFunction(cloudFunction) {
150
146
  const endpoint = `/${exports.API_VERSION}/${cloudFunction.name}`;
151
147
  const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables");
152
148
  try {
153
- const headers = previews_1.previews.artifactregistry
154
- ? { "X-Firebase-Artifact-Registry": "optin" }
155
- : undefined;
156
149
  const res = await api.request("PATCH", endpoint, {
157
- headers,
150
+ headers: { "X-Firebase-Artifact-Registry": "optin" },
158
151
  qs: {
159
152
  updateMask: fieldMasks.join(","),
160
153
  },
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, artifactregistry: 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 }, 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.22.0",
3
+ "version": "9.23.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -94,7 +94,7 @@
94
94
  "chokidar": "^3.0.2",
95
95
  "cjson": "^0.3.1",
96
96
  "cli-color": "^1.2.0",
97
- "cli-table": "^0.3.1",
97
+ "cli-table": "^0.3.8",
98
98
  "commander": "^4.0.1",
99
99
  "configstore": "^5.0.1",
100
100
  "cors": "^2.8.5",
@@ -138,7 +138,7 @@
138
138
  "universal-analytics": "^0.4.16",
139
139
  "unzipper": "^0.10.10",
140
140
  "update-notifier": "^5.1.0",
141
- "uuid": "^3.0.0",
141
+ "uuid": "^8.3.2",
142
142
  "winston": "^3.0.0",
143
143
  "winston-transport": "^4.4.0",
144
144
  "ws": "^7.2.3"
@@ -182,7 +182,7 @@
182
182
  "@types/tmp": "^0.1.0",
183
183
  "@types/triple-beam": "^1.3.0",
184
184
  "@types/unzipper": "^0.10.0",
185
- "@types/uuid": "^3.4.4",
185
+ "@types/uuid": "^8.3.1",
186
186
  "@types/winston": "^2.4.4",
187
187
  "@types/ws": "^7.2.3",
188
188
  "@typescript-eslint/eslint-plugin": "^4.12.0",