firebase-tools 10.5.0 → 10.7.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/command.js +4 -4
- package/lib/commands/deploy.js +1 -1
- package/lib/commands/emulators-start.js +7 -2
- package/lib/commands/ext-configure.js +15 -5
- package/lib/commands/ext-export.js +6 -5
- package/lib/commands/ext-install.js +28 -44
- package/lib/commands/ext-update.js +9 -1
- package/lib/commands/functions-delete.js +7 -3
- package/lib/commands/functions-secrets-destroy.js +23 -3
- package/lib/commands/functions-secrets-prune.js +15 -12
- package/lib/commands/functions-secrets-set.js +51 -4
- package/lib/commands/hosting-channel-deploy.js +2 -2
- package/lib/deploy/database/deploy.js +4 -0
- package/lib/deploy/database/index.js +1 -0
- package/lib/deploy/extensions/deploy.js +4 -4
- package/lib/deploy/extensions/deploymentSummary.js +8 -5
- package/lib/deploy/extensions/planner.js +36 -9
- package/lib/deploy/extensions/prepare.js +1 -1
- package/lib/deploy/extensions/secrets.js +2 -2
- package/lib/deploy/extensions/tasks.js +60 -21
- package/lib/deploy/functions/backend.js +37 -6
- package/lib/deploy/functions/build.js +162 -0
- package/lib/deploy/functions/checkIam.js +10 -6
- package/lib/deploy/functions/deploy.js +49 -28
- package/lib/deploy/functions/ensure.js +4 -4
- package/lib/deploy/functions/functionsDeployHelper.js +99 -24
- package/lib/deploy/functions/prepare.js +130 -62
- package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
- package/lib/deploy/functions/pricing.js +6 -3
- package/lib/deploy/functions/prompts.js +1 -7
- package/lib/deploy/functions/release/fabricator.js +70 -28
- package/lib/deploy/functions/release/index.js +41 -6
- package/lib/deploy/functions/release/planner.js +19 -12
- package/lib/deploy/functions/release/reporter.js +14 -11
- package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
- package/lib/deploy/functions/runtimes/node/index.js +1 -1
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/services/auth.js +95 -0
- package/lib/deploy/functions/services/index.js +41 -21
- package/lib/deploy/functions/services/storage.js +1 -6
- package/lib/deploy/functions/validate.js +32 -6
- package/lib/deploy/hosting/args.js +2 -0
- package/lib/deploy/hosting/convertConfig.js +39 -8
- package/lib/deploy/hosting/deploy.js +3 -3
- package/lib/deploy/hosting/prepare.js +2 -2
- package/lib/deploy/hosting/release.js +6 -2
- package/lib/deploy/index.js +82 -93
- package/lib/deploy/remoteconfig/deploy.js +4 -0
- package/lib/deploy/remoteconfig/index.js +3 -1
- package/lib/emulator/auth/operations.js +5 -0
- package/lib/emulator/auth/utils.js +3 -25
- package/lib/emulator/controller.js +17 -14
- package/lib/emulator/downloadableEmulators.js +39 -23
- package/lib/emulator/extensions/postinstall.js +41 -0
- package/lib/emulator/extensions/validation.js +2 -2
- package/lib/emulator/extensionsEmulator.js +85 -21
- package/lib/emulator/functionsEmulator.js +88 -10
- package/lib/emulator/functionsEmulatorShared.js +37 -21
- package/lib/emulator/functionsEmulatorShell.js +2 -3
- package/lib/emulator/pubsubEmulator.js +13 -9
- package/lib/emulator/registry.js +34 -12
- package/lib/emulator/storage/apis/firebase.js +13 -8
- package/lib/emulator/storage/apis/gcloud.js +15 -9
- package/lib/emulator/storage/files.js +14 -3
- package/lib/emulator/storage/index.js +9 -1
- package/lib/emulator/storage/metadata.js +18 -8
- package/lib/emulator/storage/rules/manager.js +7 -17
- package/lib/emulator/storage/server.js +38 -12
- package/lib/ensureApiEnabled.js +8 -4
- package/lib/extensions/askUserForParam.js +14 -11
- package/lib/extensions/changelog.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +9 -10
- package/lib/extensions/emulator/specHelper.js +7 -1
- package/lib/extensions/emulator/triggerHelper.js +11 -14
- package/lib/extensions/extensionsApi.js +2 -1
- package/lib/extensions/extensionsHelper.js +30 -24
- package/lib/extensions/manifest.js +28 -8
- package/lib/extensions/paramHelper.js +19 -13
- package/lib/extensions/provisioningHelper.js +2 -2
- package/lib/extensions/warnings.js +3 -3
- package/lib/functions/env.js +10 -2
- package/lib/functions/events/index.js +7 -0
- package/lib/functions/events/v1.js +6 -0
- package/lib/functions/projectConfig.js +32 -6
- package/lib/functions/runtimeConfigExport.js +10 -6
- package/lib/functions/secrets.js +99 -6
- package/lib/functionsShellCommandAction.js +1 -1
- package/lib/gcp/cloudfunctions.js +44 -18
- package/lib/gcp/cloudfunctionsv2.js +48 -25
- package/lib/gcp/cloudtasks.js +5 -3
- package/lib/gcp/identityPlatform.js +44 -0
- package/lib/gcp/secretManager.js +2 -2
- package/lib/metaprogramming.js +2 -0
- package/lib/previews.js +1 -1
- package/lib/serve/functions.js +16 -19
- package/lib/serve/hosting.js +25 -12
- package/lib/serve/index.js +6 -0
- package/lib/track.js +15 -21
- package/lib/utils.js +30 -1
- package/npm-shrinkwrap.json +44 -2
- package/package.json +4 -1
- package/schema/firebase-config.json +6 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveVersion = exports.want = exports.have = exports.getExtension = exports.getExtensionVersion = void 0;
|
|
3
|
+
exports.resolveVersion = exports.want = exports.have = exports.getExtensionSpec = exports.getExtension = exports.getExtensionVersion = void 0;
|
|
4
4
|
const semver = require("semver");
|
|
5
5
|
const extensionsApi = require("../../extensions/extensionsApi");
|
|
6
6
|
const refs = require("../../extensions/refs");
|
|
@@ -8,6 +8,7 @@ const error_1 = require("../../error");
|
|
|
8
8
|
const extensionsHelper_1 = require("../../extensions/extensionsHelper");
|
|
9
9
|
const logger_1 = require("../../logger");
|
|
10
10
|
const manifest_1 = require("../../extensions/manifest");
|
|
11
|
+
const specHelper_1 = require("../../extensions/emulator/specHelper");
|
|
11
12
|
async function getExtensionVersion(i) {
|
|
12
13
|
if (!i.extensionVersion) {
|
|
13
14
|
if (!i.ref) {
|
|
@@ -20,7 +21,7 @@ async function getExtensionVersion(i) {
|
|
|
20
21
|
exports.getExtensionVersion = getExtensionVersion;
|
|
21
22
|
async function getExtension(i) {
|
|
22
23
|
if (!i.ref) {
|
|
23
|
-
throw new error_1.FirebaseError(`Can't get
|
|
24
|
+
throw new error_1.FirebaseError(`Can't get Extension for ${i.instanceId} because it has no ref`);
|
|
24
25
|
}
|
|
25
26
|
if (!i.extension) {
|
|
26
27
|
i.extension = await extensionsApi.getExtension(refs.toExtensionRef(i.ref));
|
|
@@ -28,6 +29,23 @@ async function getExtension(i) {
|
|
|
28
29
|
return i.extension;
|
|
29
30
|
}
|
|
30
31
|
exports.getExtension = getExtension;
|
|
32
|
+
async function getExtensionSpec(i) {
|
|
33
|
+
if (!i.extensionSpec) {
|
|
34
|
+
if (i.ref) {
|
|
35
|
+
const extensionVersion = await getExtensionVersion(i);
|
|
36
|
+
i.extensionSpec = extensionVersion.spec;
|
|
37
|
+
}
|
|
38
|
+
else if (i.localPath) {
|
|
39
|
+
i.extensionSpec = await (0, specHelper_1.readExtensionYaml)(i.localPath);
|
|
40
|
+
i.extensionSpec.postinstallContent = await (0, specHelper_1.readPostinstall)(i.localPath);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
throw new error_1.FirebaseError("InstanceSpec had no ref or localPath, unable to get extensionSpec");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return i.extensionSpec;
|
|
47
|
+
}
|
|
48
|
+
exports.getExtensionSpec = getExtensionSpec;
|
|
31
49
|
async function have(projectId) {
|
|
32
50
|
const instances = await extensionsApi.listInstances(projectId);
|
|
33
51
|
return instances.map((i) => {
|
|
@@ -50,8 +68,6 @@ async function want(args) {
|
|
|
50
68
|
for (const e of Object.entries(args.extensions)) {
|
|
51
69
|
try {
|
|
52
70
|
const instanceId = e[0];
|
|
53
|
-
const ref = refs.parse(e[1]);
|
|
54
|
-
ref.version = await resolveVersion(ref);
|
|
55
71
|
const params = (0, manifest_1.readInstanceParam)({
|
|
56
72
|
projectDir: args.projectDir,
|
|
57
73
|
instanceId,
|
|
@@ -62,11 +78,22 @@ async function want(args) {
|
|
|
62
78
|
});
|
|
63
79
|
const autoPopulatedParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId, args.emulatorMode);
|
|
64
80
|
const subbedParams = (0, extensionsHelper_1.substituteParams)(params, autoPopulatedParams);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
81
|
+
if ((0, extensionsHelper_1.isLocalPath)(e[1])) {
|
|
82
|
+
instanceSpecs.push({
|
|
83
|
+
instanceId,
|
|
84
|
+
localPath: e[1],
|
|
85
|
+
params: subbedParams,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const ref = refs.parse(e[1]);
|
|
90
|
+
ref.version = await resolveVersion(ref);
|
|
91
|
+
instanceSpecs.push({
|
|
92
|
+
instanceId,
|
|
93
|
+
ref,
|
|
94
|
+
params: subbedParams,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
70
97
|
}
|
|
71
98
|
catch (err) {
|
|
72
99
|
logger_1.logger.debug(`Got error reading extensions entry ${e}: ${err}`);
|
|
@@ -28,7 +28,7 @@ async function prepare(context, options, payload) {
|
|
|
28
28
|
projectDir: options.config.projectDir,
|
|
29
29
|
extensions: options.config.get("extensions"),
|
|
30
30
|
});
|
|
31
|
-
const usingSecrets = await Promise.all((_a = context.
|
|
31
|
+
const usingSecrets = await Promise.all((_a = context.want) === null || _a === void 0 ? void 0 : _a.map(secrets_1.checkSpecForSecrets));
|
|
32
32
|
if (usingSecrets.some((i) => i)) {
|
|
33
33
|
await (0, secretsUtils_1.ensureSecretManagerApiEnabled)(options);
|
|
34
34
|
}
|
|
@@ -29,8 +29,8 @@ async function handleSecretParams(payload, have, nonInteractive) {
|
|
|
29
29
|
}
|
|
30
30
|
exports.handleSecretParams = handleSecretParams;
|
|
31
31
|
async function checkSpecForSecrets(i) {
|
|
32
|
-
const
|
|
33
|
-
return secretUtils.usesSecrets(
|
|
32
|
+
const extensionSpec = await (0, planner_1.getExtensionSpec)(i);
|
|
33
|
+
return secretUtils.usesSecrets(extensionSpec);
|
|
34
34
|
}
|
|
35
35
|
exports.checkSpecForSecrets = checkSpecForSecrets;
|
|
36
36
|
const secretsInSpec = (spec) => {
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.deleteExtensionInstanceTask = exports.configureExtensionInstanceTask = exports.updateExtensionInstanceTask = exports.createExtensionInstanceTask = exports.extensionsDeploymentHandler = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
|
+
const error_1 = require("../../error");
|
|
5
6
|
const extensionsApi = require("../../extensions/extensionsApi");
|
|
7
|
+
const extensionsHelper_1 = require("../../extensions/extensionsHelper");
|
|
6
8
|
const refs = require("../../extensions/refs");
|
|
7
9
|
const utils = require("../../utils");
|
|
8
10
|
const isRetryable = (err) => err.status === 429 || err.status === 409;
|
|
@@ -25,13 +27,28 @@ function extensionsDeploymentHandler(errorHandler) {
|
|
|
25
27
|
exports.extensionsDeploymentHandler = extensionsDeploymentHandler;
|
|
26
28
|
function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
27
29
|
const run = async () => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
if (instanceSpec.ref) {
|
|
31
|
+
await extensionsApi.createInstance({
|
|
32
|
+
projectId,
|
|
33
|
+
instanceId: instanceSpec.instanceId,
|
|
34
|
+
params: instanceSpec.params,
|
|
35
|
+
extensionVersionRef: refs.toExtensionVersionRef(instanceSpec.ref),
|
|
36
|
+
validateOnly,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else if (instanceSpec.localPath) {
|
|
40
|
+
const extensionSource = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, instanceSpec.localPath);
|
|
41
|
+
await extensionsApi.createInstance({
|
|
42
|
+
projectId,
|
|
43
|
+
instanceId: instanceSpec.instanceId,
|
|
44
|
+
params: instanceSpec.params,
|
|
45
|
+
extensionSource,
|
|
46
|
+
validateOnly,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
throw new error_1.FirebaseError(`Tried to create extension instance ${instanceSpec.instanceId} without a ref or a local path. This should never happen.`);
|
|
51
|
+
}
|
|
35
52
|
printSuccess(instanceSpec.instanceId, "create", validateOnly);
|
|
36
53
|
return;
|
|
37
54
|
};
|
|
@@ -44,13 +61,27 @@ function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
|
|
|
44
61
|
exports.createExtensionInstanceTask = createExtensionInstanceTask;
|
|
45
62
|
function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
46
63
|
const run = async () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
if (instanceSpec.ref) {
|
|
65
|
+
await extensionsApi.updateInstanceFromRegistry({
|
|
66
|
+
projectId,
|
|
67
|
+
instanceId: instanceSpec.instanceId,
|
|
68
|
+
extRef: refs.toExtensionVersionRef(instanceSpec.ref),
|
|
69
|
+
params: instanceSpec.params,
|
|
70
|
+
validateOnly,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else if (instanceSpec.localPath) {
|
|
74
|
+
const extensionSource = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, instanceSpec.localPath);
|
|
75
|
+
await extensionsApi.updateInstance({
|
|
76
|
+
projectId,
|
|
77
|
+
instanceId: instanceSpec.instanceId,
|
|
78
|
+
extensionSource,
|
|
79
|
+
validateOnly,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new error_1.FirebaseError(`Tried to update extension instance ${instanceSpec.instanceId} without a ref or a local path. This should never happen.`);
|
|
84
|
+
}
|
|
54
85
|
printSuccess(instanceSpec.instanceId, "update", validateOnly);
|
|
55
86
|
return;
|
|
56
87
|
};
|
|
@@ -63,12 +94,20 @@ function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
|
|
|
63
94
|
exports.updateExtensionInstanceTask = updateExtensionInstanceTask;
|
|
64
95
|
function configureExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
65
96
|
const run = async () => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
97
|
+
if (instanceSpec.ref) {
|
|
98
|
+
await extensionsApi.configureInstance({
|
|
99
|
+
projectId,
|
|
100
|
+
instanceId: instanceSpec.instanceId,
|
|
101
|
+
params: instanceSpec.params,
|
|
102
|
+
validateOnly,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else if (instanceSpec.localPath) {
|
|
106
|
+
throw new error_1.FirebaseError(`Tried to configure extension instance ${instanceSpec.instanceId} from a local path. This should never happen.`);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
throw new error_1.FirebaseError(`Tried to configure extension instance ${instanceSpec.instanceId} without a ref or a local path. This should never happen.`);
|
|
110
|
+
}
|
|
72
111
|
printSuccess(instanceSpec.instanceId, "configure", validateOnly);
|
|
73
112
|
return;
|
|
74
113
|
};
|
|
@@ -81,7 +120,7 @@ function configureExtensionInstanceTask(projectId, instanceSpec, validateOnly =
|
|
|
81
120
|
exports.configureExtensionInstanceTask = configureExtensionInstanceTask;
|
|
82
121
|
function deleteExtensionInstanceTask(projectId, instanceSpec) {
|
|
83
122
|
const run = async () => {
|
|
84
|
-
|
|
123
|
+
await extensionsApi.deleteInstance(projectId, instanceSpec.instanceId);
|
|
85
124
|
printSuccess(instanceSpec.instanceId, "delete", false);
|
|
86
125
|
return;
|
|
87
126
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compareFunctions = exports.
|
|
3
|
+
exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.merge = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_MEMORY_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.memoryOptionDisplayName = exports.AllMemoryOptions = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.endpointTriggerType = void 0;
|
|
4
4
|
const gcf = require("../../gcp/cloudfunctions");
|
|
5
5
|
const gcfV2 = require("../../gcp/cloudfunctionsv2");
|
|
6
6
|
const utils = require("../../utils");
|
|
7
7
|
const error_1 = require("../../error");
|
|
8
8
|
const previews_1 = require("../../previews");
|
|
9
|
+
const functional_1 = require("../../functional");
|
|
9
10
|
function endpointTriggerType(endpoint) {
|
|
10
11
|
if (isScheduleTriggered(endpoint)) {
|
|
11
12
|
return "scheduled";
|
|
@@ -22,11 +23,21 @@ function endpointTriggerType(endpoint) {
|
|
|
22
23
|
else if (isTaskQueueTriggered(endpoint)) {
|
|
23
24
|
return "taskQueue";
|
|
24
25
|
}
|
|
26
|
+
else if (isBlockingTriggered(endpoint)) {
|
|
27
|
+
return endpoint.blockingTrigger.eventType;
|
|
28
|
+
}
|
|
25
29
|
else {
|
|
26
30
|
throw new Error("Unexpected trigger type for endpoint " + JSON.stringify(endpoint));
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
exports.endpointTriggerType = endpointTriggerType;
|
|
34
|
+
exports.AllVpcEgressSettings = ["PRIVATE_RANGES_ONLY", "ALL_TRAFFIC"];
|
|
35
|
+
exports.AllIngressSettings = [
|
|
36
|
+
"ALLOW_ALL",
|
|
37
|
+
"ALLOW_INTERNAL_ONLY",
|
|
38
|
+
"ALLOW_INTERNAL_AND_GCLB",
|
|
39
|
+
];
|
|
40
|
+
exports.AllMemoryOptions = [128, 256, 512, 1024, 2048, 4096, 8192];
|
|
30
41
|
function memoryOptionDisplayName(option) {
|
|
31
42
|
return {
|
|
32
43
|
128: "128MB",
|
|
@@ -47,6 +58,7 @@ function secretVersionName(s) {
|
|
|
47
58
|
return `projects/${s.projectId}/secrets/${s.secret}/versions/${(_a = s.version) !== null && _a !== void 0 ? _a : "latest"}`;
|
|
48
59
|
}
|
|
49
60
|
exports.secretVersionName = secretVersionName;
|
|
61
|
+
exports.AllFunctionsPlatforms = ["gcfv1", "gcfv2"];
|
|
50
62
|
function isHttpsTriggered(triggered) {
|
|
51
63
|
return {}.hasOwnProperty.call(triggered, "httpsTrigger");
|
|
52
64
|
}
|
|
@@ -67,6 +79,10 @@ function isTaskQueueTriggered(triggered) {
|
|
|
67
79
|
return {}.hasOwnProperty.call(triggered, "taskQueueTrigger");
|
|
68
80
|
}
|
|
69
81
|
exports.isTaskQueueTriggered = isTaskQueueTriggered;
|
|
82
|
+
function isBlockingTriggered(triggered) {
|
|
83
|
+
return {}.hasOwnProperty.call(triggered, "blockingTrigger");
|
|
84
|
+
}
|
|
85
|
+
exports.isBlockingTriggered = isBlockingTriggered;
|
|
70
86
|
function empty() {
|
|
71
87
|
return {
|
|
72
88
|
requiredAPIs: [],
|
|
@@ -87,6 +103,25 @@ function of(...endpoints) {
|
|
|
87
103
|
return bkend;
|
|
88
104
|
}
|
|
89
105
|
exports.of = of;
|
|
106
|
+
function merge(...backends) {
|
|
107
|
+
const merged = of(...(0, functional_1.flattenArray)(backends.map((b) => allEndpoints(b))));
|
|
108
|
+
const apiToReasons = {};
|
|
109
|
+
for (const b of backends) {
|
|
110
|
+
for (const { api, reason } of b.requiredAPIs) {
|
|
111
|
+
const reasons = apiToReasons[api] || new Set();
|
|
112
|
+
if (reason) {
|
|
113
|
+
reasons.add(reason);
|
|
114
|
+
}
|
|
115
|
+
apiToReasons[api] = reasons;
|
|
116
|
+
}
|
|
117
|
+
merged.environmentVariables = Object.assign(Object.assign({}, merged.environmentVariables), b.environmentVariables);
|
|
118
|
+
}
|
|
119
|
+
for (const [api, reasons] of Object.entries(apiToReasons)) {
|
|
120
|
+
merged.requiredAPIs.push({ api, reason: Array.from(reasons).join(" ") });
|
|
121
|
+
}
|
|
122
|
+
return merged;
|
|
123
|
+
}
|
|
124
|
+
exports.merge = merge;
|
|
90
125
|
function isEmptyBackend(backend) {
|
|
91
126
|
return (Object.keys(backend.requiredAPIs).length === 0 && Object.keys(backend.endpoints).length === 0);
|
|
92
127
|
}
|
|
@@ -207,7 +242,7 @@ function findEndpoint(backend, predicate) {
|
|
|
207
242
|
}
|
|
208
243
|
exports.findEndpoint = findEndpoint;
|
|
209
244
|
function matchingBackend(backend, predicate) {
|
|
210
|
-
const filtered = Object.assign({},
|
|
245
|
+
const filtered = Object.assign(Object.assign({}, backend), { endpoints: {} });
|
|
211
246
|
for (const endpoint of allEndpoints(backend)) {
|
|
212
247
|
if (!predicate(endpoint)) {
|
|
213
248
|
continue;
|
|
@@ -230,10 +265,6 @@ const missingEndpoint = (backend) => (endpoint) => {
|
|
|
230
265
|
return !(0, exports.hasEndpoint)(backend)(endpoint);
|
|
231
266
|
};
|
|
232
267
|
exports.missingEndpoint = missingEndpoint;
|
|
233
|
-
function findEventFilter(endpoint, attribute) {
|
|
234
|
-
return endpoint.eventTrigger.eventFilters.find((ef) => ef.attribute === attribute);
|
|
235
|
-
}
|
|
236
|
-
exports.findEventFilter = findEventFilter;
|
|
237
268
|
function compareFunctions(left, right) {
|
|
238
269
|
if (left.platform !== right.platform) {
|
|
239
270
|
return right.platform < left.platform ? -1 : 1;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveBackend = void 0;
|
|
4
|
+
const backend = require("./backend");
|
|
5
|
+
const proto = require("../../gcp/proto");
|
|
6
|
+
const api = require("../../.../../api");
|
|
7
|
+
const error_1 = require("../../error");
|
|
8
|
+
const functional_1 = require("../../functional");
|
|
9
|
+
function resolveInt(from) {
|
|
10
|
+
if (from == null) {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
else if (typeof from === "string") {
|
|
14
|
+
throw new error_1.FirebaseError("CEL evaluation of expression '" + from + "' not yet supported");
|
|
15
|
+
}
|
|
16
|
+
return from;
|
|
17
|
+
}
|
|
18
|
+
function resolveString(from) {
|
|
19
|
+
if (from == null) {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
else if (from.includes("{{") && from.includes("}}")) {
|
|
23
|
+
throw new error_1.FirebaseError("CEL evaluation of expression '" + from + "' not yet supported");
|
|
24
|
+
}
|
|
25
|
+
return from;
|
|
26
|
+
}
|
|
27
|
+
function resolveBoolean(from) {
|
|
28
|
+
if (from == null) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
else if (typeof from === "string") {
|
|
32
|
+
throw new error_1.FirebaseError("CEL evaluation of expression '" + from + "' not yet supported");
|
|
33
|
+
}
|
|
34
|
+
return from;
|
|
35
|
+
}
|
|
36
|
+
function isMemoryOption(value) {
|
|
37
|
+
return value == null || [128, 256, 512, 1024, 2048, 4096, 8192].includes(value);
|
|
38
|
+
}
|
|
39
|
+
function resolveBackend(build) {
|
|
40
|
+
const bkEndpoints = [];
|
|
41
|
+
for (const endpointId of Object.keys(build.endpoints)) {
|
|
42
|
+
const endpoint = build.endpoints[endpointId];
|
|
43
|
+
let regions = endpoint.region;
|
|
44
|
+
if (typeof regions === "undefined") {
|
|
45
|
+
regions = [api.functionsDefaultRegion];
|
|
46
|
+
}
|
|
47
|
+
for (const region of regions) {
|
|
48
|
+
const trigger = discoverTrigger(endpoint);
|
|
49
|
+
if (typeof endpoint.platform === "undefined") {
|
|
50
|
+
throw new error_1.FirebaseError("platform can't be undefined");
|
|
51
|
+
}
|
|
52
|
+
if (!isMemoryOption(endpoint.availableMemoryMb)) {
|
|
53
|
+
throw new error_1.FirebaseError("available memory must be a supported value, if present");
|
|
54
|
+
}
|
|
55
|
+
let timeout;
|
|
56
|
+
if (endpoint.timeoutSeconds) {
|
|
57
|
+
timeout = resolveInt(endpoint.timeoutSeconds);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
timeout = 60;
|
|
61
|
+
}
|
|
62
|
+
const bkEndpoint = Object.assign({ id: endpointId, project: "", region: region, entryPoint: endpoint.entryPoint, platform: endpoint.platform, runtime: "", labels: endpoint.labels, environmentVariables: endpoint.environmentVariables, secretEnvironmentVariables: undefined, availableMemoryMb: endpoint.availableMemoryMb, timeoutSeconds: timeout }, trigger);
|
|
63
|
+
proto.renameIfPresent(bkEndpoint, endpoint, "maxInstances", "maxInstances", resolveInt);
|
|
64
|
+
proto.renameIfPresent(bkEndpoint, endpoint, "minInstances", "minInstances", resolveInt);
|
|
65
|
+
proto.renameIfPresent(bkEndpoint, endpoint, "concurrency", "concurrency", resolveInt);
|
|
66
|
+
proto.copyIfPresent(bkEndpoint, endpoint, "ingressSettings");
|
|
67
|
+
if (endpoint.vpc) {
|
|
68
|
+
bkEndpoint.vpc = {
|
|
69
|
+
connector: resolveString(endpoint.vpc.connector),
|
|
70
|
+
egressSettings: endpoint.vpc.egressSettings,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (endpoint.serviceAccount) {
|
|
74
|
+
bkEndpoint.serviceAccountEmail = endpoint.serviceAccount;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
bkEndpoint.serviceAccountEmail = "default";
|
|
78
|
+
}
|
|
79
|
+
bkEndpoints.push(bkEndpoint);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const bkend = backend.of(...bkEndpoints);
|
|
83
|
+
bkend.requiredAPIs = build.requiredAPIs;
|
|
84
|
+
return bkend;
|
|
85
|
+
}
|
|
86
|
+
exports.resolveBackend = resolveBackend;
|
|
87
|
+
function discoverTrigger(endpoint) {
|
|
88
|
+
let trigger;
|
|
89
|
+
if ("httpsTrigger" in endpoint) {
|
|
90
|
+
const bkHttps = {};
|
|
91
|
+
if (endpoint.httpsTrigger.invoker) {
|
|
92
|
+
bkHttps.invoker = [endpoint.httpsTrigger.invoker];
|
|
93
|
+
}
|
|
94
|
+
trigger = { httpsTrigger: bkHttps };
|
|
95
|
+
}
|
|
96
|
+
else if ("callableTrigger" in endpoint) {
|
|
97
|
+
trigger = { callableTrigger: {} };
|
|
98
|
+
}
|
|
99
|
+
else if ("blockingTrigger" in endpoint) {
|
|
100
|
+
throw new error_1.FirebaseError("blocking triggers not supported");
|
|
101
|
+
}
|
|
102
|
+
else if ("eventTrigger" in endpoint) {
|
|
103
|
+
const bkEventFilters = {};
|
|
104
|
+
for (const key in endpoint.eventTrigger.eventFilters) {
|
|
105
|
+
if (typeof key === "string") {
|
|
106
|
+
bkEventFilters[key] = resolveString(endpoint.eventTrigger.eventFilters[key]);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const bkEvent = {
|
|
110
|
+
eventType: endpoint.eventTrigger.eventType,
|
|
111
|
+
eventFilters: bkEventFilters,
|
|
112
|
+
retry: resolveBoolean(endpoint.eventTrigger.retry),
|
|
113
|
+
};
|
|
114
|
+
if (endpoint.eventTrigger.serviceAccount) {
|
|
115
|
+
bkEvent.serviceAccountEmail = endpoint.eventTrigger.serviceAccount;
|
|
116
|
+
}
|
|
117
|
+
if (endpoint.eventTrigger.region) {
|
|
118
|
+
bkEvent.region = resolveString(endpoint.eventTrigger.region);
|
|
119
|
+
}
|
|
120
|
+
trigger = { eventTrigger: bkEvent };
|
|
121
|
+
}
|
|
122
|
+
else if ("scheduleTrigger" in endpoint) {
|
|
123
|
+
const bkSchedule = {
|
|
124
|
+
schedule: resolveString(endpoint.scheduleTrigger.schedule),
|
|
125
|
+
timeZone: resolveString(endpoint.scheduleTrigger.timeZone),
|
|
126
|
+
};
|
|
127
|
+
proto.renameIfPresent(bkSchedule, endpoint.scheduleTrigger, "retryConfig", "retryConfig", resolveInt);
|
|
128
|
+
trigger = { scheduleTrigger: bkSchedule };
|
|
129
|
+
}
|
|
130
|
+
else if ("taskQueueTrigger" in endpoint) {
|
|
131
|
+
const bkTaskQueue = {};
|
|
132
|
+
if (endpoint.taskQueueTrigger.rateLimits) {
|
|
133
|
+
const bkRateLimits = {};
|
|
134
|
+
proto.renameIfPresent(bkRateLimits, endpoint.taskQueueTrigger.rateLimits, "maxConcurrentDispatches", "maxConcurrentDispatches", resolveInt);
|
|
135
|
+
proto.renameIfPresent(bkRateLimits, endpoint.taskQueueTrigger.rateLimits, "maxDispatchesPerSecond", "maxDispatchesPerSecond", resolveInt);
|
|
136
|
+
bkTaskQueue.rateLimits = bkRateLimits;
|
|
137
|
+
}
|
|
138
|
+
if (endpoint.taskQueueTrigger.retryConfig) {
|
|
139
|
+
const bkRetryConfig = {};
|
|
140
|
+
proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxAttempts", resolveInt);
|
|
141
|
+
proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "maxBackoffSeconds", "maxBackoffSeconds", (from) => {
|
|
142
|
+
return proto.durationFromSeconds(resolveInt(from));
|
|
143
|
+
});
|
|
144
|
+
proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "minBackoffSeconds", "minBackoffSeconds", (from) => {
|
|
145
|
+
return proto.durationFromSeconds(resolveInt(from));
|
|
146
|
+
});
|
|
147
|
+
proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "maxRetrySeconds", "maxRetryDurationSeconds", (from) => {
|
|
148
|
+
return proto.durationFromSeconds(resolveInt(from));
|
|
149
|
+
});
|
|
150
|
+
proto.renameIfPresent(bkRetryConfig, endpoint.taskQueueTrigger.retryConfig, "maxDoublings", "maxDoublings", resolveInt);
|
|
151
|
+
bkTaskQueue.retryConfig = bkRetryConfig;
|
|
152
|
+
}
|
|
153
|
+
if (endpoint.taskQueueTrigger.invoker) {
|
|
154
|
+
bkTaskQueue.invoker = endpoint.taskQueueTrigger.invoker.map((sa) => resolveString(sa));
|
|
155
|
+
}
|
|
156
|
+
trigger = { taskQueueTrigger: bkTaskQueue };
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
(0, functional_1.assertExhaustive)(endpoint);
|
|
160
|
+
}
|
|
161
|
+
return trigger;
|
|
162
|
+
}
|
|
@@ -5,9 +5,10 @@ const cli_color_1 = require("cli-color");
|
|
|
5
5
|
const logger_1 = require("../../logger");
|
|
6
6
|
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
7
7
|
const error_1 = require("../../error");
|
|
8
|
+
const functional_1 = require("../../functional");
|
|
8
9
|
const iam = require("../../gcp/iam");
|
|
9
10
|
const backend = require("./backend");
|
|
10
|
-
const
|
|
11
|
+
const track_1 = require("../../track");
|
|
11
12
|
const utils = require("../../utils");
|
|
12
13
|
const resourceManager_1 = require("../../gcp/resourceManager");
|
|
13
14
|
const services_1 = require("./services");
|
|
@@ -35,11 +36,14 @@ async function checkServiceAccountIam(projectId) {
|
|
|
35
36
|
}
|
|
36
37
|
exports.checkServiceAccountIam = checkServiceAccountIam;
|
|
37
38
|
async function checkHttpIam(context, options, payload) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
if (!payload.functions) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const filters = context.filters || (0, functionsDeployHelper_1.getEndpointFilters)(options);
|
|
43
|
+
const wantBackends = Object.values(payload.functions).map(({ wantBackend }) => wantBackend);
|
|
44
|
+
const httpEndpoints = [...(0, functional_1.flattenArray)(wantBackends.map((b) => backend.allEndpoints(b)))]
|
|
41
45
|
.filter(backend.isHttpsTriggered)
|
|
42
|
-
.filter((f) => (0, functionsDeployHelper_1.
|
|
46
|
+
.filter((f) => (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(f, filters));
|
|
43
47
|
const existing = await backend.existingBackend(context);
|
|
44
48
|
const newHttpsEndpoints = httpEndpoints.filter(backend.missingEndpoint(existing));
|
|
45
49
|
if (newHttpsEndpoints.length === 0) {
|
|
@@ -56,7 +60,7 @@ async function checkHttpIam(context, options, payload) {
|
|
|
56
60
|
return;
|
|
57
61
|
}
|
|
58
62
|
if (!passed) {
|
|
59
|
-
void track("Error (User)", "deploy:functions:http_create_missing_iam");
|
|
63
|
+
void (0, track_1.track)("Error (User)", "deploy:functions:http_create_missing_iam");
|
|
60
64
|
throw new error_1.FirebaseError(`Missing required permission on project ${(0, cli_color_1.bold)(context.projectId)} to deploy new HTTPS functions. The permission ${(0, cli_color_1.bold)(PERMISSION)} is required to deploy the following functions:\n\n- ` +
|
|
61
65
|
newHttpsEndpoints.map((func) => func.id).join("\n- ") +
|
|
62
66
|
`\n\nTo address this error, please ask a project Owner to assign your account the "Cloud Functions Admin" role at the following URL:\n\nhttps://console.cloud.google.com/iam-admin/iam?project=${context.projectId}`);
|
|
@@ -6,55 +6,62 @@ const clc = require("cli-color");
|
|
|
6
6
|
const fs = require("fs");
|
|
7
7
|
const checkIam_1 = require("./checkIam");
|
|
8
8
|
const utils_1 = require("../../utils");
|
|
9
|
+
const projectConfig_1 = require("../../functions/projectConfig");
|
|
9
10
|
const gcs = require("../../gcp/storage");
|
|
10
11
|
const gcf = require("../../gcp/cloudfunctions");
|
|
11
12
|
const gcfv2 = require("../../gcp/cloudfunctionsv2");
|
|
12
13
|
const backend = require("./backend");
|
|
13
14
|
(0, tmp_1.setGracefulCleanup)();
|
|
14
|
-
async function uploadSourceV1(
|
|
15
|
-
const
|
|
16
|
-
|
|
15
|
+
async function uploadSourceV1(projectId, source, wantBackend) {
|
|
16
|
+
const v1Endpoints = backend.allEndpoints(wantBackend).filter((e) => e.platform === "gcfv1");
|
|
17
|
+
if (v1Endpoints.length === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const region = v1Endpoints[0].region;
|
|
21
|
+
const uploadUrl = await gcf.generateUploadUrl(projectId, region);
|
|
17
22
|
const uploadOpts = {
|
|
18
|
-
file:
|
|
19
|
-
stream: fs.createReadStream(
|
|
23
|
+
file: source.functionsSourceV1,
|
|
24
|
+
stream: fs.createReadStream(source.functionsSourceV1),
|
|
20
25
|
};
|
|
21
26
|
await gcs.upload(uploadOpts, uploadUrl, {
|
|
22
27
|
"x-goog-content-length-range": "0,104857600",
|
|
23
28
|
});
|
|
29
|
+
return uploadUrl;
|
|
24
30
|
}
|
|
25
|
-
async function uploadSourceV2(
|
|
26
|
-
const
|
|
31
|
+
async function uploadSourceV2(projectId, source, wantBackend) {
|
|
32
|
+
const v2Endpoints = backend.allEndpoints(wantBackend).filter((e) => e.platform === "gcfv2");
|
|
33
|
+
if (v2Endpoints.length === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const region = v2Endpoints[0].region;
|
|
37
|
+
const res = await gcfv2.generateUploadUrl(projectId, region);
|
|
27
38
|
const uploadOpts = {
|
|
28
|
-
file:
|
|
29
|
-
stream: fs.createReadStream(
|
|
39
|
+
file: source.functionsSourceV2,
|
|
40
|
+
stream: fs.createReadStream(source.functionsSourceV2),
|
|
30
41
|
};
|
|
31
42
|
await gcs.upload(uploadOpts, res.uploadUrl);
|
|
32
|
-
|
|
43
|
+
return res.storageSource;
|
|
33
44
|
}
|
|
34
|
-
async function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!context.functionsSourceV1 && !context.functionsSourceV2) {
|
|
45
|
+
async function uploadCodebase(context, codebase, wantBackend) {
|
|
46
|
+
var _a;
|
|
47
|
+
const source = (_a = context.sources) === null || _a === void 0 ? void 0 : _a[codebase];
|
|
48
|
+
if (!source || (!source.functionsSourceV1 && !source.functionsSourceV2)) {
|
|
39
49
|
return;
|
|
40
50
|
}
|
|
41
|
-
|
|
51
|
+
const uploads = [];
|
|
42
52
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
47
|
-
|
|
53
|
+
uploads.push(uploadSourceV1(context.projectId, source, wantBackend));
|
|
54
|
+
uploads.push(uploadSourceV2(context.projectId, source, wantBackend));
|
|
55
|
+
const [sourceUrl, storage] = await Promise.all(uploads);
|
|
56
|
+
if (sourceUrl) {
|
|
57
|
+
source.sourceUrl = sourceUrl;
|
|
48
58
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
uploads.push(uploadSourceV2(context, region));
|
|
52
|
-
}
|
|
59
|
+
if (storage) {
|
|
60
|
+
source.storage = storage;
|
|
53
61
|
}
|
|
54
|
-
|
|
55
|
-
const source = context.config.source;
|
|
62
|
+
const sourceDir = (0, projectConfig_1.configForCodebase)(context.config, codebase).source;
|
|
56
63
|
if (uploads.length) {
|
|
57
|
-
(0, utils_1.logSuccess)(`${clc.green.bold("functions:")} ${clc.bold(
|
|
64
|
+
(0, utils_1.logSuccess)(`${clc.green.bold("functions:")} ${clc.bold(sourceDir)} folder uploaded successfully`);
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
catch (err) {
|
|
@@ -62,4 +69,18 @@ async function deploy(context, options, payload) {
|
|
|
62
69
|
throw err;
|
|
63
70
|
}
|
|
64
71
|
}
|
|
72
|
+
async function deploy(context, options, payload) {
|
|
73
|
+
if (!context.config) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!payload.functions) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await (0, checkIam_1.checkHttpIam)(context, options, payload);
|
|
80
|
+
const uploads = [];
|
|
81
|
+
for (const [codebase, { wantBackend }] of Object.entries(payload.functions)) {
|
|
82
|
+
uploads.push(uploadCodebase(context, codebase, wantBackend));
|
|
83
|
+
}
|
|
84
|
+
await Promise.all(uploads);
|
|
85
|
+
}
|
|
65
86
|
exports.deploy = deploy;
|