firebase-tools 11.3.0 → 11.4.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/README.md +8 -15
- package/lib/apiv2.js +5 -0
- package/lib/checkValidTargetFilters.js +3 -2
- package/lib/command.js +1 -0
- package/lib/commands/hosting-clone.js +5 -0
- package/lib/commands/login-ci.js +2 -0
- package/lib/database/rulesConfig.js +35 -8
- package/lib/deploy/functions/backend.js +6 -4
- package/lib/deploy/functions/build.js +107 -95
- package/lib/deploy/functions/ensure.js +1 -1
- package/lib/deploy/functions/params.js +5 -2
- package/lib/deploy/functions/prepare.js +3 -3
- package/lib/deploy/functions/pricing.js +3 -2
- package/lib/deploy/functions/prompts.js +1 -1
- package/lib/deploy/functions/release/fabricator.js +8 -7
- package/lib/deploy/functions/release/index.js +4 -0
- package/lib/deploy/functions/runtimes/discovery/parsing.js +19 -8
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +115 -107
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +53 -21
- package/lib/deploy/functions/services/storage.js +6 -0
- package/lib/deploy/hosting/convertConfig.js +87 -16
- package/lib/deploy/hosting/deploy.js +1 -1
- package/lib/deploy/index.js +1 -1
- package/lib/deploy/storage/prepare.js +29 -6
- package/lib/emulator/controller.js +0 -1
- package/lib/emulator/functionsEmulator.js +3 -0
- package/lib/emulator/functionsEmulatorRuntime.js +1 -1
- package/lib/emulator/functionsEmulatorShared.js +6 -11
- package/lib/emulator/storage/files.js +19 -22
- package/lib/emulator/storage/metadata.js +6 -6
- package/lib/emulator/storage/persistence.js +26 -12
- package/lib/extensions/displayExtensionInfo.js +1 -101
- package/lib/extensions/emulator/triggerHelper.js +2 -2
- package/lib/extensions/updateHelper.js +1 -7
- package/lib/functional.js +16 -1
- package/lib/functions/env.js +47 -2
- package/lib/gcp/cloudfunctions.js +21 -8
- package/lib/gcp/cloudfunctionsv2.js +43 -19
- package/lib/gcp/cloudscheduler.js +25 -13
- package/lib/gcp/cloudtasks.js +4 -3
- package/lib/gcp/iam.js +20 -17
- package/lib/gcp/proto.js +18 -6
- package/lib/gcp/resourceManager.js +25 -3
- package/lib/index.js +1 -1
- package/lib/previews.js +1 -1
- package/lib/rc.js +3 -9
- package/lib/requireAuth.js +4 -0
- package/lib/rulesDeploy.js +40 -3
- package/npm-shrinkwrap.json +48 -37
- package/package.json +7 -4
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.convertConfig = void 0;
|
|
4
4
|
const error_1 = require("../../error");
|
|
5
5
|
const backend_1 = require("../functions/backend");
|
|
6
|
+
const backend = require("../functions/backend");
|
|
7
|
+
const utils_1 = require("../../utils");
|
|
6
8
|
function has(obj, k) {
|
|
7
9
|
return obj[k] !== undefined;
|
|
8
10
|
}
|
|
@@ -39,24 +41,73 @@ async function convertConfig(context, payload, config, finalize) {
|
|
|
39
41
|
if (!config) {
|
|
40
42
|
return out;
|
|
41
43
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
+
const endpointFromBackend = (targetBackend, functionsEndpointInfo) => {
|
|
45
|
+
const backendsForId = backend.allEndpoints(targetBackend).filter((endpoint) => {
|
|
46
|
+
return endpoint.id === functionsEndpointInfo.serviceId;
|
|
47
|
+
});
|
|
48
|
+
const matchingBackends = backendsForId.filter((endpoint) => {
|
|
49
|
+
return ((!functionsEndpointInfo.region || endpoint.region === functionsEndpointInfo.region) &&
|
|
50
|
+
(!functionsEndpointInfo.platform || endpoint.platform === functionsEndpointInfo.platform));
|
|
51
|
+
});
|
|
52
|
+
if (matchingBackends.length > 1) {
|
|
53
|
+
for (const endpoint of matchingBackends) {
|
|
54
|
+
if (endpoint.region === "us-central1") {
|
|
55
|
+
(0, utils_1.logLabeledBullet)(`hosting[${config.site}]`, `Function \`${functionsEndpointInfo.serviceId}\` found in multiple regions, defaulting to \`us-central1\`. ` +
|
|
56
|
+
`To rewrite to a different region, specify a \`region\` for the rewrite in \`firebase.json\`.`);
|
|
57
|
+
return endpoint;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
throw new error_1.FirebaseError(`More than one backend found for function name: ${functionsEndpointInfo.serviceId}. If the function is deployed in multiple regions, you must specify a region.`);
|
|
61
|
+
}
|
|
62
|
+
if (matchingBackends.length === 1) {
|
|
63
|
+
const endpoint = matchingBackends[0];
|
|
64
|
+
if (endpoint && ((0, backend_1.isHttpsTriggered)(endpoint) || (0, backend_1.isCallableTriggered)(endpoint))) {
|
|
65
|
+
return endpoint;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
};
|
|
70
|
+
const endpointBeingDeployed = (functionsEndpointInfo) => {
|
|
44
71
|
for (const { wantBackend } of Object.values(payload.functions || {})) {
|
|
45
|
-
|
|
46
|
-
|
|
72
|
+
if (!wantBackend) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const endpoint = endpointFromBackend(wantBackend, functionsEndpointInfo);
|
|
76
|
+
if (endpoint) {
|
|
47
77
|
return endpoint;
|
|
78
|
+
}
|
|
48
79
|
}
|
|
49
|
-
return
|
|
80
|
+
return;
|
|
50
81
|
};
|
|
51
|
-
const matchingEndpoint = async (
|
|
52
|
-
const pendingEndpoint = endpointBeingDeployed(
|
|
82
|
+
const matchingEndpoint = async (functionsEndpointInfo) => {
|
|
83
|
+
const pendingEndpoint = endpointBeingDeployed(functionsEndpointInfo);
|
|
53
84
|
if (pendingEndpoint)
|
|
54
85
|
return pendingEndpoint;
|
|
55
86
|
const backend = await (0, backend_1.existingBackend)(context);
|
|
56
87
|
return (0, backend_1.allEndpoints)(backend).find((it) => (0, backend_1.isHttpsTriggered)(it) &&
|
|
57
|
-
it.
|
|
58
|
-
it.
|
|
59
|
-
it.region === region);
|
|
88
|
+
it.id === functionsEndpointInfo.serviceId &&
|
|
89
|
+
(!functionsEndpointInfo.platform || it.platform === functionsEndpointInfo.platform) &&
|
|
90
|
+
(!functionsEndpointInfo.region || it.region === functionsEndpointInfo.region));
|
|
91
|
+
};
|
|
92
|
+
const findEndpointWithValidRegion = async (rewrite, context) => {
|
|
93
|
+
if ("function" in rewrite) {
|
|
94
|
+
const foundEndpointToBeDeployed = endpointBeingDeployed({
|
|
95
|
+
serviceId: rewrite.function,
|
|
96
|
+
region: rewrite.region,
|
|
97
|
+
});
|
|
98
|
+
if (foundEndpointToBeDeployed) {
|
|
99
|
+
return foundEndpointToBeDeployed;
|
|
100
|
+
}
|
|
101
|
+
const existingBackend = await backend.existingBackend(context);
|
|
102
|
+
const endpointAlreadyDeployed = endpointFromBackend(existingBackend, {
|
|
103
|
+
serviceId: rewrite.function,
|
|
104
|
+
region: rewrite.region,
|
|
105
|
+
});
|
|
106
|
+
if (endpointAlreadyDeployed) {
|
|
107
|
+
return endpointAlreadyDeployed;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
60
111
|
};
|
|
61
112
|
if (Array.isArray(config.rewrites)) {
|
|
62
113
|
out.rewrites = [];
|
|
@@ -66,19 +117,33 @@ async function convertConfig(context, payload, config, finalize) {
|
|
|
66
117
|
vRewrite.path = rewrite.destination;
|
|
67
118
|
}
|
|
68
119
|
else if ("function" in rewrite) {
|
|
69
|
-
if (!finalize &&
|
|
120
|
+
if (!finalize &&
|
|
121
|
+
endpointBeingDeployed({
|
|
122
|
+
serviceId: rewrite.function,
|
|
123
|
+
platform: "gcfv2",
|
|
124
|
+
region: rewrite.region,
|
|
125
|
+
})) {
|
|
70
126
|
continue;
|
|
71
|
-
|
|
127
|
+
}
|
|
128
|
+
const endpoint = await matchingEndpoint({
|
|
129
|
+
serviceId: rewrite.function,
|
|
130
|
+
platform: "gcfv2",
|
|
131
|
+
region: rewrite.region,
|
|
132
|
+
});
|
|
72
133
|
if (endpoint) {
|
|
73
134
|
vRewrite.run = { serviceId: endpoint.id, region: endpoint.region };
|
|
74
135
|
}
|
|
75
136
|
else {
|
|
76
137
|
vRewrite.function = rewrite.function;
|
|
77
|
-
|
|
78
|
-
|
|
138
|
+
const foundEndpoint = await findEndpointWithValidRegion(rewrite, context);
|
|
139
|
+
if (foundEndpoint) {
|
|
140
|
+
vRewrite.functionRegion = foundEndpoint.region;
|
|
79
141
|
}
|
|
80
142
|
else {
|
|
81
|
-
|
|
143
|
+
if (rewrite.region && rewrite.region !== "us-central1") {
|
|
144
|
+
throw new error_1.FirebaseError(`Unable to find a valid endpoint for function \`${vRewrite.function}\``);
|
|
145
|
+
}
|
|
146
|
+
(0, utils_1.logLabeledWarning)(`hosting[${config.site}]`, `Unable to find a valid endpoint for function \`${vRewrite.function}\`, but still including it in the config`);
|
|
82
147
|
}
|
|
83
148
|
}
|
|
84
149
|
}
|
|
@@ -86,8 +151,14 @@ async function convertConfig(context, payload, config, finalize) {
|
|
|
86
151
|
vRewrite.dynamicLinks = rewrite.dynamicLinks;
|
|
87
152
|
}
|
|
88
153
|
else if ("run" in rewrite) {
|
|
89
|
-
if (!finalize &&
|
|
154
|
+
if (!finalize &&
|
|
155
|
+
endpointBeingDeployed({
|
|
156
|
+
serviceId: rewrite.run.serviceId,
|
|
157
|
+
platform: "gcfv2",
|
|
158
|
+
region: rewrite.run.region,
|
|
159
|
+
})) {
|
|
90
160
|
continue;
|
|
161
|
+
}
|
|
91
162
|
vRewrite.run = Object.assign({ region: "us-central1" }, rewrite.run);
|
|
92
163
|
}
|
|
93
164
|
out.rewrites.push(vRewrite);
|
|
@@ -36,7 +36,7 @@ async function deploy(context, options) {
|
|
|
36
36
|
(0, utils_1.logLabeledBullet)(`hosting[${deploy.site}]`, 'no "public" directory to upload, continuing with release');
|
|
37
37
|
return runDeploys(deploys, debugging);
|
|
38
38
|
}
|
|
39
|
-
(0, utils_1.logLabeledBullet)(
|
|
39
|
+
(0, utils_1.logLabeledBullet)(`hosting[${deploy.site}]`, "beginning deploy...");
|
|
40
40
|
const t0 = Date.now();
|
|
41
41
|
const publicDir = options.config.path(deploy.config.public);
|
|
42
42
|
const files = (0, listFiles_1.listFiles)(publicDir, deploy.config.ignore);
|
package/lib/deploy/index.js
CHANGED
|
@@ -52,7 +52,7 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
52
52
|
for (const targetName of targetNames) {
|
|
53
53
|
const target = TARGETS[targetName];
|
|
54
54
|
if (!target) {
|
|
55
|
-
return Promise.reject(new error_1.FirebaseError((0, cli_color_1.bold)(targetName)
|
|
55
|
+
return Promise.reject(new error_1.FirebaseError(`${(0, cli_color_1.bold)(targetName)} is not a valid deploy target`));
|
|
56
56
|
}
|
|
57
57
|
predeploys.push((0, lifecycleHooks_1.lifecycleHooks)(targetName, "predeploy"));
|
|
58
58
|
prepares.push(target.prepare);
|
|
@@ -3,25 +3,48 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const _ = require("lodash");
|
|
4
4
|
const gcp = require("../../gcp");
|
|
5
5
|
const rulesDeploy_1 = require("../../rulesDeploy");
|
|
6
|
+
const error_1 = require("../../error");
|
|
6
7
|
async function default_1(context, options) {
|
|
7
8
|
let rulesConfig = options.config.get("storage");
|
|
8
9
|
if (!rulesConfig) {
|
|
9
10
|
return;
|
|
10
11
|
}
|
|
12
|
+
const onlyTargets = new Set();
|
|
13
|
+
let allStorage = !options.only;
|
|
14
|
+
if (options.only) {
|
|
15
|
+
const split = options.only.split(",");
|
|
16
|
+
if (split.includes("storage")) {
|
|
17
|
+
allStorage = true;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
for (const value of split) {
|
|
21
|
+
if (value.startsWith("storage:")) {
|
|
22
|
+
onlyTargets.add(value.split(":")[1]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
11
27
|
_.set(context, "storage.rules", rulesConfig);
|
|
12
28
|
const rulesDeploy = new rulesDeploy_1.RulesDeploy(options, rulesDeploy_1.RulesetServiceType.FIREBASE_STORAGE);
|
|
13
29
|
_.set(context, "storage.rulesDeploy", rulesDeploy);
|
|
14
|
-
if (
|
|
30
|
+
if (!Array.isArray(rulesConfig)) {
|
|
15
31
|
const defaultBucket = await gcp.storage.getDefaultBucket(options.project);
|
|
16
32
|
rulesConfig = [Object.assign(rulesConfig, { bucket: defaultBucket })];
|
|
17
33
|
_.set(context, "storage.rules", rulesConfig);
|
|
18
34
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
for (const ruleConfig of rulesConfig) {
|
|
36
|
+
const target = ruleConfig.target;
|
|
37
|
+
if (target) {
|
|
38
|
+
options.rc.requireTarget(context.projectId, "storage", target);
|
|
22
39
|
}
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
if (allStorage || onlyTargets.has(target)) {
|
|
41
|
+
rulesDeploy.addFile(ruleConfig.rules);
|
|
42
|
+
onlyTargets.delete(target);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (!allStorage && onlyTargets.size !== 0) {
|
|
46
|
+
throw new error_1.FirebaseError(`Could not find rules for the following storage targets: ${[...onlyTargets].join(", ")}`);
|
|
47
|
+
}
|
|
25
48
|
await rulesDeploy.compile();
|
|
26
49
|
}
|
|
27
50
|
exports.default = default_1;
|
|
@@ -301,7 +301,6 @@ async function startAll(options, showUI = true) {
|
|
|
301
301
|
const extensionsBackends = await extensionEmulator.getExtensionBackends();
|
|
302
302
|
const filteredExtensionsBackends = extensionEmulator.filterUnemulatedTriggers(options, extensionsBackends);
|
|
303
303
|
emulatableBackends.push(...filteredExtensionsBackends);
|
|
304
|
-
void (0, track_1.track)("Emulator Run", types_1.Emulators.EXTENSIONS);
|
|
305
304
|
await startEmulator(extensionEmulator);
|
|
306
305
|
}
|
|
307
306
|
if (emulatableBackends.length) {
|
|
@@ -646,6 +646,9 @@ class FunctionsEmulator {
|
|
|
646
646
|
envs.GCLOUD_PROJECT = this.args.projectId;
|
|
647
647
|
envs.K_REVISION = "1";
|
|
648
648
|
envs.PORT = "80";
|
|
649
|
+
if (trigger === null || trigger === void 0 ? void 0 : trigger.timeoutSeconds) {
|
|
650
|
+
envs.FUNCTIONS_EMULATOR_TIMEOUT_SECONDS = trigger.timeoutSeconds.toString();
|
|
651
|
+
}
|
|
649
652
|
if (trigger) {
|
|
650
653
|
const target = trigger.entryPoint;
|
|
651
654
|
envs.FUNCTION_TARGET = target;
|
|
@@ -537,8 +537,8 @@ async function processHTTPS(trigger) {
|
|
|
537
537
|
rejectEphemeralServer(err);
|
|
538
538
|
}
|
|
539
539
|
});
|
|
540
|
-
logDebug(`Attempting to listen to port: ${process.env.PORT}`);
|
|
541
540
|
server = ephemeralServer.listen(process.env.PORT);
|
|
541
|
+
logDebug(`Listening to port: ${process.env.PORT}`);
|
|
542
542
|
server.on("error", rejectEphemeralServer);
|
|
543
543
|
});
|
|
544
544
|
}
|
|
@@ -8,20 +8,11 @@ const crypto_1 = require("crypto");
|
|
|
8
8
|
const _ = require("lodash");
|
|
9
9
|
const backend = require("../deploy/functions/backend");
|
|
10
10
|
const constants_1 = require("./constants");
|
|
11
|
-
const proto_1 = require("../gcp/proto");
|
|
12
11
|
const manifest_1 = require("../extensions/manifest");
|
|
13
12
|
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
14
13
|
const postinstall_1 = require("./extensions/postinstall");
|
|
15
14
|
const services_1 = require("../deploy/functions/services");
|
|
16
15
|
const prepare_1 = require("../deploy/functions/prepare");
|
|
17
|
-
const memoryLookup = {
|
|
18
|
-
"128MB": 128,
|
|
19
|
-
"256MB": 256,
|
|
20
|
-
"512MB": 512,
|
|
21
|
-
"1GB": 1024,
|
|
22
|
-
"2GB": 2048,
|
|
23
|
-
"4GB": 4096,
|
|
24
|
-
};
|
|
25
16
|
class HttpConstants {
|
|
26
17
|
}
|
|
27
18
|
exports.HttpConstants = HttpConstants;
|
|
@@ -33,7 +24,7 @@ class EmulatedTrigger {
|
|
|
33
24
|
this.module = module;
|
|
34
25
|
}
|
|
35
26
|
get memoryLimitBytes() {
|
|
36
|
-
return
|
|
27
|
+
return (this.definition.availableMemoryMb || 128) * 1024 * 1024;
|
|
37
28
|
}
|
|
38
29
|
get timeoutMs() {
|
|
39
30
|
return (this.definition.timeoutSeconds || 60) * 1000;
|
|
@@ -69,7 +60,11 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
69
60
|
id: `${endpoint.region}-${endpoint.id}`,
|
|
70
61
|
codebase: endpoint.codebase,
|
|
71
62
|
};
|
|
72
|
-
|
|
63
|
+
def.availableMemoryMb = endpoint.availableMemoryMb || 256;
|
|
64
|
+
def.labels = endpoint.labels || {};
|
|
65
|
+
def.timeoutSeconds = endpoint.timeoutSeconds || 60;
|
|
66
|
+
def.secretEnvironmentVariables = endpoint.secretEnvironmentVariables || [];
|
|
67
|
+
def.platform = endpoint.platform;
|
|
73
68
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
74
69
|
def.httpsTrigger = endpoint.httpsTrigger;
|
|
75
70
|
}
|
|
@@ -18,9 +18,8 @@ const adminSdkConfig_1 = require("../adminSdkConfig");
|
|
|
18
18
|
const types_1 = require("./rules/types");
|
|
19
19
|
const upload_1 = require("./upload");
|
|
20
20
|
class StoredFile {
|
|
21
|
-
constructor(metadata
|
|
21
|
+
constructor(metadata) {
|
|
22
22
|
this.metadata = metadata;
|
|
23
|
-
this._path = path;
|
|
24
23
|
}
|
|
25
24
|
get metadata() {
|
|
26
25
|
return this._metadata;
|
|
@@ -28,12 +27,6 @@ class StoredFile {
|
|
|
28
27
|
set metadata(value) {
|
|
29
28
|
this._metadata = value;
|
|
30
29
|
}
|
|
31
|
-
get path() {
|
|
32
|
-
return this._path;
|
|
33
|
-
}
|
|
34
|
-
set path(value) {
|
|
35
|
-
this._path = value;
|
|
36
|
-
}
|
|
37
30
|
}
|
|
38
31
|
exports.StoredFile = StoredFile;
|
|
39
32
|
class StorageLayer {
|
|
@@ -163,7 +156,7 @@ class StorageLayer {
|
|
|
163
156
|
}
|
|
164
157
|
this._persistence.deleteFile(filePath, true);
|
|
165
158
|
this._persistence.renameFile(upload.path, filePath);
|
|
166
|
-
this._files.set(filePath, new StoredFile(metadata
|
|
159
|
+
this._files.set(filePath, new StoredFile(metadata));
|
|
167
160
|
this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(metadata));
|
|
168
161
|
return metadata;
|
|
169
162
|
}
|
|
@@ -202,7 +195,7 @@ class StorageLayer {
|
|
|
202
195
|
cacheControl: newMetadata.cacheControl,
|
|
203
196
|
customMetadata: newMetadata.metadata,
|
|
204
197
|
}, this._cloudFunctions, sourceBytes, incomingMetadata);
|
|
205
|
-
const file = new StoredFile(copiedFileMetadata
|
|
198
|
+
const file = new StoredFile(copiedFileMetadata);
|
|
206
199
|
this._files.set(destinationFilePath, file);
|
|
207
200
|
this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(file.metadata));
|
|
208
201
|
return file.metadata;
|
|
@@ -306,13 +299,14 @@ class StorageLayer {
|
|
|
306
299
|
await fse.writeFile(bucketsFilePath, JSON.stringify(bucketsList, undefined, 2));
|
|
307
300
|
const blobsDirPath = path.join(storageExportPath, "blobs");
|
|
308
301
|
await fse.ensureDir(blobsDirPath);
|
|
309
|
-
await fse.copy(this.dirPath, blobsDirPath, { recursive: true });
|
|
310
302
|
const metadataDirPath = path.join(storageExportPath, "metadata");
|
|
311
303
|
await fse.ensureDir(metadataDirPath);
|
|
312
304
|
try {
|
|
313
305
|
for (var _b = __asyncValues(this._files.entries()), _c; _c = await _b.next(), !_c.done;) {
|
|
314
|
-
const [
|
|
315
|
-
const
|
|
306
|
+
const [, file] = _c.value;
|
|
307
|
+
const diskFileName = this._persistence.getDiskFileName(this.path(file.metadata.bucket, file.metadata.name));
|
|
308
|
+
await fse.copy(path.join(this.dirPath, diskFileName), path.join(blobsDirPath, diskFileName));
|
|
309
|
+
const metadataExportPath = path.join(metadataDirPath, encodeURIComponent(diskFileName)) + ".json";
|
|
316
310
|
await fse.writeFile(metadataExportPath, metadata_1.StoredFileMetadata.toJSON(file.metadata));
|
|
317
311
|
}
|
|
318
312
|
}
|
|
@@ -333,6 +327,10 @@ class StorageLayer {
|
|
|
333
327
|
}
|
|
334
328
|
const metadataDir = path.join(storageExportPath, "metadata");
|
|
335
329
|
const blobsDir = path.join(storageExportPath, "blobs");
|
|
330
|
+
if (!(0, fs_1.existsSync)(metadataDir) || !(0, fs_1.existsSync)(blobsDir)) {
|
|
331
|
+
logger_1.logger.warn(`Could not find metadata directory at "${metadataDir}" and/or blobs directory at "${blobsDir}".`);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
336
334
|
const metadataList = this.walkDirSync(metadataDir);
|
|
337
335
|
const dotJson = ".json";
|
|
338
336
|
for (const f of metadataList) {
|
|
@@ -348,15 +346,14 @@ class StorageLayer {
|
|
|
348
346
|
logger_1.logger.warn(`Could not find file "${blobPath}" in storage export.`);
|
|
349
347
|
continue;
|
|
350
348
|
}
|
|
351
|
-
let
|
|
352
|
-
const
|
|
353
|
-
if (
|
|
354
|
-
|
|
349
|
+
let fileName = metadata.name;
|
|
350
|
+
const objectNameSep = getPathSep(fileName);
|
|
351
|
+
if (fileName !== path.sep) {
|
|
352
|
+
fileName = fileName.split(objectNameSep).join(path.sep);
|
|
355
353
|
}
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
this._files.set(
|
|
359
|
-
fse.copyFileSync(blobAbsPath, blobDiskPath);
|
|
354
|
+
const filepath = this.path(metadata.bucket, fileName);
|
|
355
|
+
this._persistence.copyFromExternalPath(blobAbsPath, filepath);
|
|
356
|
+
this._files.set(filepath, new StoredFile(metadata));
|
|
360
357
|
}
|
|
361
358
|
}
|
|
362
359
|
*walkDirSync(dir) {
|
|
@@ -374,6 +371,6 @@ class StorageLayer {
|
|
|
374
371
|
}
|
|
375
372
|
exports.StorageLayer = StorageLayer;
|
|
376
373
|
function getPathSep(decodedPath) {
|
|
377
|
-
const firstSepIndex = decodedPath.search(/[
|
|
374
|
+
const firstSepIndex = decodedPath.search(/[\/|\\\\]/g);
|
|
378
375
|
return decodedPath[firstSepIndex];
|
|
379
376
|
}
|
|
@@ -287,15 +287,15 @@ class CloudStorageObjectMetadata {
|
|
|
287
287
|
}
|
|
288
288
|
exports.CloudStorageObjectMetadata = CloudStorageObjectMetadata;
|
|
289
289
|
function toSerializedDate(d) {
|
|
290
|
-
const day = `${d.
|
|
291
|
-
.
|
|
290
|
+
const day = `${d.getUTCFullYear()}-${(d.getUTCMonth() + 1).toString().padStart(2, "0")}-${d
|
|
291
|
+
.getUTCDate()
|
|
292
292
|
.toString()
|
|
293
293
|
.padStart(2, "0")}`;
|
|
294
|
-
const time = `${d.
|
|
295
|
-
.
|
|
294
|
+
const time = `${d.getUTCHours().toString().padStart(2, "0")}:${d
|
|
295
|
+
.getUTCMinutes()
|
|
296
296
|
.toString()
|
|
297
|
-
.padStart(2, "0")}:${d.
|
|
298
|
-
.
|
|
297
|
+
.padStart(2, "0")}:${d.getUTCSeconds().toString().padStart(2, "0")}.${d
|
|
298
|
+
.getUTCMilliseconds()
|
|
299
299
|
.toString()
|
|
300
300
|
.padStart(3, "0")}`;
|
|
301
301
|
return `${day}T${time}Z`;
|
|
@@ -4,9 +4,12 @@ exports.Persistence = void 0;
|
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
5
|
const rimraf = require("rimraf");
|
|
6
6
|
const fs = require("fs");
|
|
7
|
+
const fse = require("fs-extra");
|
|
7
8
|
const path = require("path");
|
|
9
|
+
const uuid = require("uuid");
|
|
8
10
|
class Persistence {
|
|
9
11
|
constructor(dirPath) {
|
|
12
|
+
this._diskPathMap = new Map();
|
|
10
13
|
this.reset(dirPath);
|
|
11
14
|
}
|
|
12
15
|
reset(dirPath) {
|
|
@@ -14,22 +17,18 @@ class Persistence {
|
|
|
14
17
|
(0, fs_1.mkdirSync)(dirPath, {
|
|
15
18
|
recursive: true,
|
|
16
19
|
});
|
|
20
|
+
this._diskPathMap = new Map();
|
|
17
21
|
}
|
|
18
22
|
get dirPath() {
|
|
19
23
|
return this._dirPath;
|
|
20
24
|
}
|
|
21
25
|
appendBytes(fileName, bytes) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
fs.appendFileSync(filepath, bytes);
|
|
26
|
-
return filepath;
|
|
27
|
-
}
|
|
28
|
-
finally {
|
|
29
|
-
if (fd) {
|
|
30
|
-
(0, fs_1.closeSync)(fd);
|
|
31
|
-
}
|
|
26
|
+
if (!this._diskPathMap.has(fileName)) {
|
|
27
|
+
this._diskPathMap.set(fileName, this.generateNewDiskName());
|
|
32
28
|
}
|
|
29
|
+
const filepath = this.getDiskPath(fileName);
|
|
30
|
+
fs.appendFileSync(filepath, bytes);
|
|
31
|
+
return filepath;
|
|
33
32
|
}
|
|
34
33
|
readBytes(fileName, size, fileOffset) {
|
|
35
34
|
let fd;
|
|
@@ -55,6 +54,7 @@ class Persistence {
|
|
|
55
54
|
throw err;
|
|
56
55
|
}
|
|
57
56
|
}
|
|
57
|
+
this._diskPathMap.delete(fileName);
|
|
58
58
|
}
|
|
59
59
|
deleteAll() {
|
|
60
60
|
return new Promise((resolve, reject) => {
|
|
@@ -63,16 +63,30 @@ class Persistence {
|
|
|
63
63
|
reject(err);
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
|
+
this._diskPathMap = new Map();
|
|
66
67
|
resolve();
|
|
67
68
|
}
|
|
68
69
|
});
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
72
|
renameFile(oldName, newName) {
|
|
72
|
-
|
|
73
|
+
const oldNameId = this.getDiskFileName(oldName);
|
|
74
|
+
this._diskPathMap.set(newName, oldNameId);
|
|
75
|
+
this._diskPathMap.delete(oldName);
|
|
73
76
|
}
|
|
74
77
|
getDiskPath(fileName) {
|
|
75
|
-
|
|
78
|
+
const shortenedDiskPath = this.getDiskFileName(fileName);
|
|
79
|
+
return path.join(this._dirPath, encodeURIComponent(shortenedDiskPath));
|
|
80
|
+
}
|
|
81
|
+
getDiskFileName(fileName) {
|
|
82
|
+
return this._diskPathMap.get(fileName);
|
|
83
|
+
}
|
|
84
|
+
copyFromExternalPath(sourcePath, newName) {
|
|
85
|
+
this._diskPathMap.set(newName, this.generateNewDiskName());
|
|
86
|
+
fse.copyFileSync(sourcePath, this.getDiskPath(newName));
|
|
87
|
+
}
|
|
88
|
+
generateNewDiskName() {
|
|
89
|
+
return uuid.v4();
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
92
|
exports.Persistence = Persistence;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.printSourceDownloadLink = exports.
|
|
4
|
-
const _ = require("lodash");
|
|
3
|
+
exports.printSourceDownloadLink = exports.displayExtInfo = void 0;
|
|
5
4
|
const clc = require("cli-color");
|
|
6
5
|
const { marked } = require("marked");
|
|
7
6
|
const TerminalRenderer = require("marked-terminal");
|
|
@@ -12,8 +11,6 @@ const error_1 = require("../error");
|
|
|
12
11
|
marked.setOptions({
|
|
13
12
|
renderer: new TerminalRenderer(),
|
|
14
13
|
});
|
|
15
|
-
const additionColor = clc.green;
|
|
16
|
-
const deletionColor = clc.red;
|
|
17
14
|
function displayExtInfo(extensionName, publisher, spec, published = false) {
|
|
18
15
|
const lines = [];
|
|
19
16
|
lines.push(`**Name**: ${spec.displayName}`);
|
|
@@ -46,103 +43,6 @@ function displayExtInfo(extensionName, publisher, spec, published = false) {
|
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
exports.displayExtInfo = displayExtInfo;
|
|
49
|
-
function displayUpdateChangesNoInput(spec, newSpec) {
|
|
50
|
-
var _a, _b, _c, _d;
|
|
51
|
-
const lines = [];
|
|
52
|
-
if (spec.displayName !== newSpec.displayName) {
|
|
53
|
-
lines.push("", "**Name:**", deletionColor(`- ${spec.displayName}`), additionColor(`+ ${newSpec.displayName}`));
|
|
54
|
-
}
|
|
55
|
-
if (((_a = spec.author) === null || _a === void 0 ? void 0 : _a.authorName) !== ((_b = newSpec.author) === null || _b === void 0 ? void 0 : _b.authorName)) {
|
|
56
|
-
lines.push("", "**Author:**", deletionColor(`- ${(_c = spec.author) === null || _c === void 0 ? void 0 : _c.authorName}`), additionColor(`+ ${(_d = spec.author) === null || _d === void 0 ? void 0 : _d.authorName}`));
|
|
57
|
-
}
|
|
58
|
-
if (spec.description !== newSpec.description) {
|
|
59
|
-
lines.push("", "**Description:**", deletionColor(`- ${spec.description}`), additionColor(`+ ${newSpec.description}`));
|
|
60
|
-
}
|
|
61
|
-
if (spec.sourceUrl !== newSpec.sourceUrl) {
|
|
62
|
-
lines.push("", "**Source code:**", deletionColor(`- ${spec.sourceUrl}`), additionColor(`+ ${newSpec.sourceUrl}`));
|
|
63
|
-
}
|
|
64
|
-
if (spec.billingRequired && !newSpec.billingRequired) {
|
|
65
|
-
lines.push("", "**Billing is no longer required for this extension.**");
|
|
66
|
-
}
|
|
67
|
-
logger_1.logger.info(marked(lines.join("\n")));
|
|
68
|
-
return lines;
|
|
69
|
-
}
|
|
70
|
-
exports.displayUpdateChangesNoInput = displayUpdateChangesNoInput;
|
|
71
|
-
async function displayUpdateChangesRequiringConfirmation(args) {
|
|
72
|
-
const equals = (a, b) => {
|
|
73
|
-
return _.isEqual(a, b);
|
|
74
|
-
};
|
|
75
|
-
if (args.spec.license !== args.newSpec.license) {
|
|
76
|
-
const message = "\n" +
|
|
77
|
-
"**License**\n" +
|
|
78
|
-
deletionColor(args.spec.license ? `- ${args.spec.license}\n` : "- None\n") +
|
|
79
|
-
additionColor(args.newSpec.license ? `+ ${args.newSpec.license}\n` : "+ None\n");
|
|
80
|
-
logger_1.logger.info(message);
|
|
81
|
-
if (!(await (0, extensionsHelper_1.confirm)({ nonInteractive: args.nonInteractive, force: args.force, default: true }))) {
|
|
82
|
-
throw new error_1.FirebaseError("Unable to update this extension instance without explicit consent for the change to 'License'.");
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
const apisDiffDeletions = _.differenceWith(args.spec.apis, _.get(args.newSpec, "apis", []), equals);
|
|
86
|
-
const apisDiffAdditions = _.differenceWith(args.newSpec.apis, _.get(args.spec, "apis", []), equals);
|
|
87
|
-
if (apisDiffDeletions.length || apisDiffAdditions.length) {
|
|
88
|
-
let message = "\n**APIs:**\n";
|
|
89
|
-
apisDiffDeletions.forEach((api) => {
|
|
90
|
-
message += deletionColor(`- ${api.apiName} (${api.reason})\n`);
|
|
91
|
-
});
|
|
92
|
-
apisDiffAdditions.forEach((api) => {
|
|
93
|
-
message += additionColor(`+ ${api.apiName} (${api.reason})\n`);
|
|
94
|
-
});
|
|
95
|
-
logger_1.logger.info(message);
|
|
96
|
-
if (!(await (0, extensionsHelper_1.confirm)({ nonInteractive: args.nonInteractive, force: args.force, default: true }))) {
|
|
97
|
-
throw new error_1.FirebaseError("Unable to update this extension instance without explicit consent for the change to 'APIs'.");
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const resourcesDiffDeletions = _.differenceWith(args.spec.resources, _.get(args.newSpec, "resources", []), compareResources);
|
|
101
|
-
const resourcesDiffAdditions = _.differenceWith(args.newSpec.resources, _.get(args.spec, "resources", []), compareResources);
|
|
102
|
-
if (resourcesDiffDeletions.length || resourcesDiffAdditions.length) {
|
|
103
|
-
let message = "\n**Resources:**\n";
|
|
104
|
-
resourcesDiffDeletions.forEach((resource) => {
|
|
105
|
-
message += deletionColor(` - ${getResourceReadableName(resource)}`);
|
|
106
|
-
});
|
|
107
|
-
resourcesDiffAdditions.forEach((resource) => {
|
|
108
|
-
message += additionColor(`+ ${getResourceReadableName(resource)}`);
|
|
109
|
-
});
|
|
110
|
-
logger_1.logger.info(message);
|
|
111
|
-
if (!(await (0, extensionsHelper_1.confirm)({ nonInteractive: args.nonInteractive, force: args.force, default: true }))) {
|
|
112
|
-
throw new error_1.FirebaseError("Unable to update this extension instance without explicit consent for the change to 'Resources'.");
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
const rolesDiffDeletions = _.differenceWith(args.spec.roles, _.get(args.newSpec, "roles", []), equals);
|
|
116
|
-
const rolesDiffAdditions = _.differenceWith(args.newSpec.roles, _.get(args.spec, "roles", []), equals);
|
|
117
|
-
if (rolesDiffDeletions.length || rolesDiffAdditions.length) {
|
|
118
|
-
let message = "\n**Permissions:**\n";
|
|
119
|
-
rolesDiffDeletions.forEach((role) => {
|
|
120
|
-
message += deletionColor(`- ${role.role} (${role.reason})\n`);
|
|
121
|
-
});
|
|
122
|
-
rolesDiffAdditions.forEach((role) => {
|
|
123
|
-
message += additionColor(`+ ${role.role} (${role.reason})\n`);
|
|
124
|
-
});
|
|
125
|
-
logger_1.logger.info(message);
|
|
126
|
-
if (!(await (0, extensionsHelper_1.confirm)({ nonInteractive: args.nonInteractive, force: args.force, default: true }))) {
|
|
127
|
-
throw new error_1.FirebaseError("Unable to update this extension instance without explicit consent for the change to 'Permissions'.");
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (!args.spec.billingRequired && args.newSpec.billingRequired) {
|
|
131
|
-
logger_1.logger.info("Billing is now required for the new version of this extension.");
|
|
132
|
-
if (!(await (0, extensionsHelper_1.confirm)({ nonInteractive: args.nonInteractive, force: args.force, default: true }))) {
|
|
133
|
-
throw new error_1.FirebaseError("Unable to update this extension instance without explicit consent for the change to 'BillingRequired'.");
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
exports.displayUpdateChangesRequiringConfirmation = displayUpdateChangesRequiringConfirmation;
|
|
138
|
-
function compareResources(resource1, resource2) {
|
|
139
|
-
return resource1.name === resource2.name && resource1.type === resource2.type;
|
|
140
|
-
}
|
|
141
|
-
function getResourceReadableName(resource) {
|
|
142
|
-
return resource.type === "firebaseextensions.v1beta.function"
|
|
143
|
-
? `${resource.name} (Cloud Function): ${resource.description}\n`
|
|
144
|
-
: `${resource.name} (${resource.type})\n`;
|
|
145
|
-
}
|
|
146
46
|
function printSourceDownloadLink(sourceDownloadUri) {
|
|
147
47
|
const sourceDownloadMsg = `Want to review the source code that will be installed? Download it here: ${sourceDownloadUri}`;
|
|
148
48
|
utils.logBullet(marked(sourceDownloadMsg));
|
|
@@ -12,8 +12,8 @@ function functionResourceToEmulatedTriggerDefintion(resource) {
|
|
|
12
12
|
platform: "gcfv1",
|
|
13
13
|
};
|
|
14
14
|
const properties = resource.properties || {};
|
|
15
|
-
proto.
|
|
16
|
-
proto.
|
|
15
|
+
proto.convertIfPresent(etd, properties, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
16
|
+
proto.convertIfPresent(etd, properties, "regions", "location", (str) => [str]);
|
|
17
17
|
proto.copyIfPresent(etd, properties, "availableMemoryMb");
|
|
18
18
|
if (properties.httpsTrigger) {
|
|
19
19
|
etd.httpsTrigger = properties.httpsTrigger;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.inferUpdateSource = exports.updateFromPublisherSource = exports.updateToVersionFromPublisherSource = exports.updateFromUrlSource = exports.updateFromLocalSource = exports.update = exports.
|
|
3
|
+
exports.inferUpdateSource = exports.updateFromPublisherSource = exports.updateToVersionFromPublisherSource = exports.updateFromUrlSource = exports.updateFromLocalSource = exports.update = exports.warningUpdateToOtherSource = exports.getExistingSourceOrigin = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const semver = require("semver");
|
|
6
6
|
const { marked } = require("marked");
|
|
@@ -52,12 +52,6 @@ function warningUpdateToOtherSource(sourceOrigin) {
|
|
|
52
52
|
logger_1.logger.info(marked(warning));
|
|
53
53
|
}
|
|
54
54
|
exports.warningUpdateToOtherSource = warningUpdateToOtherSource;
|
|
55
|
-
async function displayChanges(args) {
|
|
56
|
-
utils.logLabeledBullet("extensions", "This update contains the following changes:");
|
|
57
|
-
(0, displayExtensionInfo_1.displayUpdateChangesNoInput)(args.spec, args.newSpec);
|
|
58
|
-
await (0, displayExtensionInfo_1.displayUpdateChangesRequiringConfirmation)(args);
|
|
59
|
-
}
|
|
60
|
-
exports.displayChanges = displayChanges;
|
|
61
55
|
async function update(updateOptions) {
|
|
62
56
|
const { projectId, instanceId, source, extRef, params, canEmitEvents, allowedEventTypes, eventarcChannel, } = updateOptions;
|
|
63
57
|
if (extRef) {
|