firebase-tools 10.3.1 → 10.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/lib/accountExporter.js +95 -84
- package/lib/commands/deploy.js +1 -1
- package/lib/commands/experimental-functions-shell.js +1 -1
- package/lib/commands/ext-configure.js +7 -6
- package/lib/commands/ext-export.js +7 -1
- package/lib/commands/ext-install.js +7 -6
- package/lib/commands/ext-update.js +4 -3
- package/lib/commands/functions-config-export.js +5 -3
- package/lib/commands/functions-shell.js +1 -1
- package/lib/commands/hosting-channel-create.js +2 -2
- package/lib/commands/hosting-channel-delete.js +2 -2
- package/lib/commands/hosting-channel-deploy.js +2 -2
- package/lib/commands/hosting-channel-list.js +2 -2
- package/lib/commands/hosting-channel-open.js +2 -2
- package/lib/commands/hosting-sites-delete.js +2 -2
- package/lib/commands/serve.js +1 -1
- package/lib/commands/target-apply.js +2 -2
- package/lib/commands/target-clear.js +2 -2
- package/lib/commands/target-remove.js +2 -2
- package/lib/commands/target.js +2 -2
- package/lib/config.js +14 -4
- package/lib/deploy/extensions/planner.js +9 -3
- package/lib/deploy/functions/checkIam.js +44 -1
- package/lib/deploy/functions/deploy.js +3 -7
- package/lib/deploy/functions/prepare.js +7 -5
- package/lib/deploy/functions/prepareFunctionsUpload.js +7 -13
- package/lib/deploy/functions/release/fabricator.js +13 -1
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/services/firebaseAlerts.js +1 -17
- package/lib/deploy/functions/services/index.js +2 -1
- package/lib/deploy/hosting/deploy.js +10 -0
- package/lib/emulator/auth/apiSpec.js +37 -0
- package/lib/emulator/commandUtils.js +2 -2
- package/lib/emulator/controller.js +14 -8
- package/lib/emulator/downloadableEmulators.js +5 -5
- package/lib/emulator/extensionsEmulator.js +3 -0
- package/lib/emulator/functionsEmulator.js +8 -18
- package/lib/emulator/functionsEmulatorShared.js +31 -1
- package/lib/emulator/storage/apis/firebase.js +4 -6
- package/lib/emulator/storage/files.js +5 -5
- package/lib/emulator/storage/index.js +6 -9
- package/lib/emulator/storage/rules/config.js +6 -5
- package/lib/emulator/storage/rules/manager.js +49 -32
- package/lib/emulator/storage/rules/runtime.js +4 -0
- package/lib/emulator/storage/rules/utils.js +2 -2
- package/lib/emulator/storage/server.js +1 -1
- package/lib/extensions/askUserForParam.js +87 -16
- package/lib/extensions/extensionsHelper.js +11 -2
- package/lib/extensions/manifest.js +36 -4
- package/lib/extensions/paramHelper.js +11 -6
- package/lib/fsutils.js +14 -1
- package/lib/functions/projectConfig.js +34 -0
- package/lib/init/features/functions/index.js +4 -2
- package/lib/init/features/hosting/index.js +32 -41
- package/lib/init/features/index.js +22 -12
- package/lib/init/index.js +28 -11
- package/lib/requireConfig.js +11 -9
- package/lib/requirePermissions.js +4 -1
- package/lib/serve/functions.js +5 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/schema/firebase-config.json +93 -36
- package/lib/prepareUpload.js +0 -44
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const clc = require("cli-color");
|
|
4
4
|
const command_1 = require("../command");
|
|
5
|
-
const
|
|
5
|
+
const requireConfig_1 = require("../requireConfig");
|
|
6
6
|
const utils = require("../utils");
|
|
7
7
|
exports.default = new command_1.Command("target:remove <type> <resource>")
|
|
8
8
|
.description("remove a resource target")
|
|
9
|
-
.before(requireConfig)
|
|
9
|
+
.before(requireConfig_1.requireConfig)
|
|
10
10
|
.action((type, resource, options) => {
|
|
11
11
|
const name = options.rc.removeTarget(options.project, type, resource);
|
|
12
12
|
if (name) {
|
package/lib/commands/target.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const clc = require("cli-color");
|
|
4
4
|
const command_1 = require("../command");
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
|
-
const
|
|
6
|
+
const requireConfig_1 = require("../requireConfig");
|
|
7
7
|
const utils = require("../utils");
|
|
8
8
|
function logTargets(type, targets) {
|
|
9
9
|
logger_1.logger.info(clc.cyan("[ " + type + " ]"));
|
|
@@ -13,7 +13,7 @@ function logTargets(type, targets) {
|
|
|
13
13
|
}
|
|
14
14
|
exports.default = new command_1.Command("target [type]")
|
|
15
15
|
.description("display configured deploy targets for the current project")
|
|
16
|
-
.before(requireConfig)
|
|
16
|
+
.before(requireConfig_1.requireConfig)
|
|
17
17
|
.action((type, options) => {
|
|
18
18
|
if (!options.project) {
|
|
19
19
|
return utils.reject("No active project, cannot list deploy targets.");
|
package/lib/config.js
CHANGED
|
@@ -39,10 +39,17 @@ class Config {
|
|
|
39
39
|
_.set(this.data, target, this.materialize(target));
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
|
-
if (this.projectDir &&
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
if (this.projectDir && fsutils.dirExistsSync(this.path(Config.DEFAULT_FUNCTIONS_SOURCE))) {
|
|
43
|
+
if (Array.isArray(this.get("functions"))) {
|
|
44
|
+
if (!this.get("functions.[0].source")) {
|
|
45
|
+
this.set("functions.[0].source", Config.DEFAULT_FUNCTIONS_SOURCE);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
if (!this.get("functions.source")) {
|
|
50
|
+
this.set("functions.source", Config.DEFAULT_FUNCTIONS_SOURCE);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
46
53
|
}
|
|
47
54
|
}
|
|
48
55
|
materialize(target) {
|
|
@@ -144,6 +151,9 @@ class Config {
|
|
|
144
151
|
fs.ensureFileSync(this.path(p));
|
|
145
152
|
fs.writeFileSync(this.path(p), content, "utf8");
|
|
146
153
|
}
|
|
154
|
+
projectFileExists(p) {
|
|
155
|
+
return fs.existsSync(this.path(p));
|
|
156
|
+
}
|
|
147
157
|
deleteProjectFile(p) {
|
|
148
158
|
fs.removeSync(this.path(p));
|
|
149
159
|
}
|
|
@@ -81,11 +81,17 @@ async function want(args) {
|
|
|
81
81
|
}
|
|
82
82
|
exports.want = want;
|
|
83
83
|
async function resolveVersion(ref) {
|
|
84
|
-
if (!ref.version || ref.version === "latest") {
|
|
85
|
-
return "latest";
|
|
86
|
-
}
|
|
87
84
|
const extensionRef = refs.toExtensionRef(ref);
|
|
88
85
|
const versions = await extensionsApi.listExtensionVersions(extensionRef);
|
|
86
|
+
if (versions.length === 0) {
|
|
87
|
+
throw new error_1.FirebaseError(`No versions found for ${extensionRef}`);
|
|
88
|
+
}
|
|
89
|
+
if (!ref.version || ref.version === "latest") {
|
|
90
|
+
return versions
|
|
91
|
+
.map((ev) => ev.spec.version)
|
|
92
|
+
.sort(semver.compare)
|
|
93
|
+
.pop();
|
|
94
|
+
}
|
|
89
95
|
const maxSatisfying = semver.maxSatisfying(versions.map((ev) => ev.spec.version), ref.version);
|
|
90
96
|
if (!maxSatisfying) {
|
|
91
97
|
throw new error_1.FirebaseError(`No version of ${extensionRef} matches requested version ${ref.version}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensureServiceAgentRoles = exports.mergeBindings = exports.checkHttpIam = exports.checkServiceAccountIam = void 0;
|
|
3
|
+
exports.ensureServiceAgentRoles = exports.mergeBindings = exports.obtainEventarcServiceAgentBindings = exports.obtainDefaultComputeServiceAgentBindings = exports.obtainPubSubServiceAgentBindings = exports.obtainBinding = exports.checkHttpIam = exports.checkServiceAccountIam = exports.EVENTARC_SERVICE_AGENT_ROLE = exports.EVENTARC_EVENT_RECEIVER_ROLE = exports.RUN_INVOKER_ROLE = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
|
|
4
4
|
const cli_color_1 = require("cli-color");
|
|
5
5
|
const logger_1 = require("../../logger");
|
|
6
6
|
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
@@ -12,6 +12,10 @@ const utils = require("../../utils");
|
|
|
12
12
|
const resourceManager_1 = require("../../gcp/resourceManager");
|
|
13
13
|
const services_1 = require("./services");
|
|
14
14
|
const PERMISSION = "cloudfunctions.functions.setIamPolicy";
|
|
15
|
+
exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = "roles/iam.serviceAccountTokenCreator";
|
|
16
|
+
exports.RUN_INVOKER_ROLE = "roles/run.invoker";
|
|
17
|
+
exports.EVENTARC_EVENT_RECEIVER_ROLE = "roles/eventarc.eventReceiver";
|
|
18
|
+
exports.EVENTARC_SERVICE_AGENT_ROLE = "roles/eventarc.serviceAgent";
|
|
15
19
|
async function checkServiceAccountIam(projectId) {
|
|
16
20
|
const saEmail = `${projectId}@appspot.gserviceaccount.com`;
|
|
17
21
|
let passed = false;
|
|
@@ -67,6 +71,37 @@ function reduceEventsToServices(services, endpoint) {
|
|
|
67
71
|
}
|
|
68
72
|
return services;
|
|
69
73
|
}
|
|
74
|
+
function obtainBinding(existingPolicy, serviceAccount, role) {
|
|
75
|
+
let binding = existingPolicy.bindings.find((b) => b.role === role);
|
|
76
|
+
if (!binding) {
|
|
77
|
+
binding = {
|
|
78
|
+
role,
|
|
79
|
+
members: [],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!binding.members.find((m) => m === serviceAccount)) {
|
|
83
|
+
binding.members.push(serviceAccount);
|
|
84
|
+
}
|
|
85
|
+
return binding;
|
|
86
|
+
}
|
|
87
|
+
exports.obtainBinding = obtainBinding;
|
|
88
|
+
function obtainPubSubServiceAgentBindings(projectNumber, existingPolicy) {
|
|
89
|
+
const pubsubServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-pubsub.iam.gserviceaccount.com`;
|
|
90
|
+
return [obtainBinding(existingPolicy, pubsubServiceAgent, exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE)];
|
|
91
|
+
}
|
|
92
|
+
exports.obtainPubSubServiceAgentBindings = obtainPubSubServiceAgentBindings;
|
|
93
|
+
function obtainDefaultComputeServiceAgentBindings(projectNumber, existingPolicy) {
|
|
94
|
+
const defaultComputeServiceAgent = `serviceAccount:${projectNumber}-compute@developer.gserviceaccount.com`;
|
|
95
|
+
const invokerBinding = obtainBinding(existingPolicy, defaultComputeServiceAgent, exports.RUN_INVOKER_ROLE);
|
|
96
|
+
const eventReceiverBinding = obtainBinding(existingPolicy, defaultComputeServiceAgent, exports.EVENTARC_EVENT_RECEIVER_ROLE);
|
|
97
|
+
return [invokerBinding, eventReceiverBinding];
|
|
98
|
+
}
|
|
99
|
+
exports.obtainDefaultComputeServiceAgentBindings = obtainDefaultComputeServiceAgentBindings;
|
|
100
|
+
function obtainEventarcServiceAgentBindings(projectNumber, existingPolicy) {
|
|
101
|
+
const eventarcServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-eventarc.iam.gserviceaccount.com`;
|
|
102
|
+
return [obtainBinding(existingPolicy, eventarcServiceAgent, exports.EVENTARC_SERVICE_AGENT_ROLE)];
|
|
103
|
+
}
|
|
104
|
+
exports.obtainEventarcServiceAgentBindings = obtainEventarcServiceAgentBindings;
|
|
70
105
|
function mergeBindings(policy, allRequiredBindings) {
|
|
71
106
|
for (const requiredBindings of allRequiredBindings) {
|
|
72
107
|
if (requiredBindings.length === 0) {
|
|
@@ -107,6 +142,14 @@ async function ensureServiceAgentRoles(projectNumber, want, have) {
|
|
|
107
142
|
const findRequiredBindings = [];
|
|
108
143
|
newServices.forEach((service) => findRequiredBindings.push(service.requiredProjectBindings(projectNumber, policy)));
|
|
109
144
|
const allRequiredBindings = await Promise.all(findRequiredBindings);
|
|
145
|
+
if (haveServices.length === 0) {
|
|
146
|
+
allRequiredBindings.push(obtainPubSubServiceAgentBindings(projectNumber, policy));
|
|
147
|
+
allRequiredBindings.push(obtainDefaultComputeServiceAgentBindings(projectNumber, policy));
|
|
148
|
+
allRequiredBindings.push(obtainEventarcServiceAgentBindings(projectNumber, policy));
|
|
149
|
+
}
|
|
150
|
+
if (!allRequiredBindings.find((bindings) => bindings.length > 0)) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
110
153
|
mergeBindings(policy, allRequiredBindings);
|
|
111
154
|
try {
|
|
112
155
|
await (0, resourceManager_1.setIamPolicy)(projectNumber, policy, "bindings");
|
|
@@ -9,7 +9,6 @@ const utils_1 = require("../../utils");
|
|
|
9
9
|
const gcs = require("../../gcp/storage");
|
|
10
10
|
const gcf = require("../../gcp/cloudfunctions");
|
|
11
11
|
const gcfv2 = require("../../gcp/cloudfunctionsv2");
|
|
12
|
-
const utils = require("../../utils");
|
|
13
12
|
const backend = require("./backend");
|
|
14
13
|
(0, tmp_1.setGracefulCleanup)();
|
|
15
14
|
async function uploadSourceV1(context, region) {
|
|
@@ -33,7 +32,7 @@ async function uploadSourceV2(context, region) {
|
|
|
33
32
|
context.storage = Object.assign(Object.assign({}, context.storage), { [region]: res.storageSource });
|
|
34
33
|
}
|
|
35
34
|
async function deploy(context, options, payload) {
|
|
36
|
-
if (!
|
|
35
|
+
if (!context.config) {
|
|
37
36
|
return;
|
|
38
37
|
}
|
|
39
38
|
if (!context.functionsSourceV1 && !context.functionsSourceV2) {
|
|
@@ -53,12 +52,9 @@ async function deploy(context, options, payload) {
|
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
54
|
await Promise.all(uploads);
|
|
56
|
-
|
|
55
|
+
const source = context.config.source;
|
|
57
56
|
if (uploads.length) {
|
|
58
|
-
(0, utils_1.logSuccess)(clc.green.bold("functions:")
|
|
59
|
-
" " +
|
|
60
|
-
clc.bold(options.config.src.functions.source) +
|
|
61
|
-
" folder uploaded successfully");
|
|
57
|
+
(0, utils_1.logSuccess)(`${clc.green.bold("functions:")} ${clc.bold(source)} folder uploaded successfully`);
|
|
62
58
|
}
|
|
63
59
|
}
|
|
64
60
|
catch (err) {
|
|
@@ -19,6 +19,7 @@ const logger_1 = require("../../logger");
|
|
|
19
19
|
const triggerRegionHelper_1 = require("./triggerRegionHelper");
|
|
20
20
|
const checkIam_1 = require("./checkIam");
|
|
21
21
|
const error_1 = require("../../error");
|
|
22
|
+
const projectConfig_1 = require("../../functions/projectConfig");
|
|
22
23
|
function hasUserConfig(config) {
|
|
23
24
|
return Object.keys(config).length > 1;
|
|
24
25
|
}
|
|
@@ -28,16 +29,17 @@ function hasDotenv(opts) {
|
|
|
28
29
|
async function prepare(context, options, payload) {
|
|
29
30
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
30
31
|
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
31
|
-
|
|
32
|
+
context.config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
|
|
33
|
+
const sourceDirName = context.config.source;
|
|
32
34
|
if (!sourceDirName) {
|
|
33
|
-
throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions
|
|
35
|
+
throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions source defined in firebase.json`);
|
|
34
36
|
}
|
|
35
37
|
const sourceDir = options.config.path(sourceDirName);
|
|
36
38
|
const delegateContext = {
|
|
37
39
|
projectId,
|
|
38
40
|
sourceDir,
|
|
39
41
|
projectDir: options.config.projectDir,
|
|
40
|
-
runtime:
|
|
42
|
+
runtime: context.config.runtime || "",
|
|
41
43
|
};
|
|
42
44
|
const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
|
|
43
45
|
logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
|
|
@@ -95,10 +97,10 @@ async function prepare(context, options, payload) {
|
|
|
95
97
|
" directory for uploading...");
|
|
96
98
|
}
|
|
97
99
|
if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv1")) {
|
|
98
|
-
context.functionsSourceV1 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(
|
|
100
|
+
context.functionsSourceV1 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, context.config, runtimeConfig);
|
|
99
101
|
}
|
|
100
102
|
if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
|
|
101
|
-
context.functionsSourceV2 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(
|
|
103
|
+
context.functionsSourceV2 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, context.config);
|
|
102
104
|
}
|
|
103
105
|
for (const endpoint of backend.allEndpoints(wantBackend)) {
|
|
104
106
|
endpoint.environmentVariables = wantBackend.environmentVariables;
|
|
@@ -48,15 +48,14 @@ async function pipeAsync(from, to) {
|
|
|
48
48
|
from.pipe(to);
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
-
async function packageSource(
|
|
52
|
-
var _a;
|
|
51
|
+
async function packageSource(sourceDir, config, runtimeConfig) {
|
|
53
52
|
const tmpFile = tmp.fileSync({ prefix: "firebase-functions-", postfix: ".zip" }).name;
|
|
54
53
|
const fileStream = fs.createWriteStream(tmpFile, {
|
|
55
54
|
flags: "w",
|
|
56
55
|
encoding: "binary",
|
|
57
56
|
});
|
|
58
57
|
const archive = archiver("zip");
|
|
59
|
-
const ignore =
|
|
58
|
+
const ignore = config.ignore || ["node_modules", ".git"];
|
|
60
59
|
ignore.push("firebase-debug.log", "firebase-debug.*.log", CONFIG_DEST_FILE);
|
|
61
60
|
try {
|
|
62
61
|
const files = await fsAsync.readdirRecursive({ path: sourceDir, ignore: ignore });
|
|
@@ -66,8 +65,8 @@ async function packageSource(options, sourceDir, configValues) {
|
|
|
66
65
|
mode: file.mode,
|
|
67
66
|
});
|
|
68
67
|
});
|
|
69
|
-
if (typeof
|
|
70
|
-
archive.append(JSON.stringify(
|
|
68
|
+
if (typeof runtimeConfig !== "undefined") {
|
|
69
|
+
archive.append(JSON.stringify(runtimeConfig, null, 2), {
|
|
71
70
|
name: CONFIG_DEST_FILE,
|
|
72
71
|
mode: 420,
|
|
73
72
|
});
|
|
@@ -81,20 +80,15 @@ async function packageSource(options, sourceDir, configValues) {
|
|
|
81
80
|
exit: 1,
|
|
82
81
|
});
|
|
83
82
|
}
|
|
84
|
-
utils.assertDefined(options.config.src.functions);
|
|
85
|
-
utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
|
|
86
83
|
utils.logBullet(clc.cyan.bold("functions:") +
|
|
87
84
|
" packaged " +
|
|
88
|
-
clc.bold(
|
|
85
|
+
clc.bold(sourceDir) +
|
|
89
86
|
" (" +
|
|
90
87
|
filesize(archive.pointer()) +
|
|
91
88
|
") for uploading");
|
|
92
89
|
return tmpFile;
|
|
93
90
|
}
|
|
94
|
-
async function prepareFunctionsUpload(
|
|
95
|
-
|
|
96
|
-
utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
|
|
97
|
-
const sourceDir = options.config.path(options.config.src.functions.source);
|
|
98
|
-
return packageSource(options, sourceDir, runtimeConfig);
|
|
91
|
+
async function prepareFunctionsUpload(sourceDir, config, runtimeConfig) {
|
|
92
|
+
return packageSource(sourceDir, config, runtimeConfig);
|
|
99
93
|
}
|
|
100
94
|
exports.prepareFunctionsUpload = prepareFunctionsUpload;
|
|
@@ -180,6 +180,13 @@ class Fabricator {
|
|
|
180
180
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
+
else if (backend.isCallableTriggered(endpoint)) {
|
|
184
|
+
await this.executor
|
|
185
|
+
.run(async () => {
|
|
186
|
+
await gcf.setInvokerCreate(endpoint.project, backend.functionName(endpoint), ["public"]);
|
|
187
|
+
})
|
|
188
|
+
.catch(rethrowAs(endpoint, "set invoker"));
|
|
189
|
+
}
|
|
183
190
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
184
191
|
const invoker = endpoint.taskQueueTrigger.invoker;
|
|
185
192
|
if (invoker && !invoker.includes("private")) {
|
|
@@ -232,12 +239,17 @@ class Fabricator {
|
|
|
232
239
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
233
240
|
}
|
|
234
241
|
}
|
|
242
|
+
else if (backend.isCallableTriggered(endpoint)) {
|
|
243
|
+
await this.executor
|
|
244
|
+
.run(() => run.setInvokerCreate(endpoint.project, serviceName, ["public"]))
|
|
245
|
+
.catch(rethrowAs(endpoint, "set invoker"));
|
|
246
|
+
}
|
|
235
247
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
236
248
|
const invoker = endpoint.taskQueueTrigger.invoker;
|
|
237
249
|
if (invoker && !invoker.includes("private")) {
|
|
238
250
|
await this.executor
|
|
239
251
|
.run(async () => {
|
|
240
|
-
await
|
|
252
|
+
await run.setInvokerCreate(endpoint.project, serviceName, invoker);
|
|
241
253
|
})
|
|
242
254
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
243
255
|
}
|
|
@@ -15,7 +15,7 @@ const functionsConfig_1 = require("../../../functionsConfig");
|
|
|
15
15
|
const functionsDeployHelper_1 = require("../functionsDeployHelper");
|
|
16
16
|
const error_1 = require("../../../error");
|
|
17
17
|
async function release(context, options, payload) {
|
|
18
|
-
if (!
|
|
18
|
+
if (!context.config) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
const plan = planner.createDeploymentPlan(payload.functions.backend, await backend.existingBackend(context), { filters: context.filters });
|
|
@@ -1,23 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensureFirebaseAlertsTriggerRegion =
|
|
3
|
+
exports.ensureFirebaseAlertsTriggerRegion = void 0;
|
|
4
4
|
const error_1 = require("../../../error");
|
|
5
|
-
exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = "roles/iam.serviceAccountTokenCreator";
|
|
6
|
-
function obtainFirebaseAlertsBindings(projectNumber, existingPolicy) {
|
|
7
|
-
const pubsubServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-pubsub.iam.gserviceaccount.com`;
|
|
8
|
-
let pubsubBinding = existingPolicy.bindings.find((b) => b.role === exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE);
|
|
9
|
-
if (!pubsubBinding) {
|
|
10
|
-
pubsubBinding = {
|
|
11
|
-
role: exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE,
|
|
12
|
-
members: [],
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
if (!pubsubBinding.members.find((m) => m === pubsubServiceAgent)) {
|
|
16
|
-
pubsubBinding.members.push(pubsubServiceAgent);
|
|
17
|
-
}
|
|
18
|
-
return Promise.resolve([pubsubBinding]);
|
|
19
|
-
}
|
|
20
|
-
exports.obtainFirebaseAlertsBindings = obtainFirebaseAlertsBindings;
|
|
21
5
|
function ensureFirebaseAlertsTriggerRegion(endpoint) {
|
|
22
6
|
if (!endpoint.eventTrigger.region) {
|
|
23
7
|
endpoint.eventTrigger.region = "global";
|
|
@@ -5,6 +5,7 @@ const backend = require("../backend");
|
|
|
5
5
|
const storage_1 = require("./storage");
|
|
6
6
|
const firebaseAlerts_1 = require("./firebaseAlerts");
|
|
7
7
|
const noop = () => Promise.resolve();
|
|
8
|
+
const noopProjectBindings = () => Promise.resolve([]);
|
|
8
9
|
exports.NoOpService = {
|
|
9
10
|
name: "noop",
|
|
10
11
|
api: "",
|
|
@@ -26,7 +27,7 @@ exports.StorageService = {
|
|
|
26
27
|
exports.FirebaseAlertsService = {
|
|
27
28
|
name: "firebasealerts",
|
|
28
29
|
api: "logging.googleapis.com",
|
|
29
|
-
requiredProjectBindings:
|
|
30
|
+
requiredProjectBindings: noopProjectBindings,
|
|
30
31
|
ensureTriggerRegion: firebaseAlerts_1.ensureFirebaseAlertsTriggerRegion,
|
|
31
32
|
};
|
|
32
33
|
exports.EVENT_SERVICE_MAPPING = {
|
|
@@ -41,12 +41,22 @@ async function deploy(context, options) {
|
|
|
41
41
|
const publicDir = options.config.path(deploy.config.public);
|
|
42
42
|
const files = (0, listFiles_1.listFiles)(publicDir, deploy.config.ignore);
|
|
43
43
|
(0, utils_1.logLabeledBullet)(`hosting[${deploy.site}]`, `found ${files.length} files in ${clc.bold(deploy.config.public)}`);
|
|
44
|
+
let concurrency = 200;
|
|
45
|
+
const envConcurrency = (0, utils_1.envOverride)("FIREBASE_HOSTING_UPLOAD_CONCURRENCY", "");
|
|
46
|
+
if (envConcurrency) {
|
|
47
|
+
const c = parseInt(envConcurrency, 10);
|
|
48
|
+
if (!isNaN(c) && c > 0) {
|
|
49
|
+
concurrency = c;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
logger_1.logger.debug(`[hosting] uploading with ${concurrency} concurrency`);
|
|
44
53
|
const uploader = new uploader_1.Uploader({
|
|
45
54
|
version: deploy.version,
|
|
46
55
|
files: files,
|
|
47
56
|
public: publicDir,
|
|
48
57
|
cwd: options.cwd,
|
|
49
58
|
projectRoot: (0, detectProjectRoot_1.detectProjectRoot)(options),
|
|
59
|
+
uploadConcurrency: concurrency,
|
|
50
60
|
});
|
|
51
61
|
const progressInterval = setInterval(() => updateSpinner(uploader.statusMessage(), debugging), debugging ? 2000 : 200);
|
|
52
62
|
try {
|
|
@@ -5705,6 +5705,28 @@ exports.default = {
|
|
|
5705
5705
|
},
|
|
5706
5706
|
type: "object",
|
|
5707
5707
|
},
|
|
5708
|
+
GoogleCloudIdentitytoolkitAdminV2AllowByDefault: {
|
|
5709
|
+
description: "Defines a policy of allowing every region by default and adding disallowed regions to a disallow list.",
|
|
5710
|
+
properties: {
|
|
5711
|
+
disallowedRegions: {
|
|
5712
|
+
description: "Two letter unicode region codes to disallow as defined by https://cldr.unicode.org/ The full list of these region codes is here: https://github.com/unicode-cldr/cldr-localenames-full/blob/master/main/en/territories.json",
|
|
5713
|
+
items: { type: "string" },
|
|
5714
|
+
type: "array",
|
|
5715
|
+
},
|
|
5716
|
+
},
|
|
5717
|
+
type: "object",
|
|
5718
|
+
},
|
|
5719
|
+
GoogleCloudIdentitytoolkitAdminV2AllowlistOnly: {
|
|
5720
|
+
description: "Defines a policy of only allowing regions by explicitly adding them to an allowlist.",
|
|
5721
|
+
properties: {
|
|
5722
|
+
allowedRegions: {
|
|
5723
|
+
description: "Two letter unicode region codes to allow as defined by https://cldr.unicode.org/ The full list of these region codes is here: https://github.com/unicode-cldr/cldr-localenames-full/blob/master/main/en/territories.json",
|
|
5724
|
+
items: { type: "string" },
|
|
5725
|
+
type: "array",
|
|
5726
|
+
},
|
|
5727
|
+
},
|
|
5728
|
+
type: "object",
|
|
5729
|
+
},
|
|
5708
5730
|
GoogleCloudIdentitytoolkitAdminV2Anonymous: {
|
|
5709
5731
|
description: "Configuration options related to authenticating an anonymous user.",
|
|
5710
5732
|
properties: {
|
|
@@ -6315,6 +6337,18 @@ exports.default = {
|
|
|
6315
6337
|
},
|
|
6316
6338
|
type: "object",
|
|
6317
6339
|
},
|
|
6340
|
+
GoogleCloudIdentitytoolkitAdminV2SmsRegionConfig: {
|
|
6341
|
+
description: "Configures the regions where users are allowed to send verification SMS for the project or tenant. This is based on the calling code of the destination phone number.",
|
|
6342
|
+
properties: {
|
|
6343
|
+
allowByDefault: {
|
|
6344
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2AllowByDefault",
|
|
6345
|
+
},
|
|
6346
|
+
allowlistOnly: {
|
|
6347
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2AllowlistOnly",
|
|
6348
|
+
},
|
|
6349
|
+
},
|
|
6350
|
+
type: "object",
|
|
6351
|
+
},
|
|
6318
6352
|
GoogleCloudIdentitytoolkitAdminV2SmsTemplate: {
|
|
6319
6353
|
description: "The template to use when sending an SMS.",
|
|
6320
6354
|
properties: {
|
|
@@ -6424,6 +6458,9 @@ exports.default = {
|
|
|
6424
6458
|
readOnly: true,
|
|
6425
6459
|
type: "string",
|
|
6426
6460
|
},
|
|
6461
|
+
smsRegionConfig: {
|
|
6462
|
+
$ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2SmsRegionConfig",
|
|
6463
|
+
},
|
|
6427
6464
|
testPhoneNumbers: {
|
|
6428
6465
|
additionalProperties: { type: "string" },
|
|
6429
6466
|
description: "A map of pairs that can be used for MFA. The phone number should be in E.164 format (https://www.itu.int/rec/T-REC-E.164/) and a maximum of 10 pairs can be added (error will be thrown once exceeded).",
|
|
@@ -10,7 +10,7 @@ const logger_1 = require("../logger");
|
|
|
10
10
|
const path = require("path");
|
|
11
11
|
const constants_1 = require("./constants");
|
|
12
12
|
const requireAuth_1 = require("../requireAuth");
|
|
13
|
-
const
|
|
13
|
+
const requireConfig_1 = require("../requireConfig");
|
|
14
14
|
const types_1 = require("./types");
|
|
15
15
|
const error_1 = require("../error");
|
|
16
16
|
const registry_1 = require("./registry");
|
|
@@ -102,7 +102,7 @@ async function beforeEmulatorCommand(options) {
|
|
|
102
102
|
options.config = DEFAULT_CONFIG;
|
|
103
103
|
}
|
|
104
104
|
else {
|
|
105
|
-
await requireConfig(options);
|
|
105
|
+
await (0, requireConfig_1.requireConfig)(options);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
exports.beforeEmulatorCommand = beforeEmulatorCommand;
|
|
@@ -38,6 +38,7 @@ const getDefaultDatabaseInstance_1 = require("../getDefaultDatabaseInstance");
|
|
|
38
38
|
const auth_2 = require("../auth");
|
|
39
39
|
const extensionsEmulator_1 = require("./extensionsEmulator");
|
|
40
40
|
const previews_1 = require("../previews");
|
|
41
|
+
const projectConfig_1 = require("../functions/projectConfig");
|
|
41
42
|
const START_LOGGING_EMULATOR = utils.envOverride("START_LOGGING_EMULATOR", "false", (val) => val === "true");
|
|
42
43
|
async function getAndCheckAddress(emulator, options) {
|
|
43
44
|
var _a, _b, _c, _d;
|
|
@@ -140,7 +141,7 @@ function filterEmulatorTargets(options) {
|
|
|
140
141
|
}
|
|
141
142
|
exports.filterEmulatorTargets = filterEmulatorTargets;
|
|
142
143
|
function shouldStart(options, name) {
|
|
143
|
-
var _a, _b
|
|
144
|
+
var _a, _b;
|
|
144
145
|
if (name === types_1.Emulators.HUB) {
|
|
145
146
|
return !!options.project;
|
|
146
147
|
}
|
|
@@ -155,9 +156,15 @@ function shouldStart(options, name) {
|
|
|
155
156
|
}
|
|
156
157
|
return (!!options.project && targets.some((target) => types_1.EMULATORS_SUPPORTED_BY_UI.includes(target)));
|
|
157
158
|
}
|
|
158
|
-
if (name === types_1.Emulators.FUNCTIONS && emulatorInTargets
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
if (name === types_1.Emulators.FUNCTIONS && emulatorInTargets) {
|
|
160
|
+
try {
|
|
161
|
+
(0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("WARN", "functions", `The functions emulator is configured but there is no functions source directory. Have you run ${clc.bold("firebase init functions")}?`);
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
161
168
|
}
|
|
162
169
|
if (name === types_1.Emulators.HOSTING && emulatorInTargets && !options.config.get("hosting")) {
|
|
163
170
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.HOSTING).logLabeled("WARN", "hosting", `The hosting emulator is configured but there is no hosting configuration. Have you run ${clc.bold("firebase init hosting")}?`);
|
|
@@ -253,16 +260,15 @@ async function startAll(options, showUI = true) {
|
|
|
253
260
|
const emulatableBackends = [];
|
|
254
261
|
const projectDir = (options.extDevDir || options.config.projectDir);
|
|
255
262
|
if (shouldStart(options, types_1.Emulators.FUNCTIONS)) {
|
|
256
|
-
|
|
257
|
-
utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
|
|
263
|
+
const functionsCfg = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
|
|
258
264
|
utils.assertIsStringOrUndefined(options.extDevDir);
|
|
259
|
-
const functionsDir = path.join(projectDir,
|
|
265
|
+
const functionsDir = path.join(projectDir, functionsCfg.source);
|
|
260
266
|
emulatableBackends.push({
|
|
261
267
|
functionsDir,
|
|
262
268
|
env: Object.assign({}, options.extDevEnv),
|
|
263
269
|
secretEnv: [],
|
|
264
270
|
predefinedTriggers: options.extDevTriggers,
|
|
265
|
-
nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion ||
|
|
271
|
+
nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion || functionsCfg.runtime),
|
|
266
272
|
});
|
|
267
273
|
}
|
|
268
274
|
if (shouldStart(options, types_1.Emulators.EXTENSIONS) && previews_1.previews.extensionsemulator) {
|
|
@@ -40,13 +40,13 @@ exports.DownloadDetails = {
|
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
storage: {
|
|
43
|
-
downloadPath: path.join(CACHE_DIR, "cloud-storage-rules-runtime-v1.0.
|
|
44
|
-
version: "1.0.
|
|
43
|
+
downloadPath: path.join(CACHE_DIR, "cloud-storage-rules-runtime-v1.0.2.jar"),
|
|
44
|
+
version: "1.0.2",
|
|
45
45
|
opts: {
|
|
46
46
|
cacheDir: CACHE_DIR,
|
|
47
|
-
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v1.0.
|
|
48
|
-
expectedSize:
|
|
49
|
-
expectedChecksum: "
|
|
47
|
+
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v1.0.2.jar",
|
|
48
|
+
expectedSize: 35704306,
|
|
49
|
+
expectedChecksum: "0dd3e17939610fc3dbdf53fb24cfda86",
|
|
50
50
|
namePrefix: "cloud-storage-rules-emulator",
|
|
51
51
|
},
|
|
52
52
|
},
|
|
@@ -65,6 +65,9 @@ class ExtensionsEmulator {
|
|
|
65
65
|
"./functions/package.json",
|
|
66
66
|
"./functions/node_modules",
|
|
67
67
|
];
|
|
68
|
+
if (!fs.existsSync(args.path)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
68
71
|
for (const requiredFile of requiredFiles) {
|
|
69
72
|
const f = path.join(args.path, requiredFile);
|
|
70
73
|
if (!fs.existsSync(f)) {
|
|
@@ -289,7 +289,7 @@ class FunctionsEmulator {
|
|
|
289
289
|
});
|
|
290
290
|
for (const definition of toSetup) {
|
|
291
291
|
try {
|
|
292
|
-
(0, validate_1.functionIdsAreValid)([definition]);
|
|
292
|
+
(0, validate_1.functionIdsAreValid)([Object.assign(Object.assign({}, definition), { id: definition.name })]);
|
|
293
293
|
}
|
|
294
294
|
catch (e) {
|
|
295
295
|
this.logger.logLabeled("WARN", `functions[${definition.id}]`, `Invalid function id: ${e.message}`);
|
|
@@ -488,26 +488,16 @@ class FunctionsEmulator {
|
|
|
488
488
|
return def.eventTrigger ? `${def.id}-${this.triggerGeneration}` : def.id;
|
|
489
489
|
}
|
|
490
490
|
getBackendInfo() {
|
|
491
|
-
const cf3Triggers =
|
|
492
|
-
.filter((t) => !t.backend.extensionInstanceId)
|
|
493
|
-
.map((t) => t.def);
|
|
491
|
+
const cf3Triggers = this.getCF3Triggers();
|
|
494
492
|
return this.args.emulatableBackends.map((e) => {
|
|
495
|
-
|
|
496
|
-
const envWithSecrets = Object.assign({}, e.env);
|
|
497
|
-
for (const s of e.secretEnv) {
|
|
498
|
-
envWithSecrets[s.key] = backend.secretVersionName(s);
|
|
499
|
-
}
|
|
500
|
-
return {
|
|
501
|
-
directory: e.functionsDir,
|
|
502
|
-
env: envWithSecrets,
|
|
503
|
-
extensionInstanceId: e.extensionInstanceId,
|
|
504
|
-
extension: e.extension,
|
|
505
|
-
extensionVersion: e.extensionVersion,
|
|
506
|
-
extensionSpec: e.extensionSpec,
|
|
507
|
-
functionTriggers: (_a = e.predefinedTriggers) !== null && _a !== void 0 ? _a : cf3Triggers,
|
|
508
|
-
};
|
|
493
|
+
return (0, functionsEmulatorShared_1.toBackendInfo)(e, cf3Triggers);
|
|
509
494
|
});
|
|
510
495
|
}
|
|
496
|
+
getCF3Triggers() {
|
|
497
|
+
return Object.values(this.triggers)
|
|
498
|
+
.filter((t) => !t.backend.extensionInstanceId)
|
|
499
|
+
.map((t) => t.def);
|
|
500
|
+
}
|
|
511
501
|
addTriggerRecord(def, opts) {
|
|
512
502
|
const key = this.getTriggerKey(def);
|
|
513
503
|
this.triggers[key] = {
|