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 +7 -5
- package/lib/commands/functions-delete.js +7 -1
- package/lib/commands/remoteconfig-get.js +6 -5
- package/lib/deploy/functions/release/fabricator.js +3 -1
- package/lib/deploy/functions/release/index.js +5 -1
- package/lib/deploy/functions/release/reporter.js +4 -0
- package/lib/emulator/auth/handlers.js +1 -1
- package/lib/emulator/auth/widget_ui.js +4 -4
- package/lib/emulator/functionsEmulator.js +15 -1
- package/lib/emulator/functionsEmulatorShared.js +1 -0
- package/lib/emulator/pubsubEmulator.js +58 -45
- package/lib/emulator/storage/cloudFunctions.js +13 -6
- package/lib/ensureApiEnabled.js +1 -2
- package/lib/extensions/askUserForParam.js +32 -8
- package/lib/extensions/emulator/triggerHelper.js +1 -0
- package/lib/functions/env.js +2 -2
- package/lib/init/features/functions/index.js +3 -3
- package/lib/init/features/hosting/github.js +3 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
-
|
|
2
|
-
- Fixes issue
|
|
3
|
-
- Fixes
|
|
4
|
-
-
|
|
5
|
-
-
|
|
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
|
-
|
|
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
|
|
17
|
-
if (typeof Number(versionNumber) == "number") {
|
|
17
|
+
function checkValidOptionalNumber(versionNumber) {
|
|
18
|
+
if (!versionNumber || typeof Number(versionNumber) == "number") {
|
|
18
19
|
return versionNumber;
|
|
19
20
|
}
|
|
20
|
-
|
|
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.
|
|
30
|
-
const template = await rcGet.getTemplate(projectUtils_1.needProjectId(options),
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
76
|
+
throw new Error("Action is not defined as a CloudEvents action");
|
|
76
77
|
}
|
|
77
78
|
const data = objectMetadataPayload;
|
|
78
|
-
|
|
79
|
-
|
|
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;
|
package/lib/ensureApiEnabled.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensure = exports.
|
|
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 (
|
|
143
|
-
|
|
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
|
|
178
|
+
return promptCreateSecret(projectId, instanceId, paramSpec, name);
|
|
162
179
|
}
|
|
163
180
|
else if (secretValue !== "") {
|
|
164
|
-
|
|
165
|
-
|
|
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) {
|
package/lib/functions/env.js
CHANGED
|
@@ -32,11 +32,11 @@ const RESERVED_KEYS = [
|
|
|
32
32
|
const LINE_RE = new RegExp("^" +
|
|
33
33
|
"\\s*" +
|
|
34
34
|
"(\\w+)" +
|
|
35
|
-
"\\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
|
|
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
|
-
|
|
21
|
-
|
|
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.
|
|
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": "
|
|
97
|
+
"cli-table": "0.3.11",
|
|
98
98
|
"commander": "^4.0.1",
|
|
99
99
|
"configstore": "^5.0.1",
|
|
100
100
|
"cors": "^2.8.5",
|