firebase-tools 9.23.1 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
- - Corrects a bug where containers in Artifact Registry would not be deleted if a function has an upper case character in its name (#3918)
2
- - Fixes issue where providing the `--project` flag during `init` would not be recognized with a default project already set. (#3870)
3
- - Fixes issue with setting memory limits for some functions (#3924)
4
- - New HTTPS functions only allow secure traffic. (#3923)
5
- - No longer default-enable AR and don't send builds to AR unless an experiment is enabled (#3935)
1
+ - Fixes issue when installing a Firebase Extension where secrets would be created before validation.
2
+ - Fixes issue with filtering on a specific storage bucket using functions in the emulator. (#3893)
3
+ - Fixes check in Cloud Functions for Firebase initialization to check for API enablement before trying to enable them. (#2574)
4
+ - No longer tries to clean up function build images from Artifact Registry when Artifact Registry is not enabled. (#3943)
5
+ - Show error message when running `firebase init hosting:github` with no Hosting config in `firebase.json`. (#3113)
6
+ - Fixes issue where `remoteconfig:get` was not fetching the latest version by default. (#3559)
7
+ - Fixes issue where empty variables in .env files would instead read as multi-line values. (#3934)
@@ -8,6 +8,7 @@ const projectUtils_1 = require("../projectUtils");
8
8
  const prompt_1 = require("../prompt");
9
9
  const functional_1 = require("../functional");
10
10
  const requirePermissions_1 = require("../requirePermissions");
11
+ const ensure = require("../ensureApiEnabled");
11
12
  const helper = require("../deploy/functions/functionsDeployHelper");
12
13
  const utils = require("../utils");
13
14
  const backend = require("../deploy/functions/backend");
@@ -84,5 +85,10 @@ exports.default = new command_1.Command("functions:delete [filters...]")
84
85
  exit: 1,
85
86
  });
86
87
  }
87
- await containerCleaner.cleanupBuildImages([], allEpToDelete);
88
+ const opts = {};
89
+ const arEnabled = await ensure.check(projectUtils_1.needProjectId(options), "artifactregistry.googleapis.com", "functions", true);
90
+ if (!arEnabled) {
91
+ opts.ar = new containerCleaner.NoopArtifactRegistryCleaner();
92
+ }
93
+ await containerCleaner.cleanupBuildImages([], allEpToDelete, opts);
88
94
  });
@@ -11,13 +11,14 @@ const utils = require("../utils");
11
11
  const Table = require("cli-table");
12
12
  const fs = require("fs");
13
13
  const util = require("util");
14
+ const error_1 = require("../error");
14
15
  const tableHead = ["Entry Name", "Value"];
15
16
  const MAX_DISPLAY_ITEMS = 20;
16
- function checkValidNumber(versionNumber) {
17
- if (typeof Number(versionNumber) == "number") {
17
+ function checkValidOptionalNumber(versionNumber) {
18
+ if (!versionNumber || typeof Number(versionNumber) == "number") {
18
19
  return versionNumber;
19
20
  }
20
- return "null";
21
+ throw new error_1.FirebaseError(`Could not interpret "${versionNumber}" as a valid number.`);
21
22
  }
22
23
  module.exports = new command_1.Command("remoteconfig:get")
23
24
  .description("get a Firebase project's Remote Config template")
@@ -26,8 +27,8 @@ module.exports = new command_1.Command("remoteconfig:get")
26
27
  .before(requireAuth_1.requireAuth)
27
28
  .before(requirePermissions_1.requirePermissions, ["cloudconfig.configs.get"])
28
29
  .action(async (options) => {
29
- utils.assertIsString(options.versionNumber);
30
- const template = await rcGet.getTemplate(projectUtils_1.needProjectId(options), checkValidNumber(options.versionNumber));
30
+ utils.assertIsStringOrUndefined(options.versionNumber);
31
+ const template = await rcGet.getTemplate(projectUtils_1.needProjectId(options), checkValidOptionalNumber(options.versionNumber));
31
32
  const table = new Table({ head: tableHead, style: { head: ["green"] } });
32
33
  if (template.conditions) {
33
34
  let updatedConditions = template.conditions
@@ -124,7 +124,9 @@ class Fabricator {
124
124
  await this.setTrigger(endpoint);
125
125
  }
126
126
  async updateEndpoint(update, scraper) {
127
- update.endpoint.labels = Object.assign(Object.assign({}, update.endpoint.labels), deploymentTool.labels());
127
+ if (update.deleteAndRecreate || update.endpoint.platform !== "gcfv2") {
128
+ update.endpoint.labels = Object.assign(Object.assign({}, update.endpoint.labels), deploymentTool.labels());
129
+ }
128
130
  if (update.deleteAndRecreate) {
129
131
  await this.deleteEndpoint(update.deleteAndRecreate);
130
132
  await this.createEndpoint(update.endpoint, scraper);
@@ -49,7 +49,11 @@ async function release(context, options, payload) {
49
49
  const deletedEndpoints = Object.values(plan)
50
50
  .map((r) => r.endpointsToDelete)
51
51
  .reduce(functional_1.reduceFlat, []);
52
- await containerCleaner.cleanupBuildImages(haveEndpoints, deletedEndpoints);
52
+ const opts = {};
53
+ if (!context.artifactRegistryEnabled) {
54
+ opts.ar = new containerCleaner.NoopArtifactRegistryCleaner();
55
+ }
56
+ await containerCleaner.cleanupBuildImages(haveEndpoints, deletedEndpoints, opts);
53
57
  const allErrors = summary.results.filter((r) => r.error).map((r) => r.error);
54
58
  if (allErrors.length) {
55
59
  const opts = allErrors.length == 1 ? { original: allErrors[0] } : { children: allErrors };
@@ -29,8 +29,10 @@ async function logAndTrackDeployStats(summary) {
29
29
  let totalSuccesses = 0;
30
30
  let totalAborts = 0;
31
31
  const reports = [];
32
+ const regions = new Set();
32
33
  for (const result of summary.results) {
33
34
  const tag = triggerTag(result.endpoint);
35
+ regions.add(result.endpoint.region);
34
36
  totalTime += result.durationMs;
35
37
  if (!result.error) {
36
38
  totalSuccesses++;
@@ -45,6 +47,8 @@ async function logAndTrackDeployStats(summary) {
45
47
  reports.push(track.track("function_deploy_failure", tag, result.durationMs));
46
48
  }
47
49
  }
50
+ const regionCountTag = regions.size < 5 ? regions.size.toString() : ">=5";
51
+ reports.push(track.track("functions_region_count", regionCountTag, 1));
48
52
  const gcfv1 = summary.results.find((r) => r.endpoint.platform === "gcfv1");
49
53
  const gcfv2 = summary.results.find((r) => r.endpoint.platform === "gcfv2");
50
54
  const tag = gcfv1 && gcfv2 ? "v1+v2" : gcfv1 ? "v1" : "v2";
@@ -178,7 +178,7 @@ function registerHandlers(app, getProjectStateByApiKey) {
178
178
  : `
179
179
  <span class="mdc-list-item__graphic material-icons" aria-hidden=true>person</span>`}
180
180
  <span class="mdc-list-item__text"><span class="mdc-list-item__primary-text">${info.displayName || "(No display name)"}</span>
181
- <span class="mdc-list-item__secondary-text fallback-secondary-text">${info.email || ""}</span>
181
+ <span class="mdc-list-item__secondary-text fallback-secondary-text" id="reuse-email">${info.email || ""}</span>
182
182
  </li>`)
183
183
  .join("\n");
184
184
  res.end(widget_ui_1.WIDGET_UI.replace(widget_ui_1.PROVIDERS_LIST_PLACEHOLDER, options));
@@ -158,16 +158,17 @@ var reuseAccountEls = document.querySelectorAll('.js-reuse-account');
158
158
  if (reuseAccountEls.length) {
159
159
  [].forEach.call(reuseAccountEls, function (el) {
160
160
  var urlEncodedIdToken = el.dataset.idToken;
161
+ const decoded = JSON.parse(decodeURIComponent(urlEncodedIdToken));
161
162
  el.addEventListener('click', function (e) {
162
163
  e.preventDefault();
163
- finishWithUser(urlEncodedIdToken);
164
+ finishWithUser(urlEncodedIdToken, decoded.email);
164
165
  });
165
166
  });
166
167
  } else {
167
168
  document.querySelector('.js-accounts-help-text').textContent = "No " + formattedProviderId + " accounts exist in the Auth Emulator.";
168
169
  }
169
170
 
170
- function finishWithUser(urlEncodedIdToken) {
171
+ function finishWithUser(urlEncodedIdToken, email) {
171
172
  // Use widget URL, but replace all query parameters (no apiKey etc.).
172
173
  var url = window.location.href.split('?')[0];
173
174
  // Avoid URLSearchParams for browser compatibility.
@@ -176,7 +177,6 @@ function finishWithUser(urlEncodedIdToken) {
176
177
 
177
178
  // Save reasonable defaults for SAML providers
178
179
  if (isSamlProvider) {
179
- var email = document.getElementById('email-input').value;
180
180
  url += '&SAMLResponse=' + encodeURIComponent(JSON.stringify({
181
181
  assertion: {
182
182
  subject: {
@@ -234,7 +234,7 @@ document.getElementById('main-form').addEventListener('submit', function(e) {
234
234
  if (screenName) claims.screenName = screenName;
235
235
  if (photoUrl) claims.photoUrl = photoUrl;
236
236
 
237
- finishWithUser(createFakeClaims(claims));
237
+ finishWithUser(createFakeClaims(claims), claims.email);
238
238
  }
239
239
  });
240
240
 
@@ -29,6 +29,7 @@ const defaultCredentials_1 = require("../defaultCredentials");
29
29
  const adminSdkConfig_1 = require("./adminSdkConfig");
30
30
  const functionsEnv = require("../functions/env");
31
31
  const types_2 = require("./events/types");
32
+ const validate_1 = require("../deploy/functions/validate");
32
33
  const EVENT_INVOKE = "functions:invoke";
33
34
  const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
34
35
  class FunctionsEmulator {
@@ -138,6 +139,9 @@ class FunctionsEmulator {
138
139
  else {
139
140
  triggerKey = `${this.args.projectId}:${proto.eventType}`;
140
141
  }
142
+ if (proto.data.bucket) {
143
+ triggerKey += `:${proto.data.bucket}`;
144
+ }
141
145
  const triggers = this.multicastTriggers[triggerKey] || [];
142
146
  triggers.forEach((triggerId) => {
143
147
  this.workQueue.submit(() => {
@@ -247,6 +251,13 @@ class FunctionsEmulator {
247
251
  return !anyEnabledMatch;
248
252
  });
249
253
  for (const definition of toSetup) {
254
+ try {
255
+ validate_1.functionIdsAreValid([definition]);
256
+ }
257
+ catch (e) {
258
+ this.logger.logLabeled("WARN", `functions[${definition.id}]`, `Invalid function id: ${e.message}`);
259
+ continue;
260
+ }
250
261
  let added = false;
251
262
  let url = undefined;
252
263
  if (definition.httpsTrigger) {
@@ -396,7 +407,10 @@ class FunctionsEmulator {
396
407
  }
397
408
  addStorageTrigger(projectId, key, eventTrigger) {
398
409
  logger_1.logger.debug(`addStorageTrigger`, JSON.stringify({ eventTrigger }));
399
- const eventTriggerId = `${projectId}:${eventTrigger.eventType}`;
410
+ const bucket = eventTrigger.resource.startsWith("projects/_/buckets/")
411
+ ? eventTrigger.resource.split("/")[3]
412
+ : eventTrigger.resource;
413
+ const eventTriggerId = `${projectId}:${eventTrigger.eventType}:${bucket}`;
400
414
  const triggers = this.multicastTriggers[eventTriggerId] || [];
401
415
  triggers.push(key);
402
416
  this.multicastTriggers[eventTriggerId] = triggers;
@@ -55,6 +55,7 @@ function emulatedFunctionsByRegion(definitions) {
55
55
  defDeepCopy.regions = [region];
56
56
  defDeepCopy.region = region;
57
57
  defDeepCopy.id = `${region}-${defDeepCopy.name}`;
58
+ defDeepCopy.platform = defDeepCopy.platform || "gcfv1";
58
59
  regionDefinitions.push(defDeepCopy);
59
60
  }
60
61
  }
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PubsubEmulator = void 0;
4
4
  const uuid = require("uuid");
5
5
  const pubsub_1 = require("@google-cloud/pubsub");
6
- const api = require("../api");
7
6
  const downloadableEmulators = require("./downloadableEmulators");
7
+ const apiv2_1 = require("../apiv2");
8
8
  const emulatorLogger_1 = require("./emulatorLogger");
9
9
  const types_1 = require("../emulator/types");
10
10
  const constants_1 = require("./constants");
@@ -87,47 +87,54 @@ class PubsubEmulator {
87
87
  this.triggersForTopic.set(topicName, triggers);
88
88
  this.subscriptionForTopic.set(topicName, sub);
89
89
  }
90
- getRequestOptions(topic, message, signatureType) {
91
- const baseOpts = {
92
- origin: `http://${registry_1.EmulatorRegistry.getInfoHostString(registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS).getInfo())}`,
93
- };
94
- if (signatureType === "event") {
95
- return Object.assign(Object.assign({}, baseOpts), { data: {
96
- context: {
97
- eventId: uuid.v4(),
98
- resource: {
99
- service: "pubsub.googleapis.com",
100
- name: `projects/${this.args.projectId}/topics/${topic}`,
101
- },
102
- eventType: "google.pubsub.topic.publish",
103
- timestamp: message.publishTime.toISOString(),
104
- },
105
- data: {
106
- data: message.data,
107
- attributes: message.attributes,
108
- },
109
- } });
90
+ ensureFunctionsClient() {
91
+ if (this.client != undefined)
92
+ return;
93
+ const funcEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS);
94
+ if (!funcEmulator) {
95
+ throw new error_1.FirebaseError(`Attempted to execute pubsub trigger but could not find the Functions emulator`);
110
96
  }
111
- else if (signatureType === "cloudevent") {
112
- const data = {
113
- message: {
114
- messageId: message.id,
115
- publishTime: message.publishTime,
116
- attributes: message.attributes,
117
- orderingKey: message.orderingKey,
118
- data: message.data.toString("base64"),
97
+ this.client = new apiv2_1.Client({
98
+ urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(funcEmulator.getInfo())}`,
99
+ auth: false,
100
+ });
101
+ }
102
+ createLegacyEventRequestBody(topic, message) {
103
+ return {
104
+ context: {
105
+ eventId: uuid.v4(),
106
+ resource: {
107
+ service: "pubsub.googleapis.com",
108
+ name: `projects/${this.args.projectId}/topics/${topic}`,
119
109
  },
120
- subscription: this.subscriptionForTopic.get(topic).name,
121
- };
122
- const ce = {
123
- specVersion: 1,
124
- type: "google.cloud.pubsub.topic.v1.messagePublished",
125
- source: `//pubsub.googleapis.com/projects/${this.args.projectId}/topics/${topic}`,
126
- data,
127
- };
128
- return Object.assign(Object.assign({}, baseOpts), { headers: { "Content-Type": "application/cloudevents+json; charset=UTF-8" }, data: ce });
129
- }
130
- throw new error_1.FirebaseError(`Unsupported trigger signature: ${signatureType}`);
110
+ eventType: "google.pubsub.topic.publish",
111
+ timestamp: message.publishTime.toISOString(),
112
+ },
113
+ data: {
114
+ data: message.data,
115
+ attributes: message.attributes,
116
+ },
117
+ };
118
+ }
119
+ createCloudEventRequestBody(topic, message) {
120
+ const data = {
121
+ message: {
122
+ messageId: message.id,
123
+ publishTime: message.publishTime,
124
+ attributes: message.attributes,
125
+ orderingKey: message.orderingKey,
126
+ data: message.data.toString("base64"),
127
+ },
128
+ subscription: this.subscriptionForTopic.get(topic).name,
129
+ };
130
+ return {
131
+ specversion: "1",
132
+ id: uuid.v4(),
133
+ time: message.publishTime.toISOString(),
134
+ type: "google.cloud.pubsub.topic.v1.messagePublished",
135
+ source: `//pubsub.googleapis.com/projects/${this.args.projectId}/topics/${topic}`,
136
+ data,
137
+ };
131
138
  }
132
139
  async onMessage(topicName, message) {
133
140
  this.logger.logLabeled("DEBUG", "pubsub", `onMessage(${topicName}, ${message.id})`);
@@ -135,14 +142,20 @@ class PubsubEmulator {
135
142
  if (!triggers || triggers.length === 0) {
136
143
  throw new error_1.FirebaseError(`No trigger for topic: ${topicName}`);
137
144
  }
138
- if (!registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS)) {
139
- throw new error_1.FirebaseError(`Attempted to execute pubsub trigger for topic ${topicName} but could not find Functions emulator`);
140
- }
141
145
  this.logger.logLabeled("DEBUG", "pubsub", `Executing ${triggers.length} matching triggers (${JSON.stringify(triggers.map((t) => t.triggerKey))})`);
146
+ this.ensureFunctionsClient();
142
147
  for (const { triggerKey, signatureType } of triggers) {
143
- const reqOpts = this.getRequestOptions(topicName, message, signatureType);
144
148
  try {
145
- await api.request("POST", `/functions/projects/${this.args.projectId}/triggers/${triggerKey}`, reqOpts);
149
+ const path = `/functions/projects/${this.args.projectId}/triggers/${triggerKey}`;
150
+ if (signatureType === "event") {
151
+ await this.client.post(path, this.createLegacyEventRequestBody(topicName, message));
152
+ }
153
+ else if (signatureType === "cloudevent") {
154
+ await this.client.post(path, this.createCloudEventRequestBody(topicName, message), { headers: { "Content-Type": "application/cloudevents+json; charset=UTF-8" } });
155
+ }
156
+ else {
157
+ throw new error_1.FirebaseError(`Unsupported trigger signature: ${signatureType}`);
158
+ }
146
159
  }
147
160
  catch (e) {
148
161
  this.logger.logLabeled("DEBUG", "pubsub", e);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StorageCloudFunctions = void 0;
4
+ const uuid = require("uuid");
4
5
  const registry_1 = require("../registry");
5
6
  const types_1 = require("../types");
6
7
  const emulatorLogger_1 = require("../emulatorLogger");
@@ -57,7 +58,7 @@ class StorageCloudFunctions {
57
58
  }
58
59
  createLegacyEventRequestBody(action, objectMetadataPayload) {
59
60
  const timestamp = new Date();
60
- return JSON.stringify({
61
+ return {
61
62
  eventId: `${timestamp.getTime()}`,
62
63
  timestamp: metadata_1.toSerializedDate(timestamp),
63
64
  eventType: `google.storage.object.${action}`,
@@ -67,20 +68,26 @@ class StorageCloudFunctions {
67
68
  type: "storage#object",
68
69
  },
69
70
  data: objectMetadataPayload,
70
- });
71
+ };
71
72
  }
72
73
  createCloudEventRequestBody(action, objectMetadataPayload) {
73
74
  const ceAction = STORAGE_V2_ACTION_MAP[action];
74
75
  if (!ceAction) {
75
- throw new Error("Action is not definied as a CloudEvents action");
76
+ throw new Error("Action is not defined as a CloudEvents action");
76
77
  }
77
78
  const data = objectMetadataPayload;
78
- return JSON.stringify({
79
- specVersion: 1,
79
+ let time = new Date().toISOString();
80
+ if (data.updated) {
81
+ time = typeof data.updated === "string" ? data.updated : data.updated.toISOString();
82
+ }
83
+ return {
84
+ specversion: "1",
85
+ id: uuid.v4(),
80
86
  type: `google.cloud.storage.object.v1.${ceAction}`,
81
87
  source: `//storage.googleapis.com/projects/_/buckets/${objectMetadataPayload.bucket}/objects/${objectMetadataPayload.name}`,
88
+ time,
82
89
  data,
83
- });
90
+ };
84
91
  }
85
92
  }
86
93
  exports.StorageCloudFunctions = StorageCloudFunctions;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensure = exports.enable = exports.check = exports.POLL_SETTINGS = void 0;
3
+ exports.ensure = exports.check = exports.POLL_SETTINGS = void 0;
4
4
  const cli_color_1 = require("cli-color");
5
5
  const track = require("./track");
6
6
  const api_1 = require("./api");
@@ -37,7 +37,6 @@ https://console.firebase.google.com/project/${projectId}/usage/details`);
37
37
  throw err;
38
38
  }
39
39
  }
40
- exports.enable = enable;
41
40
  async function pollCheckEnabled(projectId, apiName, prefix, silent, enablementRetries, pollRetries = 0) {
42
41
  if (pollRetries > exports.POLL_SETTINGS.pollsBeforeRetry) {
43
42
  return enableApiWithRetries(projectId, apiName, prefix, silent, enablementRetries + 1);
@@ -77,6 +77,7 @@ async function askForParam(projectId, instanceId, paramSpec, reconfiguring) {
77
77
  "You may only select one option.",
78
78
  choices: utils_1.convertExtensionOptionToLabeledList(paramSpec.options),
79
79
  });
80
+ valid = checkResponse(response, paramSpec);
80
81
  break;
81
82
  case extensionsApi_1.ParamType.MULTISELECT:
82
83
  response = await utils_1.onceWithJoin({
@@ -95,11 +96,13 @@ async function askForParam(projectId, instanceId, paramSpec, reconfiguring) {
95
96
  "You may select multiple options.",
96
97
  choices: utils_1.convertExtensionOptionToLabeledList(paramSpec.options),
97
98
  });
99
+ valid = checkResponse(response, paramSpec);
98
100
  break;
99
101
  case extensionsApi_1.ParamType.SECRET:
100
102
  response = reconfiguring
101
103
  ? await promptReconfigureSecret(projectId, instanceId, paramSpec)
102
104
  : await promptCreateSecret(projectId, instanceId, paramSpec);
105
+ valid = true;
103
106
  break;
104
107
  default:
105
108
  response = await prompt_1.promptOnce({
@@ -108,8 +111,8 @@ async function askForParam(projectId, instanceId, paramSpec, reconfiguring) {
108
111
  default: paramSpec.default,
109
112
  message: `Enter a value for ${label}:`,
110
113
  });
114
+ valid = checkResponse(response, paramSpec);
111
115
  }
112
- valid = checkResponse(response, paramSpec);
113
116
  }
114
117
  return response;
115
118
  }
@@ -139,10 +142,24 @@ async function promptReconfigureSecret(projectId, instanceId, paramSpec) {
139
142
  type: "password",
140
143
  message: `This secret will be stored in Cloud Secret Manager as ${secretName}.\nEnter new value for ${paramSpec.label.trim()}:`,
141
144
  });
142
- if (!secret) {
143
- secret = await secretManagerApi.createSecret(projectId, secretName, secretsUtils.getSecretLabels(instanceId));
145
+ if (secretValue === "" && paramSpec.required) {
146
+ logger_1.logger.info(`Secret value cannot be empty for required param ${paramSpec.param}`);
147
+ return promptReconfigureSecret(projectId, instanceId, paramSpec);
148
+ }
149
+ else if (secretValue !== "") {
150
+ if (checkResponse(secretValue, paramSpec)) {
151
+ if (!secret) {
152
+ secret = await secretManagerApi.createSecret(projectId, secretName, secretsUtils.getSecretLabels(instanceId));
153
+ }
154
+ return addNewSecretVersion(projectId, instanceId, secret, paramSpec, secretValue);
155
+ }
156
+ else {
157
+ return promptReconfigureSecret(projectId, instanceId, paramSpec);
158
+ }
159
+ }
160
+ else {
161
+ return "";
144
162
  }
145
- return addNewSecretVersion(projectId, instanceId, secret, paramSpec, secretValue);
146
163
  case SecretUpdateAction.LEAVE:
147
164
  default:
148
165
  return paramSpec.default || "";
@@ -158,13 +175,20 @@ async function promptCreateSecret(projectId, instanceId, paramSpec, secretName)
158
175
  });
159
176
  if (secretValue === "" && paramSpec.required) {
160
177
  logger_1.logger.info(`Secret value cannot be empty for required param ${paramSpec.param}`);
161
- return await promptCreateSecret(projectId, instanceId, paramSpec, name);
178
+ return promptCreateSecret(projectId, instanceId, paramSpec, name);
162
179
  }
163
180
  else if (secretValue !== "") {
164
- const secret = await secretManagerApi.createSecret(projectId, name, secretsUtils.getSecretLabels(instanceId));
165
- return addNewSecretVersion(projectId, instanceId, secret, paramSpec, secretValue);
181
+ if (checkResponse(secretValue, paramSpec)) {
182
+ const secret = await secretManagerApi.createSecret(projectId, name, secretsUtils.getSecretLabels(instanceId));
183
+ return addNewSecretVersion(projectId, instanceId, secret, paramSpec, secretValue);
184
+ }
185
+ else {
186
+ return promptCreateSecret(projectId, instanceId, paramSpec, name);
187
+ }
188
+ }
189
+ else {
190
+ return "";
166
191
  }
167
- return secretValue;
168
192
  }
169
193
  exports.promptCreateSecret = promptCreateSecret;
170
194
  async function generateSecretName(projectId, instanceId, paramName) {
@@ -9,6 +9,7 @@ function functionResourceToEmulatedTriggerDefintion(resource) {
9
9
  const etd = {
10
10
  name: resource.name,
11
11
  entryPoint: resource.name,
12
+ platform: "gcfv1",
12
13
  };
13
14
  const properties = _.get(resource, "properties", {});
14
15
  if (properties.timeout) {
@@ -32,11 +32,11 @@ const RESERVED_KEYS = [
32
32
  const LINE_RE = new RegExp("^" +
33
33
  "\\s*" +
34
34
  "(\\w+)" +
35
- "\\s*=\\s*" +
35
+ "\\s*=[\\f\\t\\v]*" +
36
36
  "(" +
37
37
  "\\s*'(?:\\\\'|[^'])*'|" +
38
38
  '\\s*"(?:\\\\"|[^"])*"|' +
39
- "[^#\\r\\n]+" +
39
+ "[^#\\r\\n]*" +
40
40
  ")?" +
41
41
  "\\s*" +
42
42
  "(?:#[^\\n]*)?" +
@@ -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");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "9.23.1",
3
+ "version": "9.23.2",
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.8",
97
+ "cli-table": "0.3.11",
98
98
  "commander": "^4.0.1",
99
99
  "configstore": "^5.0.1",
100
100
  "cors": "^2.8.5",