firebase-tools 11.15.0 → 11.16.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.
- package/lib/commands/database-push.js +5 -0
- package/lib/commands/database-remove.js +2 -1
- package/lib/commands/database-set.js +5 -0
- package/lib/commands/database-update.js +5 -0
- package/lib/database/remove.js +2 -2
- package/lib/database/removeRemote.js +7 -2
- package/lib/deploy/functions/build.js +3 -0
- package/lib/deploy/functions/prepare.js +21 -8
- package/lib/deploy/functions/release/fabricator.js +2 -2
- package/lib/deploy/functions/release/sourceTokenScraper.js +34 -7
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +2 -1
- package/lib/deploy/hosting/convertConfig.js +21 -7
- package/lib/deploy/hosting/prepare.js +16 -5
- package/lib/deploy/hosting/release.js +2 -2
- package/lib/dynamicImport.js +7 -10
- package/lib/emulator/auth/server.js +2 -3
- package/lib/emulator/download.js +2 -1
- package/lib/emulator/downloadableEmulators.js +65 -50
- package/lib/emulator/extensionsEmulator.js +1 -1
- package/lib/emulator/functionsEmulator.js +2 -1
- package/lib/emulator/functionsEmulatorShared.js +12 -1
- package/lib/emulator/functionsRuntimeWorker.js +9 -2
- package/lib/emulator/storage/apis/firebase.js +7 -27
- package/lib/emulator/storage/apis/gcloud.js +9 -33
- package/lib/emulator/storage/apis/shared.js +43 -0
- package/lib/emulator/storage/rules/config.js +9 -0
- package/lib/emulator/storage/upload.js +2 -2
- package/lib/experiments.js +1 -3
- package/lib/extensions/provisioningHelper.js +10 -4
- package/lib/frameworks/index.js +9 -4
- package/lib/frameworks/next/index.js +57 -17
- package/lib/serve/hosting.js +4 -4
- package/npm-shrinkwrap.json +600 -50
- package/package.json +2 -2
- package/templates/emulators/default_storage.rules +8 -0
- package/templates/init/functions/golang/functions.go +1 -1
- package/templates/init/functions/javascript/index.js +2 -2
- package/templates/init/functions/typescript/index.ts +1 -1
|
@@ -19,6 +19,7 @@ exports.command = new command_1.Command("database:push <path> [infile]")
|
|
|
19
19
|
.description("add a new JSON object to a list of data in your Firebase")
|
|
20
20
|
.option("-d, --data <data>", "specify escaped JSON directly")
|
|
21
21
|
.option("--instance <instance>", "use the database <instance>.firebaseio.com (if omitted, use default database instance)")
|
|
22
|
+
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation")
|
|
22
23
|
.before(requirePermissions_1.requirePermissions, ["firebasedatabase.instances.update"])
|
|
23
24
|
.before(requireDatabaseInstance_1.requireDatabaseInstance)
|
|
24
25
|
.before(database_1.populateInstanceDetails)
|
|
@@ -30,6 +31,9 @@ exports.command = new command_1.Command("database:push <path> [infile]")
|
|
|
30
31
|
const inStream = utils.stringToStream(options.data) || (infile ? fs.createReadStream(infile) : process.stdin);
|
|
31
32
|
const origin = (0, api_1.realtimeOriginOrEmulatorOrCustomUrl)(options.instanceDetails.databaseUrl);
|
|
32
33
|
const u = new url_1.URL(utils.getDatabaseUrl(origin, options.instance, path + ".json"));
|
|
34
|
+
if (options.disableTriggers) {
|
|
35
|
+
u.searchParams.set("disableTriggers", "true");
|
|
36
|
+
}
|
|
33
37
|
if (!infile && !options.data) {
|
|
34
38
|
utils.explainStdin();
|
|
35
39
|
}
|
|
@@ -41,6 +45,7 @@ exports.command = new command_1.Command("database:push <path> [infile]")
|
|
|
41
45
|
method: "POST",
|
|
42
46
|
path: u.pathname,
|
|
43
47
|
body: inStream,
|
|
48
|
+
queryParams: u.searchParams,
|
|
44
49
|
});
|
|
45
50
|
}
|
|
46
51
|
catch (err) {
|
|
@@ -16,6 +16,7 @@ exports.command = new command_1.Command("database:remove <path>")
|
|
|
16
16
|
.description("remove data from your Firebase at the specified path")
|
|
17
17
|
.option("-f, --force", "pass this option to bypass confirmation prompt")
|
|
18
18
|
.option("--instance <instance>", "use the database <instance>.firebaseio.com (if omitted, use default database instance)")
|
|
19
|
+
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation")
|
|
19
20
|
.before(requirePermissions_1.requirePermissions, ["firebasedatabase.instances.update"])
|
|
20
21
|
.before(requireDatabaseInstance_1.requireDatabaseInstance)
|
|
21
22
|
.before(database_1.populateInstanceDetails)
|
|
@@ -35,7 +36,7 @@ exports.command = new command_1.Command("database:remove <path>")
|
|
|
35
36
|
if (!confirm) {
|
|
36
37
|
return utils.reject("Command aborted.", { exit: 1 });
|
|
37
38
|
}
|
|
38
|
-
const removeOps = new remove_1.default(options.instance, path, origin);
|
|
39
|
+
const removeOps = new remove_1.default(options.instance, path, origin, !!options.disableTriggers);
|
|
39
40
|
await removeOps.execute();
|
|
40
41
|
utils.logSuccess("Data removed successfully");
|
|
41
42
|
});
|
|
@@ -21,6 +21,7 @@ exports.command = new command_1.Command("database:set <path> [infile]")
|
|
|
21
21
|
.option("-d, --data <data>", "specify escaped JSON directly")
|
|
22
22
|
.option("-f, --force", "pass this option to bypass confirmation prompt")
|
|
23
23
|
.option("--instance <instance>", "use the database <instance>.firebaseio.com (if omitted, use default database instance)")
|
|
24
|
+
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation")
|
|
24
25
|
.before(requirePermissions_1.requirePermissions, ["firebasedatabase.instances.update"])
|
|
25
26
|
.before(requireDatabaseInstance_1.requireDatabaseInstance)
|
|
26
27
|
.before(database_1.populateInstanceDetails)
|
|
@@ -32,6 +33,9 @@ exports.command = new command_1.Command("database:set <path> [infile]")
|
|
|
32
33
|
const origin = (0, api_1.realtimeOriginOrEmulatorOrCustomUrl)(options.instanceDetails.databaseUrl);
|
|
33
34
|
const dbPath = utils.getDatabaseUrl(origin, options.instance, path);
|
|
34
35
|
const dbJsonURL = new url_1.URL(utils.getDatabaseUrl(origin, options.instance, path + ".json"));
|
|
36
|
+
if (options.disableTriggers) {
|
|
37
|
+
dbJsonURL.searchParams.set("disableTriggers", "true");
|
|
38
|
+
}
|
|
35
39
|
const confirm = await (0, prompt_1.promptOnce)({
|
|
36
40
|
type: "confirm",
|
|
37
41
|
name: "force",
|
|
@@ -51,6 +55,7 @@ exports.command = new command_1.Command("database:set <path> [infile]")
|
|
|
51
55
|
method: "PUT",
|
|
52
56
|
path: dbJsonURL.pathname,
|
|
53
57
|
body: inStream,
|
|
58
|
+
queryParams: dbJsonURL.searchParams,
|
|
54
59
|
});
|
|
55
60
|
}
|
|
56
61
|
catch (err) {
|
|
@@ -21,6 +21,7 @@ exports.command = new command_1.Command("database:update <path> [infile]")
|
|
|
21
21
|
.option("-d, --data <data>", "specify escaped JSON directly")
|
|
22
22
|
.option("-f, --force", "pass this option to bypass confirmation prompt")
|
|
23
23
|
.option("--instance <instance>", "use the database <instance>.firebaseio.com (if omitted, use default database instance)")
|
|
24
|
+
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation")
|
|
24
25
|
.before(requirePermissions_1.requirePermissions, ["firebasedatabase.instances.update"])
|
|
25
26
|
.before(requireDatabaseInstance_1.requireDatabaseInstance)
|
|
26
27
|
.before(database_1.populateInstanceDetails)
|
|
@@ -44,6 +45,9 @@ exports.command = new command_1.Command("database:update <path> [infile]")
|
|
|
44
45
|
(infile && fs.createReadStream(infile)) ||
|
|
45
46
|
process.stdin;
|
|
46
47
|
const jsonUrl = new url_1.URL(utils.getDatabaseUrl(origin, options.instance, path + ".json"));
|
|
48
|
+
if (options.disableTriggers) {
|
|
49
|
+
jsonUrl.searchParams.set("disableTriggers", "true");
|
|
50
|
+
}
|
|
47
51
|
if (!infile && !options.data) {
|
|
48
52
|
utils.explainStdin();
|
|
49
53
|
}
|
|
@@ -53,6 +57,7 @@ exports.command = new command_1.Command("database:update <path> [infile]")
|
|
|
53
57
|
method: "PATCH",
|
|
54
58
|
path: jsonUrl.pathname,
|
|
55
59
|
body: inStream,
|
|
60
|
+
queryParams: jsonUrl.searchParams,
|
|
56
61
|
});
|
|
57
62
|
}
|
|
58
63
|
catch (err) {
|
package/lib/database/remove.js
CHANGED
|
@@ -15,9 +15,9 @@ const INITIAL_DELETE_BATCH_SIZE = 25;
|
|
|
15
15
|
const INITIAL_LIST_NUM_SUB_PATH = 100;
|
|
16
16
|
const MAX_LIST_NUM_SUB_PATH = 204800;
|
|
17
17
|
class DatabaseRemove {
|
|
18
|
-
constructor(instance, path, host) {
|
|
18
|
+
constructor(instance, path, host, disableTriggers) {
|
|
19
19
|
this.path = path;
|
|
20
|
-
this.remote = new removeRemote_1.RTDBRemoveRemote(instance, host);
|
|
20
|
+
this.remote = new removeRemote_1.RTDBRemoveRemote(instance, host, disableTriggers);
|
|
21
21
|
this.deleteJobStack = new stack_1.Stack({
|
|
22
22
|
name: "delete stack",
|
|
23
23
|
concurrency: 1,
|
|
@@ -6,9 +6,10 @@ const url_1 = require("url");
|
|
|
6
6
|
const logger_1 = require("../logger");
|
|
7
7
|
const utils = require("../utils");
|
|
8
8
|
class RTDBRemoveRemote {
|
|
9
|
-
constructor(instance, host) {
|
|
9
|
+
constructor(instance, host, disableTriggers) {
|
|
10
10
|
this.instance = instance;
|
|
11
11
|
this.host = host;
|
|
12
|
+
this.disableTriggers = disableTriggers;
|
|
12
13
|
const url = new url_1.URL(utils.getDatabaseUrl(this.host, this.instance, "/"));
|
|
13
14
|
this.apiClient = new apiv2_1.Client({ urlPrefix: url.origin, auth: true });
|
|
14
15
|
}
|
|
@@ -25,7 +26,11 @@ class RTDBRemoveRemote {
|
|
|
25
26
|
async patch(path, body, note) {
|
|
26
27
|
const t0 = Date.now();
|
|
27
28
|
const url = new url_1.URL(utils.getDatabaseUrl(this.host, this.instance, path + ".json"));
|
|
28
|
-
const queryParams = {
|
|
29
|
+
const queryParams = {
|
|
30
|
+
print: "silent",
|
|
31
|
+
writeSizeLimit: "tiny",
|
|
32
|
+
disableTriggers: this.disableTriggers.toString(),
|
|
33
|
+
};
|
|
29
34
|
const res = await this.apiClient.request({
|
|
30
35
|
method: "PATCH",
|
|
31
36
|
path: url.pathname,
|
|
@@ -160,6 +160,9 @@ function toBackend(build, paramValues) {
|
|
|
160
160
|
const bkEndpoints = [];
|
|
161
161
|
for (const endpointId of Object.keys(build.endpoints)) {
|
|
162
162
|
const bdEndpoint = build.endpoints[endpointId];
|
|
163
|
+
if (r.resolveBoolean(bdEndpoint.omit || false)) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
163
166
|
let regions = bdEndpoint.region;
|
|
164
167
|
if (typeof regions === "undefined") {
|
|
165
168
|
regions = [api.functionsDefaultRegion];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.resolveCpuAndConcurrency = exports.inferBlockingDetails = exports.updateEndpointTargetedStatus = exports.inferDetailsFromExisting = exports.prepare = exports.EVENTARC_SOURCE_ENV = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const backend = require("./backend");
|
|
6
6
|
const build = require("./build");
|
|
@@ -25,6 +25,8 @@ const v1_1 = require("../../functions/events/v1");
|
|
|
25
25
|
const serviceusage_1 = require("../../gcp/serviceusage");
|
|
26
26
|
const applyHash_1 = require("./cache/applyHash");
|
|
27
27
|
const backend_1 = require("./backend");
|
|
28
|
+
const functional_1 = require("../../functional");
|
|
29
|
+
exports.EVENTARC_SOURCE_ENV = "EVENTARC_CLOUD_EVENT_SOURCE";
|
|
28
30
|
function hasUserConfig(config) {
|
|
29
31
|
return Object.keys(config).length > 1;
|
|
30
32
|
}
|
|
@@ -94,7 +96,18 @@ async function prepare(context, options, payload) {
|
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
for (const endpoint of backend.allEndpoints(wantBackend)) {
|
|
97
|
-
endpoint.environmentVariables = wantBackend.environmentVariables;
|
|
99
|
+
endpoint.environmentVariables = wantBackend.environmentVariables || {};
|
|
100
|
+
let resource;
|
|
101
|
+
if (endpoint.platform === "gcfv1") {
|
|
102
|
+
resource = `projects/${endpoint.project}/locations/${endpoint.region}/functions/${endpoint.id}`;
|
|
103
|
+
}
|
|
104
|
+
else if (endpoint.platform === "gcfv2") {
|
|
105
|
+
resource = `projects/${endpoint.project}/locations/${endpoint.region}/services/${endpoint.id}`;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
(0, functional_1.assertExhaustive)(endpoint.platform);
|
|
109
|
+
}
|
|
110
|
+
endpoint.environmentVariables[exports.EVENTARC_SOURCE_ENV] = resource;
|
|
98
111
|
endpoint.codebase = codebase;
|
|
99
112
|
}
|
|
100
113
|
wantBackends[codebase] = wantBackend;
|
|
@@ -143,7 +156,7 @@ async function prepare(context, options, payload) {
|
|
|
143
156
|
for (const [codebase, { wantBackend, haveBackend }] of Object.entries(payload.functions)) {
|
|
144
157
|
inferDetailsFromExisting(wantBackend, haveBackend, codebaseUsesEnvs.includes(codebase));
|
|
145
158
|
await (0, triggerRegionHelper_1.ensureTriggerRegions)(wantBackend);
|
|
146
|
-
|
|
159
|
+
resolveCpuAndConcurrency(wantBackend);
|
|
147
160
|
validate.endpointsAreValid(wantBackend);
|
|
148
161
|
inferBlockingDetails(wantBackend);
|
|
149
162
|
}
|
|
@@ -205,9 +218,6 @@ function inferDetailsFromExisting(want, have, usedDotenv) {
|
|
|
205
218
|
if (typeof wantE.availableMemoryMb === "undefined" && haveE.availableMemoryMb) {
|
|
206
219
|
wantE.availableMemoryMb = haveE.availableMemoryMb;
|
|
207
220
|
}
|
|
208
|
-
if (typeof wantE.concurrency === "undefined" && haveE.concurrency) {
|
|
209
|
-
wantE.concurrency = haveE.concurrency;
|
|
210
|
-
}
|
|
211
221
|
if (typeof wantE.cpu === "undefined" && haveE.cpu) {
|
|
212
222
|
wantE.cpu = haveE.cpu;
|
|
213
223
|
}
|
|
@@ -264,7 +274,7 @@ function inferBlockingDetails(want) {
|
|
|
264
274
|
}
|
|
265
275
|
}
|
|
266
276
|
exports.inferBlockingDetails = inferBlockingDetails;
|
|
267
|
-
function
|
|
277
|
+
function resolveCpuAndConcurrency(want) {
|
|
268
278
|
for (const e of backend.allEndpoints(want)) {
|
|
269
279
|
if (e.platform === "gcfv1") {
|
|
270
280
|
continue;
|
|
@@ -275,6 +285,9 @@ function resolveCpu(want) {
|
|
|
275
285
|
else if (!e.cpu) {
|
|
276
286
|
e.cpu = backend.memoryToGen2Cpu(e.availableMemoryMb || backend.DEFAULT_MEMORY);
|
|
277
287
|
}
|
|
288
|
+
if (!e.concurrency) {
|
|
289
|
+
e.concurrency = e.cpu >= 1 ? backend.DEFAULT_CONCURRENCY : 1;
|
|
290
|
+
}
|
|
278
291
|
}
|
|
279
292
|
}
|
|
280
|
-
exports.
|
|
293
|
+
exports.resolveCpuAndConcurrency = resolveCpuAndConcurrency;
|
|
@@ -167,9 +167,9 @@ class Fabricator {
|
|
|
167
167
|
if (apiFunction.httpsTrigger) {
|
|
168
168
|
apiFunction.httpsTrigger.securityLevel = "SECURE_ALWAYS";
|
|
169
169
|
}
|
|
170
|
-
apiFunction.sourceToken = await scraper.tokenPromise();
|
|
171
170
|
const resultFunction = await this.functionExecutor
|
|
172
171
|
.run(async () => {
|
|
172
|
+
apiFunction.sourceToken = await scraper.getToken();
|
|
173
173
|
const op = await gcf.createFunction(apiFunction);
|
|
174
174
|
return poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `create-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
|
|
175
175
|
})
|
|
@@ -301,9 +301,9 @@ class Fabricator {
|
|
|
301
301
|
throw new Error("Precondition failed");
|
|
302
302
|
}
|
|
303
303
|
const apiFunction = gcf.functionFromEndpoint(endpoint, sourceUrl);
|
|
304
|
-
apiFunction.sourceToken = await scraper.tokenPromise();
|
|
305
304
|
const resultFunction = await this.functionExecutor
|
|
306
305
|
.run(async () => {
|
|
306
|
+
apiFunction.sourceToken = await scraper.getToken();
|
|
307
307
|
const op = await gcf.updateFunction(apiFunction);
|
|
308
308
|
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `update-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
|
|
309
309
|
})
|
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SourceTokenScraper = void 0;
|
|
4
|
+
const error_1 = require("../../../error");
|
|
5
|
+
const functional_1 = require("../../../functional");
|
|
4
6
|
const logger_1 = require("../../../logger");
|
|
5
7
|
class SourceTokenScraper {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.
|
|
8
|
+
constructor(validDurationMs = 1500000) {
|
|
9
|
+
this.tokenValidDurationMs = validDurationMs;
|
|
8
10
|
this.promise = new Promise((resolve) => (this.resolve = resolve));
|
|
11
|
+
this.fetchState = "NONE";
|
|
9
12
|
}
|
|
10
|
-
|
|
11
|
-
if (this.
|
|
12
|
-
this.
|
|
13
|
-
return
|
|
13
|
+
async getToken() {
|
|
14
|
+
if (this.fetchState === "NONE") {
|
|
15
|
+
this.fetchState = "FETCHING";
|
|
16
|
+
return undefined;
|
|
14
17
|
}
|
|
15
|
-
|
|
18
|
+
else if (this.fetchState === "FETCHING") {
|
|
19
|
+
return this.promise;
|
|
20
|
+
}
|
|
21
|
+
else if (this.fetchState === "VALID") {
|
|
22
|
+
if (this.isTokenExpired()) {
|
|
23
|
+
this.fetchState = "FETCHING";
|
|
24
|
+
this.promise = new Promise((resolve) => (this.resolve = resolve));
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return this.promise;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
(0, functional_1.assertExhaustive)(this.fetchState);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
isTokenExpired() {
|
|
34
|
+
if (this.expiry === undefined) {
|
|
35
|
+
throw new error_1.FirebaseError("Your deployment is checking the expiration of a source token that has not yet been polled. " +
|
|
36
|
+
"Hitting this case should never happen and should be considered a bug. " +
|
|
37
|
+
"Please file an issue at https://github.com/firebase/firebase-tools/issues " +
|
|
38
|
+
"and try deploying your functions again.");
|
|
39
|
+
}
|
|
40
|
+
return Date.now() >= this.expiry;
|
|
16
41
|
}
|
|
17
42
|
get poller() {
|
|
18
43
|
return (op) => {
|
|
@@ -21,6 +46,8 @@ class SourceTokenScraper {
|
|
|
21
46
|
const [, , , region] = ((_c = (_b = op.metadata) === null || _b === void 0 ? void 0 : _b.target) === null || _c === void 0 ? void 0 : _c.split("/")) || [];
|
|
22
47
|
logger_1.logger.debug(`Got source token ${(_d = op.metadata) === null || _d === void 0 ? void 0 : _d.sourceToken} for region ${region}`);
|
|
23
48
|
this.resolve((_e = op.metadata) === null || _e === void 0 ? void 0 : _e.sourceToken);
|
|
49
|
+
this.fetchState = "VALID";
|
|
50
|
+
this.expiry = Date.now() + this.tokenValidDurationMs;
|
|
24
51
|
}
|
|
25
52
|
};
|
|
26
53
|
}
|
|
@@ -51,6 +51,7 @@ function assertBuildEndpoint(ep, id) {
|
|
|
51
51
|
region: "array",
|
|
52
52
|
platform: (platform) => build.AllFunctionsPlatforms.includes(platform),
|
|
53
53
|
entryPoint: "string",
|
|
54
|
+
omit: "Field<boolean>?",
|
|
54
55
|
availableMemoryMb: (mem) => mem === null || isCEL(mem) || build.isValidMemoryOption(mem),
|
|
55
56
|
maxInstances: "Field<number>?",
|
|
56
57
|
minInstances: "Field<number>?",
|
|
@@ -259,7 +260,7 @@ function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
|
|
|
259
260
|
if ("serviceAccountEmail" in ep) {
|
|
260
261
|
parsed.serviceAccount = ep.serviceAccountEmail;
|
|
261
262
|
}
|
|
262
|
-
(0, proto_1.copyIfPresent)(parsed, ep, "availableMemoryMb", "cpu", "maxInstances", "minInstances", "concurrency", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables", "serviceAccount");
|
|
263
|
+
(0, proto_1.copyIfPresent)(parsed, ep, "omit", "availableMemoryMb", "cpu", "maxInstances", "minInstances", "concurrency", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables", "serviceAccount");
|
|
263
264
|
(0, proto_1.convertIfPresent)(parsed, ep, "secretEnvironmentVariables", (senvs) => {
|
|
264
265
|
if (!senvs) {
|
|
265
266
|
return null;
|
|
@@ -36,13 +36,13 @@ function extractPattern(type, source) {
|
|
|
36
36
|
function findEndpointForRewrite(site, targetBackend, id, region) {
|
|
37
37
|
const endpoints = backend.allEndpoints(targetBackend).filter((e) => e.id === id);
|
|
38
38
|
if (endpoints.length === 0) {
|
|
39
|
-
return;
|
|
39
|
+
return { matchingEndpoint: undefined, foundMatchingId: false };
|
|
40
40
|
}
|
|
41
41
|
if (endpoints.length === 1) {
|
|
42
42
|
if (region && region !== endpoints[0].region) {
|
|
43
|
-
return;
|
|
43
|
+
return { matchingEndpoint: undefined, foundMatchingId: true };
|
|
44
44
|
}
|
|
45
|
-
return endpoints[0];
|
|
45
|
+
return { matchingEndpoint: endpoints[0], foundMatchingId: true };
|
|
46
46
|
}
|
|
47
47
|
if (!region) {
|
|
48
48
|
const us = endpoints.find((e) => e.region === "us-central1");
|
|
@@ -51,15 +51,19 @@ function findEndpointForRewrite(site, targetBackend, id, region) {
|
|
|
51
51
|
}
|
|
52
52
|
(0, utils_1.logLabeledBullet)(`hosting[${site}]`, `Function \`${id}\` found in multiple regions, defaulting to \`us-central1\`. ` +
|
|
53
53
|
`To rewrite to a different region, specify a \`region\` for the rewrite in \`firebase.json\`.`);
|
|
54
|
-
return us;
|
|
54
|
+
return { matchingEndpoint: us, foundMatchingId: true };
|
|
55
55
|
}
|
|
56
|
-
return
|
|
56
|
+
return {
|
|
57
|
+
matchingEndpoint: endpoints.find((e) => e.region === region),
|
|
58
|
+
foundMatchingId: true,
|
|
59
|
+
};
|
|
57
60
|
}
|
|
58
61
|
exports.findEndpointForRewrite = findEndpointForRewrite;
|
|
59
|
-
async function convertConfig(context, deploy) {
|
|
62
|
+
async function convertConfig(context, functionsPayload, deploy) {
|
|
60
63
|
var _a, _b, _c, _d;
|
|
61
64
|
const config = {};
|
|
62
65
|
const hasBackends = !!((_a = deploy.config.rewrites) === null || _a === void 0 ? void 0 : _a.some((r) => "function" in r || "run" in r));
|
|
66
|
+
const wantBackend = backend.merge(...Object.values(functionsPayload.functions || {}).map((c) => c.wantBackend));
|
|
63
67
|
let haveBackend = backend.empty();
|
|
64
68
|
if (hasBackends) {
|
|
65
69
|
try {
|
|
@@ -87,8 +91,18 @@ async function convertConfig(context, deploy) {
|
|
|
87
91
|
}
|
|
88
92
|
const id = rewrite.function.functionId;
|
|
89
93
|
const region = rewrite.function.region;
|
|
90
|
-
const
|
|
94
|
+
const deployingEndpointSearch = findEndpointForRewrite(deploy.config.site, wantBackend, id, region);
|
|
95
|
+
const existingEndpointSearch = !deployingEndpointSearch.foundMatchingId && !deployingEndpointSearch.matchingEndpoint
|
|
96
|
+
? findEndpointForRewrite(deploy.config.site, haveBackend, id, region)
|
|
97
|
+
: undefined;
|
|
98
|
+
const endpoint = deployingEndpointSearch.matchingEndpoint
|
|
99
|
+
? deployingEndpointSearch.matchingEndpoint
|
|
100
|
+
: existingEndpointSearch === null || existingEndpointSearch === void 0 ? void 0 : existingEndpointSearch.matchingEndpoint;
|
|
91
101
|
if (!endpoint) {
|
|
102
|
+
if (deployingEndpointSearch.foundMatchingId || (existingEndpointSearch === null || existingEndpointSearch === void 0 ? void 0 : existingEndpointSearch.foundMatchingId)) {
|
|
103
|
+
throw new error_1.FirebaseError(`Unable to find a valid endpoint for function. Functions matching the rewrite
|
|
104
|
+
are present but in the wrong region.`);
|
|
105
|
+
}
|
|
92
106
|
(0, utils_1.logLabeledWarning)(`hosting[${deploy.config.site}]`, `Unable to find a valid endpoint for function \`${id}\`, but still including it in the config`);
|
|
93
107
|
const apiRewrite = Object.assign(Object.assign({}, target), { function: id });
|
|
94
108
|
if (region) {
|
|
@@ -6,6 +6,7 @@ const api = require("../../hosting/api");
|
|
|
6
6
|
const config = require("../../hosting/config");
|
|
7
7
|
const deploymentTool = require("../../deploymentTool");
|
|
8
8
|
const functional_1 = require("../../functional");
|
|
9
|
+
const track_1 = require("../../track");
|
|
9
10
|
async function prepare(context, options) {
|
|
10
11
|
if (options.public) {
|
|
11
12
|
if (Array.isArray(options.config.get("hosting"))) {
|
|
@@ -17,11 +18,21 @@ async function prepare(context, options) {
|
|
|
17
18
|
if (configs.length === 0) {
|
|
18
19
|
return Promise.resolve();
|
|
19
20
|
}
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const versions = await Promise.all(configs.map(async (config) => {
|
|
22
|
+
const labels = Object.assign({}, deploymentTool.labels());
|
|
23
|
+
if (config.webFramework) {
|
|
24
|
+
labels["firebase-web-framework"] = config.webFramework;
|
|
25
|
+
}
|
|
26
|
+
const version = {
|
|
27
|
+
status: "CREATED",
|
|
28
|
+
labels,
|
|
29
|
+
};
|
|
30
|
+
const [, versionName] = await Promise.all([
|
|
31
|
+
(0, track_1.track)("hosting_deploy", config.webFramework || "classic"),
|
|
32
|
+
api.createVersion(config.site, version),
|
|
33
|
+
]);
|
|
34
|
+
return versionName;
|
|
35
|
+
}));
|
|
25
36
|
context.hosting = {
|
|
26
37
|
deploys: [],
|
|
27
38
|
};
|
|
@@ -6,7 +6,7 @@ const logger_1 = require("../../logger");
|
|
|
6
6
|
const utils = require("../../utils");
|
|
7
7
|
const convertConfig_1 = require("./convertConfig");
|
|
8
8
|
const error_1 = require("../../error");
|
|
9
|
-
async function release(context, options) {
|
|
9
|
+
async function release(context, options, functionsPayload) {
|
|
10
10
|
if (!context.hosting || !context.hosting.deploys) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
@@ -18,7 +18,7 @@ async function release(context, options) {
|
|
|
18
18
|
utils.logLabeledBullet(`hosting[${deploy.config.site}]`, "finalizing version...");
|
|
19
19
|
const update = {
|
|
20
20
|
status: "FINALIZED",
|
|
21
|
-
config: await (0, convertConfig_1.convertConfig)(context, deploy),
|
|
21
|
+
config: await (0, convertConfig_1.convertConfig)(context, functionsPayload, deploy),
|
|
22
22
|
};
|
|
23
23
|
const versionId = utils.last(deploy.version.split("/"));
|
|
24
24
|
const finalizedVersion = await api.updateVersion(deploy.config.site, versionId, update);
|
package/lib/dynamicImport.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
const { pathToFileURL } = require("url");
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (mod.startsWith("/"))
|
|
7
|
-
return Promise.resolve().then(() => require(pathToFileURL(mod).toString()));
|
|
2
|
+
|
|
3
|
+
exports.dynamicImport = function(mod) {
|
|
4
|
+
if (mod.startsWith("file://")) return import(mod);
|
|
5
|
+
if (mod.startsWith("/")) return import(pathToFileURL(mod).toString());
|
|
8
6
|
try {
|
|
9
7
|
const path = require.resolve(mod);
|
|
10
|
-
return
|
|
11
|
-
}
|
|
12
|
-
catch (e) {
|
|
8
|
+
return import(pathToFileURL(path).toString());
|
|
9
|
+
} catch(e) {
|
|
13
10
|
return Promise.reject(e);
|
|
14
11
|
}
|
|
15
|
-
}
|
|
12
|
+
}
|
|
@@ -259,9 +259,8 @@ async function createApp(defaultProjectId, singleProjectMode = index_1.SinglePro
|
|
|
259
259
|
defaultProjectId !== projectId) {
|
|
260
260
|
const errorString = `Multiple projectIds are not recommended in single project mode. ` +
|
|
261
261
|
`Requested project ID ${projectId}, but the emulator is configured for ` +
|
|
262
|
-
`${defaultProjectId}.
|
|
263
|
-
|
|
264
|
-
` firebase.json emulators config.`;
|
|
262
|
+
`${defaultProjectId}. To opt-out of single project mode add/set the ` +
|
|
263
|
+
`\'"singleProjectMode"\' false' property in the firebase.json emulators config.`;
|
|
265
264
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.AUTH).log("WARN", errorString);
|
|
266
265
|
if (singleProjectMode === index_1.SingleProjectMode.ERROR) {
|
|
267
266
|
throw new errors_2.BadRequestError(errorString);
|
package/lib/emulator/download.js
CHANGED
|
@@ -27,6 +27,7 @@ async function downloadEmulator(name) {
|
|
|
27
27
|
if (emulator.unzipDir) {
|
|
28
28
|
await unzip(emulator.downloadPath, emulator.unzipDir);
|
|
29
29
|
}
|
|
30
|
+
await new Promise((f) => setTimeout(f, 2000));
|
|
30
31
|
const executablePath = emulator.binaryPath || emulator.downloadPath;
|
|
31
32
|
fs.chmodSync(executablePath, 0o755);
|
|
32
33
|
removeOldFiles(name, emulator);
|
|
@@ -54,7 +55,7 @@ function unzip(zipPath, unzipDir) {
|
|
|
54
55
|
fs.createReadStream(zipPath)
|
|
55
56
|
.pipe(unzipper.Extract({ path: unzipDir }))
|
|
56
57
|
.on("error", reject)
|
|
57
|
-
.on("
|
|
58
|
+
.on("close", resolve);
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
61
|
function removeOldFiles(name, emulator, removeAllVersions = false) {
|
|
@@ -16,79 +16,94 @@ const download_1 = require("../emulator/download");
|
|
|
16
16
|
const experiments = require("../experiments");
|
|
17
17
|
const EMULATOR_INSTANCE_KILL_TIMEOUT = 4000;
|
|
18
18
|
const CACHE_DIR = process.env.FIREBASE_EMULATORS_PATH || path.join(os.homedir(), ".cache", "firebase", "emulators");
|
|
19
|
+
const EMULATOR_UPDATE_DETAILS = {
|
|
20
|
+
database: {
|
|
21
|
+
version: "4.11.0",
|
|
22
|
+
expectedSize: 34318940,
|
|
23
|
+
expectedChecksum: "311609538bd65666eb724ef47c2e6466",
|
|
24
|
+
},
|
|
25
|
+
firestore: {
|
|
26
|
+
version: "1.15.1",
|
|
27
|
+
expectedSize: 61475851,
|
|
28
|
+
expectedChecksum: "4f41d24a3c0f3b55ea22804a424cc0ee",
|
|
29
|
+
},
|
|
30
|
+
storage: {
|
|
31
|
+
version: "1.1.1",
|
|
32
|
+
expectedSize: 46448285,
|
|
33
|
+
expectedChecksum: "691982db4019d49d345a97151bdea7e2",
|
|
34
|
+
},
|
|
35
|
+
ui: experiments.isEnabled("emulatoruisnapshot")
|
|
36
|
+
? { version: "SNAPSHOT", expectedSize: -1, expectedChecksum: "" }
|
|
37
|
+
: {
|
|
38
|
+
version: "1.11.1",
|
|
39
|
+
expectedSize: 3061713,
|
|
40
|
+
expectedChecksum: "a4944414518be206280b495f526f18bf",
|
|
41
|
+
},
|
|
42
|
+
pubsub: {
|
|
43
|
+
version: "0.7.1",
|
|
44
|
+
expectedSize: 65137179,
|
|
45
|
+
expectedChecksum: "b59a6e705031a54a69e5e1dced7ca9bf",
|
|
46
|
+
},
|
|
47
|
+
};
|
|
19
48
|
exports.DownloadDetails = {
|
|
20
49
|
database: {
|
|
21
|
-
downloadPath: path.join(CACHE_DIR,
|
|
22
|
-
version:
|
|
50
|
+
downloadPath: path.join(CACHE_DIR, `firebase-database-emulator-v${EMULATOR_UPDATE_DETAILS.database.version}.jar`),
|
|
51
|
+
version: EMULATOR_UPDATE_DETAILS.database.version,
|
|
23
52
|
opts: {
|
|
24
53
|
cacheDir: CACHE_DIR,
|
|
25
|
-
remoteUrl:
|
|
26
|
-
expectedSize:
|
|
27
|
-
expectedChecksum:
|
|
54
|
+
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/firebase-database-emulator-v${EMULATOR_UPDATE_DETAILS.database.version}.jar`,
|
|
55
|
+
expectedSize: EMULATOR_UPDATE_DETAILS.database.expectedSize,
|
|
56
|
+
expectedChecksum: EMULATOR_UPDATE_DETAILS.database.expectedChecksum,
|
|
28
57
|
namePrefix: "firebase-database-emulator",
|
|
29
58
|
},
|
|
30
59
|
},
|
|
31
60
|
firestore: {
|
|
32
|
-
downloadPath: path.join(CACHE_DIR,
|
|
33
|
-
version:
|
|
61
|
+
downloadPath: path.join(CACHE_DIR, `cloud-firestore-emulator-v${EMULATOR_UPDATE_DETAILS.firestore.version}.jar`),
|
|
62
|
+
version: EMULATOR_UPDATE_DETAILS.firestore.version,
|
|
34
63
|
opts: {
|
|
35
64
|
cacheDir: CACHE_DIR,
|
|
36
|
-
remoteUrl:
|
|
37
|
-
expectedSize:
|
|
38
|
-
expectedChecksum:
|
|
65
|
+
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v${EMULATOR_UPDATE_DETAILS.firestore.version}.jar`,
|
|
66
|
+
expectedSize: EMULATOR_UPDATE_DETAILS.firestore.expectedSize,
|
|
67
|
+
expectedChecksum: EMULATOR_UPDATE_DETAILS.firestore.expectedChecksum,
|
|
39
68
|
namePrefix: "cloud-firestore-emulator",
|
|
40
69
|
},
|
|
41
70
|
},
|
|
42
71
|
storage: {
|
|
43
|
-
downloadPath: path.join(CACHE_DIR,
|
|
44
|
-
version:
|
|
72
|
+
downloadPath: path.join(CACHE_DIR, `cloud-storage-rules-runtime-v${EMULATOR_UPDATE_DETAILS.storage.version}.jar`),
|
|
73
|
+
version: EMULATOR_UPDATE_DETAILS.storage.version,
|
|
45
74
|
opts: {
|
|
46
75
|
cacheDir: CACHE_DIR,
|
|
47
|
-
remoteUrl:
|
|
48
|
-
expectedSize:
|
|
49
|
-
expectedChecksum:
|
|
76
|
+
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v${EMULATOR_UPDATE_DETAILS.storage.version}.jar`,
|
|
77
|
+
expectedSize: EMULATOR_UPDATE_DETAILS.storage.expectedSize,
|
|
78
|
+
expectedChecksum: EMULATOR_UPDATE_DETAILS.storage.expectedChecksum,
|
|
50
79
|
namePrefix: "cloud-storage-rules-emulator",
|
|
51
80
|
},
|
|
52
81
|
},
|
|
53
|
-
ui:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
namePrefix: "ui",
|
|
67
|
-
},
|
|
68
|
-
}
|
|
69
|
-
: {
|
|
70
|
-
version: "1.11.1",
|
|
71
|
-
downloadPath: path.join(CACHE_DIR, "ui-v1.11.1.zip"),
|
|
72
|
-
unzipDir: path.join(CACHE_DIR, "ui-v1.11.1"),
|
|
73
|
-
binaryPath: path.join(CACHE_DIR, "ui-v1.11.1", "server", "server.js"),
|
|
74
|
-
opts: {
|
|
75
|
-
cacheDir: CACHE_DIR,
|
|
76
|
-
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.11.1.zip",
|
|
77
|
-
expectedSize: 3061713,
|
|
78
|
-
expectedChecksum: "a4944414518be206280b495f526f18bf",
|
|
79
|
-
namePrefix: "ui",
|
|
80
|
-
},
|
|
82
|
+
ui: {
|
|
83
|
+
version: EMULATOR_UPDATE_DETAILS.ui.version,
|
|
84
|
+
downloadPath: path.join(CACHE_DIR, `ui-v${EMULATOR_UPDATE_DETAILS.ui.version}.zip`),
|
|
85
|
+
unzipDir: path.join(CACHE_DIR, `ui-v${EMULATOR_UPDATE_DETAILS.ui.version}`),
|
|
86
|
+
binaryPath: path.join(CACHE_DIR, `ui-v${EMULATOR_UPDATE_DETAILS.ui.version}`, "server", "server.js"),
|
|
87
|
+
opts: {
|
|
88
|
+
cacheDir: CACHE_DIR,
|
|
89
|
+
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v${EMULATOR_UPDATE_DETAILS.ui.version}.zip`,
|
|
90
|
+
expectedSize: EMULATOR_UPDATE_DETAILS.ui.expectedSize,
|
|
91
|
+
expectedChecksum: EMULATOR_UPDATE_DETAILS.ui.expectedChecksum,
|
|
92
|
+
skipCache: experiments.isEnabled("emulatoruisnapshot"),
|
|
93
|
+
skipChecksumAndSize: experiments.isEnabled("emulatoruisnapshot"),
|
|
94
|
+
namePrefix: "ui",
|
|
81
95
|
},
|
|
96
|
+
},
|
|
82
97
|
pubsub: {
|
|
83
|
-
downloadPath: path.join(CACHE_DIR,
|
|
84
|
-
version:
|
|
85
|
-
unzipDir: path.join(CACHE_DIR,
|
|
86
|
-
binaryPath: path.join(CACHE_DIR,
|
|
98
|
+
downloadPath: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}.zip`),
|
|
99
|
+
version: EMULATOR_UPDATE_DETAILS.pubsub.version,
|
|
100
|
+
unzipDir: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`),
|
|
101
|
+
binaryPath: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`, `pubsub-emulator/bin/cloud-pubsub-emulator${process.platform === "win32" ? ".bat" : ""}`),
|
|
87
102
|
opts: {
|
|
88
103
|
cacheDir: CACHE_DIR,
|
|
89
|
-
remoteUrl:
|
|
90
|
-
expectedSize:
|
|
91
|
-
expectedChecksum:
|
|
104
|
+
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}.zip`,
|
|
105
|
+
expectedSize: EMULATOR_UPDATE_DETAILS.pubsub.expectedSize,
|
|
106
|
+
expectedChecksum: EMULATOR_UPDATE_DETAILS.pubsub.expectedChecksum,
|
|
92
107
|
namePrefix: "pubsub-emulator",
|
|
93
108
|
},
|
|
94
109
|
},
|