firebase-tools 12.5.4 → 12.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,7 +40,7 @@ class AppDistributionClient {
40
40
  method: "POST",
41
41
  path: `/upload/v1/${appName}/releases:upload`,
42
42
  headers: {
43
- "X-Goog-Upload-File-Name": distribution.getFileName(),
43
+ "X-Goog-Upload-File-Name": encodeURIComponent(distribution.getFileName()),
44
44
  "X-Goog-Upload-Protocol": "raw",
45
45
  "Content-Type": "application/octet-stream",
46
46
  },
@@ -6,7 +6,9 @@ const Table = require("cli-table");
6
6
  const experiments = require("../experiments");
7
7
  const functional_1 = require("../functional");
8
8
  const logger_1 = require("../logger");
9
- exports.command = new command_1.Command("experiments:list").action(() => {
9
+ exports.command = new command_1.Command("experiments:list")
10
+ .description("list all experiments, along with a description of each experiment and whether it is currently enabled")
11
+ .action(() => {
10
12
  const table = new Table({
11
13
  head: ["Enabled", "Name", "Description"],
12
14
  style: { head: ["yellow"] },
@@ -185,7 +185,7 @@ function toBackend(build, paramValues) {
185
185
  throw new error_1.FirebaseError("platform can't be undefined");
186
186
  }
187
187
  const bkEndpoint = Object.assign({ id: endpointId, project: bdEndpoint.project, region: region, entryPoint: bdEndpoint.entryPoint, platform: bdEndpoint.platform, runtime: bdEndpoint.runtime }, trigger);
188
- proto.copyIfPresent(bkEndpoint, bdEndpoint, "environmentVariables", "labels", "secretEnvironmentVariables", "serviceAccount");
188
+ proto.copyIfPresent(bkEndpoint, bdEndpoint, "environmentVariables", "labels", "secretEnvironmentVariables");
189
189
  proto.convertIfPresent(bkEndpoint, bdEndpoint, "ingressSettings", (from) => {
190
190
  if (from !== null && !backend.AllIngressSettings.includes(from)) {
191
191
  throw new error_1.FirebaseError(`Cannot set ingress settings to invalid value ${from}`);
@@ -199,6 +199,7 @@ function toBackend(build, paramValues) {
199
199
  }
200
200
  return mem || null;
201
201
  });
202
+ r.resolveStrings(bkEndpoint, bdEndpoint, "serviceAccount");
202
203
  r.resolveInts(bkEndpoint, bdEndpoint, "timeoutSeconds", "maxInstances", "minInstances", "concurrency");
203
204
  proto.convertIfPresent(bkEndpoint, bdEndpoint, "cpu", (0, functional_1.nullsafeVisitor)((cpu) => (cpu === "gcf_gen1" ? cpu : r.resolveInt(cpu))));
204
205
  if (bdEndpoint.vpc) {
@@ -95,17 +95,18 @@ class Fabricator {
95
95
  deployResults.push(result);
96
96
  };
97
97
  const upserts = [];
98
- const scraper = new sourceTokenScraper_1.SourceTokenScraper();
98
+ const scraperV1 = new sourceTokenScraper_1.SourceTokenScraper();
99
+ const scraperV2 = new sourceTokenScraper_1.SourceTokenScraper();
99
100
  for (const endpoint of changes.endpointsToCreate) {
100
101
  this.logOpStart("creating", endpoint);
101
- upserts.push(handle("create", endpoint, () => this.createEndpoint(endpoint, scraper)));
102
+ upserts.push(handle("create", endpoint, () => this.createEndpoint(endpoint, scraperV1, scraperV2)));
102
103
  }
103
104
  for (const endpoint of changes.endpointsToSkip) {
104
105
  utils.logSuccess(this.getLogSuccessMessage("skip", endpoint));
105
106
  }
106
107
  for (const update of changes.endpointsToUpdate) {
107
108
  this.logOpStart("updating", update.endpoint);
108
- upserts.push(handle("update", update.endpoint, () => this.updateEndpoint(update, scraper)));
109
+ upserts.push(handle("update", update.endpoint, () => this.updateEndpoint(update, scraperV1, scraperV2)));
109
110
  }
110
111
  await utils.allSettled(upserts);
111
112
  if (deployResults.find((r) => r.error)) {
@@ -126,31 +127,31 @@ class Fabricator {
126
127
  await utils.allSettled(deletes);
127
128
  return deployResults;
128
129
  }
129
- async createEndpoint(endpoint, scraper) {
130
+ async createEndpoint(endpoint, scraperV1, scraperV2) {
130
131
  endpoint.labels = Object.assign(Object.assign({}, endpoint.labels), deploymentTool.labels());
131
132
  if (endpoint.platform === "gcfv1") {
132
- await this.createV1Function(endpoint, scraper);
133
+ await this.createV1Function(endpoint, scraperV1);
133
134
  }
134
135
  else if (endpoint.platform === "gcfv2") {
135
- await this.createV2Function(endpoint);
136
+ await this.createV2Function(endpoint, scraperV2);
136
137
  }
137
138
  else {
138
139
  (0, functional_1.assertExhaustive)(endpoint.platform);
139
140
  }
140
141
  await this.setTrigger(endpoint);
141
142
  }
142
- async updateEndpoint(update, scraper) {
143
+ async updateEndpoint(update, scraperV1, scraperV2) {
143
144
  update.endpoint.labels = Object.assign(Object.assign({}, update.endpoint.labels), deploymentTool.labels());
144
145
  if (update.deleteAndRecreate) {
145
146
  await this.deleteEndpoint(update.deleteAndRecreate);
146
- await this.createEndpoint(update.endpoint, scraper);
147
+ await this.createEndpoint(update.endpoint, scraperV1, scraperV2);
147
148
  return;
148
149
  }
149
150
  if (update.endpoint.platform === "gcfv1") {
150
- await this.updateV1Function(update.endpoint, scraper);
151
+ await this.updateV1Function(update.endpoint, scraperV1);
151
152
  }
152
153
  else if (update.endpoint.platform === "gcfv2") {
153
- await this.updateV2Function(update.endpoint);
154
+ await this.updateV2Function(update.endpoint, scraperV2);
154
155
  }
155
156
  else {
156
157
  (0, functional_1.assertExhaustive)(update.endpoint.platform);
@@ -221,7 +222,7 @@ class Fabricator {
221
222
  .catch(rethrowAs(endpoint, "set invoker"));
222
223
  }
223
224
  }
224
- async createV2Function(endpoint) {
225
+ async createV2Function(endpoint, scraper) {
225
226
  var _a, _b, _c, _d, _e;
226
227
  const storageSource = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
227
228
  if (!storageSource) {
@@ -275,10 +276,12 @@ class Fabricator {
275
276
  while (!resultFunction) {
276
277
  resultFunction = await this.functionExecutor
277
278
  .run(async () => {
279
+ apiFunction.buildConfig.sourceToken = await scraper.getToken();
278
280
  const op = await gcfV2.createFunction(apiFunction);
279
- return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `create-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
281
+ return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `create-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
280
282
  })
281
283
  .catch(async (err) => {
284
+ scraper.abort();
282
285
  if (err.code === CLOUD_RUN_RESOURCE_EXHAUSTED_CODE) {
283
286
  await this.deleteV2Function(endpoint);
284
287
  return null;
@@ -366,7 +369,7 @@ class Fabricator {
366
369
  .catch(rethrowAs(endpoint, "set invoker"));
367
370
  }
368
371
  }
369
- async updateV2Function(endpoint) {
372
+ async updateV2Function(endpoint, scraper) {
370
373
  var _a, _b, _c, _d;
371
374
  const storageSource = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
372
375
  if (!storageSource) {
@@ -379,10 +382,15 @@ class Fabricator {
379
382
  }
380
383
  const resultFunction = await this.functionExecutor
381
384
  .run(async () => {
385
+ apiFunction.buildConfig.sourceToken = await scraper.getToken();
382
386
  const op = await gcfV2.updateFunction(apiFunction);
383
- return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `update-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
387
+ return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `update-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
384
388
  }, { retryCodes: [...executor_1.DEFAULT_RETRY_CODES, CLOUD_RUN_RESOURCE_EXHAUSTED_CODE] })
385
- .catch(rethrowAs(endpoint, "update"));
389
+ .catch((err) => {
390
+ scraper.abort();
391
+ logger_1.logger.error(err.message);
392
+ throw new reporter.DeploymentError(endpoint, "update", err);
393
+ });
386
394
  endpoint.uri = (_c = resultFunction.serviceConfig) === null || _c === void 0 ? void 0 : _c.uri;
387
395
  const serviceName = (_d = resultFunction.serviceConfig) === null || _d === void 0 ? void 0 : _d.service;
388
396
  endpoint.runServiceId = utils.last(serviceName === null || serviceName === void 0 ? void 0 : serviceName.split("/"));
@@ -10,21 +10,30 @@ class SourceTokenScraper {
10
10
  this.promise = new Promise((resolve) => (this.resolve = resolve));
11
11
  this.fetchState = "NONE";
12
12
  }
13
+ abort() {
14
+ this.resolve({ aborted: true });
15
+ }
13
16
  async getToken() {
14
17
  if (this.fetchState === "NONE") {
15
18
  this.fetchState = "FETCHING";
16
19
  return undefined;
17
20
  }
18
21
  else if (this.fetchState === "FETCHING") {
19
- return this.promise;
22
+ const tokenResult = await this.promise;
23
+ if (tokenResult.aborted) {
24
+ this.promise = new Promise((resolve) => (this.resolve = resolve));
25
+ return undefined;
26
+ }
27
+ return tokenResult.token;
20
28
  }
21
29
  else if (this.fetchState === "VALID") {
30
+ const tokenResult = await this.promise;
22
31
  if (this.isTokenExpired()) {
23
32
  this.fetchState = "FETCHING";
24
33
  this.promise = new Promise((resolve) => (this.resolve = resolve));
25
34
  return undefined;
26
35
  }
27
- return this.promise;
36
+ return tokenResult.token;
28
37
  }
29
38
  else {
30
39
  (0, functional_1.assertExhaustive)(this.fetchState);
@@ -45,7 +54,10 @@ class SourceTokenScraper {
45
54
  if (((_a = op.metadata) === null || _a === void 0 ? void 0 : _a.sourceToken) || op.done) {
46
55
  const [, , , region] = ((_c = (_b = op.metadata) === null || _b === void 0 ? void 0 : _b.target) === null || _c === void 0 ? void 0 : _c.split("/")) || [];
47
56
  logger_1.logger.debug(`Got source token ${(_d = op.metadata) === null || _d === void 0 ? void 0 : _d.sourceToken} for region ${region}`);
48
- this.resolve((_e = op.metadata) === null || _e === void 0 ? void 0 : _e.sourceToken);
57
+ this.resolve({
58
+ token: (_e = op.metadata) === null || _e === void 0 ? void 0 : _e.sourceToken,
59
+ aborted: false,
60
+ });
49
61
  this.fetchState = "VALID";
50
62
  this.expiry = Date.now() + this.tokenValidDurationMs;
51
63
  }
@@ -620,13 +620,15 @@ async function exportEmulatorData(exportPath, options, initiatedBy) {
620
620
  fs.mkdirSync(exportAbsPath);
621
621
  }
622
622
  const existingMetadata = hubExport_1.HubExport.readMetadata(exportAbsPath);
623
- if (existingMetadata && !(options.force || options.exportOnExit)) {
623
+ const isExportDirEmpty = fs.readdirSync(exportAbsPath).length === 0;
624
+ if ((existingMetadata || !isExportDirEmpty) && !(options.force || options.exportOnExit)) {
624
625
  if (options.noninteractive) {
625
626
  throw new error_1.FirebaseError("Export already exists in the target directory, re-run with --force to overwrite.", { exit: 1 });
626
627
  }
627
- const prompt = await (0, prompt_1.promptOnce)({
628
- type: "confirm",
629
- message: `The directory ${exportAbsPath} already contains export data. Exporting again to the same directory will overwrite all data. Do you want to continue?`,
628
+ const prompt = await (0, prompt_1.confirm)({
629
+ message: `The directory ${exportAbsPath} is not empty. Existing files in this directory will be overwritten. Do you want to continue?`,
630
+ nonInteractive: options.nonInteractive,
631
+ force: options.force,
630
632
  default: false,
631
633
  });
632
634
  if (!prompt) {
@@ -387,7 +387,7 @@ async function getConfig(dir) {
387
387
  if ((0, semver_1.gte)(version, "12.0.0")) {
388
388
  const { default: loadConfig } = (0, utils_1.relativeRequire)(dir, "next/dist/server/config");
389
389
  const { PHASE_PRODUCTION_BUILD } = (0, utils_1.relativeRequire)(dir, "next/constants");
390
- config = await loadConfig(PHASE_PRODUCTION_BUILD, dir, null);
390
+ config = await loadConfig(PHASE_PRODUCTION_BUILD, dir);
391
391
  }
392
392
  else {
393
393
  try {
@@ -13,7 +13,8 @@ const fsutils_1 = require("../fsutils");
13
13
  const url_1 = require("url");
14
14
  const constants_1 = require("./constants");
15
15
  const { dynamicImport } = require(true && "../dynamicImport");
16
- const NPM_ROOT_TIMEOUT_MILLIES = 2000;
16
+ const NPM_ROOT_TIMEOUT_MILLIES = 5000;
17
+ const NPM_ROOT_MEMO = new Map();
17
18
  function isUrl(url) {
18
19
  return /^https?:\/\//.test(url);
19
20
  }
@@ -139,8 +140,16 @@ function scanDependencyTree(searchingFor, dependencies = {}) {
139
140
  }
140
141
  function getNpmRoot(cwd) {
141
142
  var _a;
142
- return (_a = (0, cross_spawn_1.sync)("npm", ["root"], { cwd, timeout: NPM_ROOT_TIMEOUT_MILLIES })
143
+ let npmRoot = NPM_ROOT_MEMO.get(cwd);
144
+ if (npmRoot)
145
+ return npmRoot;
146
+ npmRoot = (_a = (0, cross_spawn_1.sync)("npm", ["root"], {
147
+ cwd,
148
+ timeout: NPM_ROOT_TIMEOUT_MILLIES,
149
+ })
143
150
  .stdout) === null || _a === void 0 ? void 0 : _a.toString().trim();
151
+ NPM_ROOT_MEMO.set(cwd, npmRoot);
152
+ return npmRoot;
144
153
  }
145
154
  exports.getNpmRoot = getNpmRoot;
146
155
  function getNodeModuleBin(name, cwd) {
@@ -91,6 +91,7 @@ async function createFunction(cloudFunction) {
91
91
  const components = cloudFunction.name.split("/");
92
92
  const functionId = components.splice(-1, 1)[0];
93
93
  cloudFunction.buildConfig.environmentVariables = Object.assign(Object.assign({}, cloudFunction.buildConfig.environmentVariables), { GOOGLE_NODE_RUN_SCRIPTS: "" });
94
+ cloudFunction.serviceConfig.environmentVariables = Object.assign(Object.assign({}, cloudFunction.serviceConfig.environmentVariables), { FUNCTION_TARGET: functionId.replace("-", ".") });
94
95
  try {
95
96
  const res = await client.post(components.join("/"), cloudFunction, { queryParams: { functionId } });
96
97
  return res.body;
@@ -143,9 +144,12 @@ async function listFunctionsInternal(projectId, region) {
143
144
  }
144
145
  }
145
146
  async function updateFunction(cloudFunction) {
147
+ const components = cloudFunction.name.split("/");
148
+ const functionId = components.splice(-1, 1)[0];
146
149
  const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "serviceConfig.environmentVariables", "serviceConfig.secretEnvironmentVariables");
147
150
  cloudFunction.buildConfig.environmentVariables = Object.assign(Object.assign({}, cloudFunction.buildConfig.environmentVariables), { GOOGLE_NODE_RUN_SCRIPTS: "" });
148
151
  fieldMasks.push("buildConfig.buildEnvironmentVariables");
152
+ cloudFunction.serviceConfig.environmentVariables = Object.assign(Object.assign({}, cloudFunction.serviceConfig.environmentVariables), { FUNCTION_TARGET: functionId.replace("-", ".") });
149
153
  try {
150
154
  const queryParams = {
151
155
  updateMask: fieldMasks.join(","),
@@ -211,6 +215,7 @@ function functionFromEndpoint(endpoint) {
211
215
  if (backend.isEventTriggered(endpoint)) {
212
216
  gcfFunction.eventTrigger = {
213
217
  eventType: endpoint.eventTrigger.eventType,
218
+ retryPolicy: "RETRY_POLICY_UNSPECIFIED",
214
219
  };
215
220
  if (gcfFunction.eventTrigger.eventType === v2_1.PUBSUB_PUBLISH_EVENT) {
216
221
  if (!((_b = endpoint.eventTrigger.eventFilters) === null || _b === void 0 ? void 0 : _b.topic)) {
@@ -240,9 +245,9 @@ function functionFromEndpoint(endpoint) {
240
245
  }
241
246
  proto.renameIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "triggerRegion", "region");
242
247
  proto.copyIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "channel");
243
- if (endpoint.eventTrigger.retry) {
244
- logger_1.logger.warn("Cannot set a retry policy on Cloud Function", endpoint.id);
245
- }
248
+ endpoint.eventTrigger.retry
249
+ ? (gcfFunction.eventTrigger.retryPolicy = "RETRY_POLICY_RETRY")
250
+ : (gcfFunction.eventTrigger.retryPolicy = "RETRY_POLICY_DO_NOT_RETRY");
246
251
  gcfFunction.serviceConfig.environmentVariables = Object.assign(Object.assign({}, gcfFunction.serviceConfig.environmentVariables), { FUNCTION_SIGNATURE_TYPE: "cloudevent" });
247
252
  }
248
253
  else if (backend.isScheduleTriggered(endpoint)) {
@@ -315,7 +320,7 @@ function endpointFromFunction(gcfFunction) {
315
320
  trigger = {
316
321
  eventTrigger: {
317
322
  eventType: gcfFunction.eventTrigger.eventType,
318
- retry: false,
323
+ retry: gcfFunction.eventTrigger.retryPolicy === "RETRY_POLICY_RETRY" ? true : false,
319
324
  },
320
325
  };
321
326
  if (Object.keys(eventFilters).length) {
@@ -6,19 +6,21 @@ const api_1 = require("../api");
6
6
  const apiv2_1 = require("../apiv2");
7
7
  const error_1 = require("../error");
8
8
  const logger_1 = require("../logger");
9
- const projects_1 = require("../management/projects");
9
+ const ensureApiEnabled_1 = require("../ensureApiEnabled");
10
10
  async function getDefaultBucket(projectId) {
11
11
  var _a;
12
+ await (0, ensureApiEnabled_1.ensure)(projectId, "firebasestorage.googleapis.com", "storage", false);
12
13
  try {
13
- const metadata = await (0, projects_1.getFirebaseProject)(projectId);
14
- if (!((_a = metadata.resources) === null || _a === void 0 ? void 0 : _a.storageBucket)) {
14
+ const localAPIClient = new apiv2_1.Client({ urlPrefix: api_1.firebaseStorageOrigin, apiVersion: "v1alpha" });
15
+ const response = await localAPIClient.get(`/projects/${projectId}/defaultBucket`);
16
+ if (!((_a = response.body) === null || _a === void 0 ? void 0 : _a.name)) {
15
17
  logger_1.logger.debug("Default storage bucket is undefined.");
16
18
  throw new error_1.FirebaseError("Your project is being set up. Please wait a minute before deploying again.");
17
19
  }
18
- return metadata.resources.storageBucket;
20
+ return response.body.name;
19
21
  }
20
22
  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.");
23
+ logger_1.logger.info("\n\nThere was an issue deploying your Storage rules. 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
24
  throw err;
23
25
  }
24
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "12.5.4",
3
+ "version": "12.6.1",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -19,7 +19,8 @@
19
19
  },
20
20
  "pkg": {
21
21
  "scripts": [
22
- "node_modules/npm/src/**/*.js"
22
+ "node_modules/npm/lib/*.js",
23
+ "node_modules/npm/lib/**/*.js"
23
24
  ],
24
25
  "assets": [
25
26
  "node_modules/.bin/**",