firebase-tools 10.4.2 → 10.7.0
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/bin/firebase.js +1 -1
- package/lib/command.js +4 -4
- package/lib/commands/deploy.js +1 -1
- package/lib/commands/emulators-start.js +13 -3
- package/lib/commands/ext-configure.js +15 -5
- package/lib/commands/ext-dev-emulators-start.js +5 -1
- 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 +2 -5
- 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 +17 -6
- package/lib/deploy/functions/build.js +162 -0
- package/lib/deploy/functions/checkIam.js +6 -5
- package/lib/deploy/functions/deploy.js +14 -15
- package/lib/deploy/functions/ensure.js +4 -4
- package/lib/deploy/functions/functionsDeployHelper.js +54 -23
- package/lib/deploy/functions/prepare.js +92 -39
- 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 +44 -5
- package/lib/deploy/functions/release/index.js +31 -6
- package/lib/deploy/functions/release/planner.js +10 -8
- 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 +8 -5
- package/lib/deploy/hosting/args.js +2 -0
- package/lib/deploy/hosting/convertConfig.js +37 -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 +26 -20
- package/lib/emulator/auth/state.js +79 -43
- package/lib/emulator/auth/utils.js +3 -25
- package/lib/emulator/commandUtils.js +72 -2
- package/lib/emulator/controller.js +14 -5
- package/lib/emulator/downloadableEmulators.js +47 -24
- 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 +79 -7
- package/lib/emulator/functionsEmulatorShared.js +36 -21
- package/lib/emulator/registry.js +34 -12
- package/lib/emulator/shared/request.js +19 -0
- package/lib/emulator/storage/apis/firebase.js +32 -35
- package/lib/emulator/storage/apis/gcloud.js +84 -66
- package/lib/emulator/storage/files.js +56 -52
- package/lib/emulator/storage/index.js +23 -3
- package/lib/emulator/storage/metadata.js +18 -8
- package/lib/emulator/storage/rules/manager.js +7 -17
- package/lib/emulator/storage/rules/utils.js +11 -3
- 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 +24 -3
- package/lib/functions/runtimeConfigExport.js +10 -6
- package/lib/functions/secrets.js +99 -6
- package/lib/gcp/cloudfunctions.js +37 -18
- package/lib/gcp/cloudfunctionsv2.js +41 -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/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
- package/lib/emulator/storage/list.js +0 -18
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = exports.buildBindingOptionsWithBaseValue = exports.getBaseParamBindings = void 0;
|
|
4
|
-
const _ = require("lodash");
|
|
5
4
|
const path = require("path");
|
|
6
5
|
const clc = require("cli-color");
|
|
7
6
|
const fs = require("fs-extra");
|
|
@@ -9,8 +8,9 @@ const error_1 = require("../error");
|
|
|
9
8
|
const logger_1 = require("../logger");
|
|
10
9
|
const extensionsHelper_1 = require("./extensionsHelper");
|
|
11
10
|
const askUserForParam = require("./askUserForParam");
|
|
12
|
-
const
|
|
11
|
+
const track_1 = require("../track");
|
|
13
12
|
const env = require("../functions/env");
|
|
13
|
+
const utils_1 = require("../utils");
|
|
14
14
|
function getBaseParamBindings(params) {
|
|
15
15
|
let ret = {};
|
|
16
16
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -37,8 +37,9 @@ function setNewDefaults(params, newDefaults) {
|
|
|
37
37
|
}
|
|
38
38
|
exports.setNewDefaults = setNewDefaults;
|
|
39
39
|
function getParamsWithCurrentValuesAsDefaults(extensionInstance) {
|
|
40
|
-
|
|
41
|
-
const
|
|
40
|
+
var _a, _b, _c, _d;
|
|
41
|
+
const specParams = (0, utils_1.cloneDeep)(((_c = (_b = (_a = extensionInstance === null || extensionInstance === void 0 ? void 0 : extensionInstance.config) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.spec) === null || _c === void 0 ? void 0 : _c.params) || []);
|
|
42
|
+
const currentParams = (0, utils_1.cloneDeep)(((_d = extensionInstance === null || extensionInstance === void 0 ? void 0 : extensionInstance.config) === null || _d === void 0 ? void 0 : _d.params) || {});
|
|
42
43
|
return setNewDefaults(specParams, currentParams);
|
|
43
44
|
}
|
|
44
45
|
exports.getParamsWithCurrentValuesAsDefaults = getParamsWithCurrentValuesAsDefaults;
|
|
@@ -71,7 +72,8 @@ async function getParams(args) {
|
|
|
71
72
|
reconfiguring: !!args.reconfiguring,
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
const paramNames = Object.keys(params);
|
|
76
|
+
void (0, track_1.track)("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
|
|
75
77
|
return params;
|
|
76
78
|
}
|
|
77
79
|
exports.getParams = getParams;
|
|
@@ -103,27 +105,31 @@ async function getParamsForUpdate(args) {
|
|
|
103
105
|
instanceId: args.instanceId,
|
|
104
106
|
});
|
|
105
107
|
}
|
|
106
|
-
|
|
108
|
+
const paramNames = Object.keys(params);
|
|
109
|
+
void (0, track_1.track)("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
|
|
107
110
|
return params;
|
|
108
111
|
}
|
|
109
112
|
exports.getParamsForUpdate = getParamsForUpdate;
|
|
110
113
|
async function promptForNewParams(args) {
|
|
111
114
|
const newParamBindingOptions = buildBindingOptionsWithBaseValue(args.currentParams);
|
|
112
115
|
const firebaseProjectParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId);
|
|
113
|
-
const
|
|
116
|
+
const sameParam = (param1) => (param2) => {
|
|
114
117
|
return param1.type === param2.type && param1.param === param2.param;
|
|
115
118
|
};
|
|
119
|
+
const paramDiff = (left, right) => {
|
|
120
|
+
return left.filter((aLeft) => !right.find(sameParam(aLeft)));
|
|
121
|
+
};
|
|
116
122
|
const oldParams = args.spec.params.filter((p) => Object.keys(args.currentParams).includes(p.param));
|
|
117
|
-
let paramsDiffDeletions =
|
|
123
|
+
let paramsDiffDeletions = paramDiff(oldParams, args.newSpec.params);
|
|
118
124
|
paramsDiffDeletions = (0, extensionsHelper_1.substituteParams)(paramsDiffDeletions, firebaseProjectParams);
|
|
119
|
-
let paramsDiffAdditions =
|
|
125
|
+
let paramsDiffAdditions = paramDiff(args.newSpec.params, oldParams);
|
|
120
126
|
paramsDiffAdditions = (0, extensionsHelper_1.substituteParams)(paramsDiffAdditions, firebaseProjectParams);
|
|
121
127
|
if (paramsDiffDeletions.length) {
|
|
122
128
|
logger_1.logger.info("The following params will no longer be used:");
|
|
123
|
-
|
|
129
|
+
for (const param of paramsDiffDeletions) {
|
|
124
130
|
logger_1.logger.info(clc.red(`- ${param.param}: ${args.currentParams[param.param.toUpperCase()]}`));
|
|
125
131
|
delete newParamBindingOptions[param.param.toUpperCase()];
|
|
126
|
-
}
|
|
132
|
+
}
|
|
127
133
|
}
|
|
128
134
|
if (paramsDiffAdditions.length) {
|
|
129
135
|
logger_1.logger.info("To update this instance, configure the following new parameters:");
|
|
@@ -144,10 +150,10 @@ function getParamsFromFile(args) {
|
|
|
144
150
|
let envParams;
|
|
145
151
|
try {
|
|
146
152
|
envParams = readEnvFile(args.paramsEnvPath);
|
|
147
|
-
void track("Extension Env File", "Present");
|
|
153
|
+
void (0, track_1.track)("Extension Env File", "Present");
|
|
148
154
|
}
|
|
149
155
|
catch (err) {
|
|
150
|
-
void track("Extension Env File", "Invalid");
|
|
156
|
+
void (0, track_1.track)("Extension Env File", "Invalid");
|
|
151
157
|
throw new error_1.FirebaseError(`Error reading env file: ${err.message}\n`, { original: err });
|
|
152
158
|
}
|
|
153
159
|
const params = (0, extensionsHelper_1.populateDefaultParams)(envParams, args.paramSpecs);
|
|
@@ -19,8 +19,8 @@ async function checkProductsProvisioned(projectId, spec) {
|
|
|
19
19
|
exports.checkProductsProvisioned = checkProductsProvisioned;
|
|
20
20
|
async function bulkCheckProductsProvisioned(projectId, instanceSpecs) {
|
|
21
21
|
const usedProducts = await Promise.all(instanceSpecs.map(async (i) => {
|
|
22
|
-
const
|
|
23
|
-
return getUsedProducts(
|
|
22
|
+
const extensionSpec = await (0, planner_1.getExtensionSpec)(i);
|
|
23
|
+
return getUsedProducts(extensionSpec);
|
|
24
24
|
}));
|
|
25
25
|
await checkProducts(projectId, [...(0, functional_1.flattenArray)(usedProducts)]);
|
|
26
26
|
}
|
|
@@ -49,11 +49,11 @@ const toListEntry = (i) => {
|
|
|
49
49
|
};
|
|
50
50
|
async function displayWarningsForDeploy(instancesToCreate) {
|
|
51
51
|
const trustedPublishers = await (0, resolveSource_1.getTrustedPublishers)();
|
|
52
|
-
|
|
52
|
+
const publishedExtensionInstances = instancesToCreate.filter((i) => i.ref);
|
|
53
|
+
for (const i of publishedExtensionInstances) {
|
|
53
54
|
await (0, planner_1.getExtension)(i);
|
|
54
|
-
await (0, planner_1.getExtensionVersion)(i);
|
|
55
55
|
}
|
|
56
|
-
const [eapExtensions, nonEapExtensions] = (0, functional_1.partition)(
|
|
56
|
+
const [eapExtensions, nonEapExtensions] = (0, functional_1.partition)(publishedExtensionInstances, (i) => { var _a, _b; return !trustedPublishers.includes((_b = (_a = i.ref) === null || _a === void 0 ? void 0 : _a.publisherId) !== null && _b !== void 0 ? _b : ""); });
|
|
57
57
|
const experimental = nonEapExtensions.filter((i) => i.extension.registryLaunchStage === extensionsApi_1.RegistryLaunchStage.EXPERIMENTAL);
|
|
58
58
|
if (experimental.length) {
|
|
59
59
|
const humanReadableList = experimental.map((i) => `\t${(0, deploymentSummary_1.humanReadable)(i)}`).join("\n");
|
package/lib/functions/env.js
CHANGED
|
@@ -42,6 +42,15 @@ const LINE_RE = new RegExp("^" +
|
|
|
42
42
|
"\\s*" +
|
|
43
43
|
"(?:#[^\\n]*)?" +
|
|
44
44
|
"$", "gms");
|
|
45
|
+
const ESCAPE_SEQUENCES_TO_CHARACTERS = {
|
|
46
|
+
"\\n": "\n",
|
|
47
|
+
"\\r": "\r",
|
|
48
|
+
"\\t": "\t",
|
|
49
|
+
"\\v": "\v",
|
|
50
|
+
"\\\\": "\\",
|
|
51
|
+
"\\'": "'",
|
|
52
|
+
'\\"': '"',
|
|
53
|
+
};
|
|
45
54
|
function parse(data) {
|
|
46
55
|
const envs = {};
|
|
47
56
|
const errors = [];
|
|
@@ -54,8 +63,7 @@ function parse(data) {
|
|
|
54
63
|
if ((quotesMatch = /^(["'])(.*)\1$/ms.exec(v)) != null) {
|
|
55
64
|
v = quotesMatch[2];
|
|
56
65
|
if (quotesMatch[1] === '"') {
|
|
57
|
-
v = v.replace(
|
|
58
|
-
v = v.replace(/\\([\\'"])/g, "$1");
|
|
66
|
+
v = v.replace(/\\[nrtv\\'"]/g, (match) => ESCAPE_SEQUENCES_TO_CHARACTERS[match]);
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
envs[k] = v;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AUTH_BLOCKING_EVENTS = exports.BEFORE_SIGN_IN_EVENT = exports.BEFORE_CREATE_EVENT = void 0;
|
|
4
|
+
exports.BEFORE_CREATE_EVENT = "providers/cloud.auth/eventTypes/user.beforeCreate";
|
|
5
|
+
exports.BEFORE_SIGN_IN_EVENT = "providers/cloud.auth/eventTypes/user.beforeSignIn";
|
|
6
|
+
exports.AUTH_BLOCKING_EVENTS = [exports.BEFORE_CREATE_EVENT, exports.BEFORE_SIGN_IN_EVENT];
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.normalizeAndValidate = exports.validate = exports.normalize = void 0;
|
|
3
|
+
exports.normalizeAndValidate = exports.validate = exports.normalize = exports.DEFAULT_CODEBASE = void 0;
|
|
4
4
|
const error_1 = require("../error");
|
|
5
|
+
exports.DEFAULT_CODEBASE = "default";
|
|
5
6
|
function normalize(config) {
|
|
6
7
|
if (!config) {
|
|
7
8
|
throw new error_1.FirebaseError("No valid functions configuration detected in firebase.json");
|
|
@@ -19,13 +20,33 @@ function validateSingle(config) {
|
|
|
19
20
|
if (!config.source) {
|
|
20
21
|
throw new error_1.FirebaseError("functions.source must be specified");
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
+
if (!config.codebase) {
|
|
24
|
+
config.codebase = exports.DEFAULT_CODEBASE;
|
|
25
|
+
}
|
|
26
|
+
if (config.codebase.length > 63 || !/^[a-z0-9_-]+$/.test(config.codebase)) {
|
|
27
|
+
throw new error_1.FirebaseError("Invalid codebase name. Codebase must be less than 63 characters and " +
|
|
28
|
+
"can contain only lowercase letters, numeric characters, underscores, and dashes.");
|
|
29
|
+
}
|
|
30
|
+
return Object.assign(Object.assign({}, config), { source: config.source, codebase: config.codebase });
|
|
31
|
+
}
|
|
32
|
+
function assertUnique(config, property) {
|
|
33
|
+
const values = new Set();
|
|
34
|
+
for (const single of config) {
|
|
35
|
+
const value = single[property];
|
|
36
|
+
if (values.has(value)) {
|
|
37
|
+
throw new error_1.FirebaseError(`functions.${property} must be unique but '${value}' was used more than once.`);
|
|
38
|
+
}
|
|
39
|
+
values.add(value);
|
|
40
|
+
}
|
|
23
41
|
}
|
|
24
42
|
function validate(config) {
|
|
25
43
|
if (config.length > 1) {
|
|
26
44
|
throw new error_1.FirebaseError("More than one functions.source detected in firebase.json.");
|
|
27
45
|
}
|
|
28
|
-
|
|
46
|
+
const validated = validateSingle(config[0]);
|
|
47
|
+
assertUnique([validated], "source");
|
|
48
|
+
assertUnique([validated], "codebase");
|
|
49
|
+
return [validated];
|
|
29
50
|
}
|
|
30
51
|
exports.validate = validate;
|
|
31
52
|
function normalizeAndValidate(config) {
|
|
@@ -115,13 +115,17 @@ function hydrateEnvs(pInfos, prefix) {
|
|
|
115
115
|
return errMsg;
|
|
116
116
|
}
|
|
117
117
|
exports.hydrateEnvs = hydrateEnvs;
|
|
118
|
+
const CHARACTERS_TO_ESCAPE_SEQUENCES = {
|
|
119
|
+
"\n": "\\n",
|
|
120
|
+
"\r": "\\r",
|
|
121
|
+
"\t": "\\t",
|
|
122
|
+
"\v": "\\v",
|
|
123
|
+
"\\": "\\\\",
|
|
124
|
+
'"': '\\"',
|
|
125
|
+
"'": "\\'",
|
|
126
|
+
};
|
|
118
127
|
function escape(s) {
|
|
119
|
-
|
|
120
|
-
.replace("\n", "\\n")
|
|
121
|
-
.replace("\r", "\\r")
|
|
122
|
-
.replace("\t", "\\t")
|
|
123
|
-
.replace("\v", "\\v");
|
|
124
|
-
return result.replace(/(['"])/g, "\\$1");
|
|
128
|
+
return s.replace(/[\n\r\t\v\\"']/g, (ch) => CHARACTERS_TO_ESCAPE_SEQUENCES[ch]);
|
|
125
129
|
}
|
|
126
130
|
function toDotenvFormat(envs, header = "") {
|
|
127
131
|
const lines = envs.map(({ newKey, value }) => `${newKey}="${escape(value)}"`);
|
package/lib/functions/secrets.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.pruneSecrets = exports.of = exports.ensureSecret = exports.ensureValidKey = exports.labels = exports.isFirebaseManaged = void 0;
|
|
3
|
+
exports.updateEndpointSecret = exports.pruneAndDestroySecrets = exports.pruneSecrets = exports.inUse = exports.of = exports.ensureSecret = exports.ensureValidKey = exports.labels = exports.isFirebaseManaged = void 0;
|
|
4
|
+
const utils = require("../utils");
|
|
5
|
+
const poller = require("../operation-poller");
|
|
6
|
+
const gcf = require("../gcp/cloudfunctions");
|
|
4
7
|
const secretManager_1 = require("../gcp/secretManager");
|
|
5
8
|
const error_1 = require("../error");
|
|
6
9
|
const utils_1 = require("../utils");
|
|
7
10
|
const prompt_1 = require("../prompt");
|
|
8
11
|
const env_1 = require("./env");
|
|
12
|
+
const logger_1 = require("../logger");
|
|
13
|
+
const api_1 = require("../api");
|
|
14
|
+
const functional_1 = require("../functional");
|
|
9
15
|
const FIREBASE_MANGED = "firebase-managed";
|
|
10
16
|
function isFirebaseManaged(secret) {
|
|
11
17
|
return Object.keys(secret.labels || []).includes(FIREBASE_MANGED);
|
|
@@ -17,8 +23,7 @@ function labels() {
|
|
|
17
23
|
exports.labels = labels;
|
|
18
24
|
function toUpperSnakeCase(key) {
|
|
19
25
|
return key
|
|
20
|
-
.replace(
|
|
21
|
-
.replace(".", "_")
|
|
26
|
+
.replace(/[.-]/g, "_")
|
|
22
27
|
.replace(/([a-z])([A-Z])/g, "$1_$2")
|
|
23
28
|
.toUpperCase();
|
|
24
29
|
}
|
|
@@ -80,19 +85,40 @@ function of(endpoints) {
|
|
|
80
85
|
return endpoints.reduce((envs, endpoint) => [...envs, ...(endpoint.secretEnvironmentVariables || [])], []);
|
|
81
86
|
}
|
|
82
87
|
exports.of = of;
|
|
88
|
+
function inUse(projectInfo, secret, endpoint) {
|
|
89
|
+
const { projectId, projectNumber } = projectInfo;
|
|
90
|
+
for (const sev of of([endpoint])) {
|
|
91
|
+
if ((sev.projectId === projectId || sev.projectId === projectNumber) &&
|
|
92
|
+
sev.secret === secret.name) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
exports.inUse = inUse;
|
|
83
99
|
async function pruneSecrets(projectInfo, endpoints) {
|
|
84
100
|
const { projectId, projectNumber } = projectInfo;
|
|
85
101
|
const pruneKey = (name, version) => `${name}@${version}`;
|
|
86
102
|
const prunedSecrets = new Set();
|
|
87
103
|
const haveSecrets = await (0, secretManager_1.listSecrets)(projectId, `labels.${FIREBASE_MANGED}=true`);
|
|
88
104
|
for (const secret of haveSecrets) {
|
|
89
|
-
const versions = await (0, secretManager_1.listSecretVersions)(projectId, secret.name, `state:
|
|
105
|
+
const versions = await (0, secretManager_1.listSecretVersions)(projectId, secret.name, `NOT state: DESTROYED`);
|
|
90
106
|
for (const version of versions) {
|
|
91
107
|
prunedSecrets.add(pruneKey(secret.name, version.versionId));
|
|
92
108
|
}
|
|
93
109
|
}
|
|
94
|
-
const
|
|
95
|
-
for (const
|
|
110
|
+
const secrets = [];
|
|
111
|
+
for (const secret of of(endpoints)) {
|
|
112
|
+
if (!secret.version) {
|
|
113
|
+
throw new error_1.FirebaseError(`Secret ${secret.secret} version is unexpectedly empty.`);
|
|
114
|
+
}
|
|
115
|
+
if (secret.projectId === projectId || secret.projectId === projectNumber) {
|
|
116
|
+
if (secret.version) {
|
|
117
|
+
secrets.push(Object.assign(Object.assign({}, secret), { version: secret.version }));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const sev of secrets) {
|
|
96
122
|
let name = sev.secret;
|
|
97
123
|
if (name.includes("/")) {
|
|
98
124
|
const secret = (0, secretManager_1.parseSecretResourceName)(name);
|
|
@@ -110,3 +136,70 @@ async function pruneSecrets(projectInfo, endpoints) {
|
|
|
110
136
|
.map(([secret, version]) => ({ projectId, version, secret, key: secret }));
|
|
111
137
|
}
|
|
112
138
|
exports.pruneSecrets = pruneSecrets;
|
|
139
|
+
async function pruneAndDestroySecrets(projectInfo, endpoints) {
|
|
140
|
+
const { projectId, projectNumber } = projectInfo;
|
|
141
|
+
logger_1.logger.debug("Pruning secrets to find unused secret versions...");
|
|
142
|
+
const unusedSecrets = await module.exports.pruneSecrets({ projectId, projectNumber }, endpoints);
|
|
143
|
+
if (unusedSecrets.length === 0) {
|
|
144
|
+
return { destroyed: [], erred: [] };
|
|
145
|
+
}
|
|
146
|
+
const destroyed = [];
|
|
147
|
+
const erred = [];
|
|
148
|
+
const msg = unusedSecrets.map((s) => `${s.secret}@${s.version}`);
|
|
149
|
+
logger_1.logger.debug(`Found unused secret versions: ${msg}. Destroying them...`);
|
|
150
|
+
const destroyResults = await utils.allSettled(unusedSecrets.map(async (sev) => {
|
|
151
|
+
await (0, secretManager_1.destroySecretVersion)(sev.projectId, sev.secret, sev.version);
|
|
152
|
+
return sev;
|
|
153
|
+
}));
|
|
154
|
+
for (const result of destroyResults) {
|
|
155
|
+
if (result.status === "fulfilled") {
|
|
156
|
+
destroyed.push(result.value);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
erred.push(result.reason);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { destroyed, erred };
|
|
163
|
+
}
|
|
164
|
+
exports.pruneAndDestroySecrets = pruneAndDestroySecrets;
|
|
165
|
+
async function updateEndpointSecret(projectInfo, secretVersion, endpoint) {
|
|
166
|
+
const { projectId, projectNumber } = projectInfo;
|
|
167
|
+
if (!inUse(projectInfo, secretVersion.secret, endpoint)) {
|
|
168
|
+
return endpoint;
|
|
169
|
+
}
|
|
170
|
+
const updatedSevs = [];
|
|
171
|
+
for (const sev of of([endpoint])) {
|
|
172
|
+
const updatedSev = Object.assign({}, sev);
|
|
173
|
+
if ((updatedSev.projectId === projectId || updatedSev.projectId === projectNumber) &&
|
|
174
|
+
updatedSev.secret === secretVersion.secret.name) {
|
|
175
|
+
updatedSev.version = secretVersion.versionId;
|
|
176
|
+
}
|
|
177
|
+
updatedSevs.push(updatedSev);
|
|
178
|
+
}
|
|
179
|
+
if (endpoint.platform === "gcfv1") {
|
|
180
|
+
const fn = gcf.functionFromEndpoint(endpoint, "");
|
|
181
|
+
const op = await gcf.updateFunction({
|
|
182
|
+
name: fn.name,
|
|
183
|
+
runtime: fn.runtime,
|
|
184
|
+
entryPoint: fn.entryPoint,
|
|
185
|
+
secretEnvironmentVariables: updatedSevs,
|
|
186
|
+
});
|
|
187
|
+
const gcfV1PollerOptions = {
|
|
188
|
+
apiOrigin: api_1.functionsOrigin,
|
|
189
|
+
apiVersion: gcf.API_VERSION,
|
|
190
|
+
masterTimeout: 25 * 60 * 1000,
|
|
191
|
+
maxBackoff: 10000,
|
|
192
|
+
pollerName: `update-${endpoint.region}-${endpoint.id}`,
|
|
193
|
+
operationResourceName: op.name,
|
|
194
|
+
};
|
|
195
|
+
const cfn = await poller.pollOperation(gcfV1PollerOptions);
|
|
196
|
+
return gcf.endpointFromFunction(cfn);
|
|
197
|
+
}
|
|
198
|
+
else if (endpoint.platform === "gcfv2") {
|
|
199
|
+
throw new error_1.FirebaseError(`Unsupported platform ${endpoint.platform}`);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
(0, functional_1.assertExhaustive)(endpoint.platform);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.updateEndpointSecret = updateEndpointSecret;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.API_VERSION = void 0;
|
|
3
|
+
exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.BLOCKING_LABEL = exports.CODEBASE_LABEL = exports.API_VERSION = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const error_1 = require("../error");
|
|
6
6
|
const logger_1 = require("../logger");
|
|
@@ -9,10 +9,21 @@ const backend = require("../deploy/functions/backend");
|
|
|
9
9
|
const utils = require("../utils");
|
|
10
10
|
const proto = require("./proto");
|
|
11
11
|
const runtimes = require("../deploy/functions/runtimes");
|
|
12
|
+
const projectConfig = require("../functions/projectConfig");
|
|
12
13
|
const apiv2_1 = require("../apiv2");
|
|
13
14
|
const api_1 = require("../api");
|
|
14
15
|
exports.API_VERSION = "v1";
|
|
16
|
+
exports.CODEBASE_LABEL = "firebase-functions-codebase";
|
|
15
17
|
const client = new apiv2_1.Client({ urlPrefix: api_1.functionsOrigin, apiVersion: exports.API_VERSION });
|
|
18
|
+
exports.BLOCKING_LABEL = "deployment-blocking";
|
|
19
|
+
const BLOCKING_LABEL_KEY_TO_EVENT = {
|
|
20
|
+
"before-create": "providers/cloud.auth/eventTypes/user.beforeCreate",
|
|
21
|
+
"before-sign-in": "providers/cloud.auth/eventTypes/user.beforeSignIn",
|
|
22
|
+
};
|
|
23
|
+
const BLOCKING_EVENT_TO_LABEL_KEY = {
|
|
24
|
+
"providers/cloud.auth/eventTypes/user.beforeCreate": "before-create",
|
|
25
|
+
"providers/cloud.auth/eventTypes/user.beforeSignIn": "before-sign-in",
|
|
26
|
+
};
|
|
16
27
|
function validateFunction(func) {
|
|
17
28
|
proto.assertOneOf("Cloud Function", func, "sourceCode", "sourceArchiveUrl", "sourceRepository", "sourceUploadUrl");
|
|
18
29
|
proto.assertOneOf("Cloud Function", func, "trigger", "httpsTrigger", "eventTrigger");
|
|
@@ -202,7 +213,7 @@ async function listAllFunctions(projectId) {
|
|
|
202
213
|
}
|
|
203
214
|
exports.listAllFunctions = listAllFunctions;
|
|
204
215
|
function endpointFromFunction(gcfFunction) {
|
|
205
|
-
var _a, _b, _c, _d, _e;
|
|
216
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
206
217
|
const [, project, , region, , id] = gcfFunction.name.split("/");
|
|
207
218
|
let trigger;
|
|
208
219
|
let uri;
|
|
@@ -223,25 +234,29 @@ function endpointFromFunction(gcfFunction) {
|
|
|
223
234
|
callableTrigger: {},
|
|
224
235
|
};
|
|
225
236
|
}
|
|
237
|
+
else if ((_e = gcfFunction.labels) === null || _e === void 0 ? void 0 : _e[exports.BLOCKING_LABEL]) {
|
|
238
|
+
trigger = {
|
|
239
|
+
blockingTrigger: {
|
|
240
|
+
eventType: BLOCKING_LABEL_KEY_TO_EVENT[gcfFunction.labels[exports.BLOCKING_LABEL]],
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
226
244
|
else if (gcfFunction.httpsTrigger) {
|
|
227
245
|
trigger = { httpsTrigger: {} };
|
|
228
|
-
uri = gcfFunction.httpsTrigger.url;
|
|
229
|
-
securityLevel = gcfFunction.httpsTrigger.securityLevel;
|
|
230
246
|
}
|
|
231
247
|
else {
|
|
232
248
|
trigger = {
|
|
233
249
|
eventTrigger: {
|
|
234
250
|
eventType: gcfFunction.eventTrigger.eventType,
|
|
235
|
-
eventFilters:
|
|
236
|
-
|
|
237
|
-
attribute: "resource",
|
|
238
|
-
value: gcfFunction.eventTrigger.resource,
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
retry: !!((_e = gcfFunction.eventTrigger.failurePolicy) === null || _e === void 0 ? void 0 : _e.retry),
|
|
251
|
+
eventFilters: { resource: gcfFunction.eventTrigger.resource },
|
|
252
|
+
retry: !!((_f = gcfFunction.eventTrigger.failurePolicy) === null || _f === void 0 ? void 0 : _f.retry),
|
|
242
253
|
},
|
|
243
254
|
};
|
|
244
255
|
}
|
|
256
|
+
if (gcfFunction.httpsTrigger) {
|
|
257
|
+
uri = gcfFunction.httpsTrigger.url;
|
|
258
|
+
securityLevel = gcfFunction.httpsTrigger.securityLevel;
|
|
259
|
+
}
|
|
245
260
|
if (!runtimes.isValidRuntime(gcfFunction.runtime)) {
|
|
246
261
|
logger_1.logger.debug("GCFv1 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
|
|
247
262
|
}
|
|
@@ -254,11 +269,13 @@ function endpointFromFunction(gcfFunction) {
|
|
|
254
269
|
if (securityLevel) {
|
|
255
270
|
endpoint.securityLevel = securityLevel;
|
|
256
271
|
}
|
|
257
|
-
proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "
|
|
272
|
+
proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "labels", "environmentVariables", "secretEnvironmentVariables", "sourceUploadUrl");
|
|
273
|
+
proto.renameIfPresent(endpoint, gcfFunction, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
258
274
|
if (gcfFunction.vpcConnector) {
|
|
259
275
|
endpoint.vpc = { connector: gcfFunction.vpcConnector };
|
|
260
276
|
proto.renameIfPresent(endpoint.vpc, gcfFunction, "egressSettings", "vpcConnectorEgressSettings");
|
|
261
277
|
}
|
|
278
|
+
endpoint.codebase = ((_g = gcfFunction.labels) === null || _g === void 0 ? void 0 : _g[exports.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
|
|
262
279
|
return endpoint;
|
|
263
280
|
}
|
|
264
281
|
exports.endpointFromFunction = endpointFromFunction;
|
|
@@ -278,13 +295,9 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
|
|
|
278
295
|
};
|
|
279
296
|
proto.copyIfPresent(gcfFunction, endpoint, "labels");
|
|
280
297
|
if (backend.isEventTriggered(endpoint)) {
|
|
281
|
-
const resourceFilter = backend.findEventFilter(endpoint, "resource");
|
|
282
|
-
if (!resourceFilter) {
|
|
283
|
-
throw new error_1.FirebaseError("Invalid event trigger definition. Expected event filter with 'resource' attribute.");
|
|
284
|
-
}
|
|
285
298
|
gcfFunction.eventTrigger = {
|
|
286
299
|
eventType: endpoint.eventTrigger.eventType,
|
|
287
|
-
resource:
|
|
300
|
+
resource: endpoint.eventTrigger.eventFilters.resource,
|
|
288
301
|
};
|
|
289
302
|
gcfFunction.eventTrigger.failurePolicy = endpoint.eventTrigger.retry
|
|
290
303
|
? { retry: {} }
|
|
@@ -302,6 +315,10 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
|
|
|
302
315
|
gcfFunction.httpsTrigger = {};
|
|
303
316
|
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
|
|
304
317
|
}
|
|
318
|
+
else if (backend.isBlockingTriggered(endpoint)) {
|
|
319
|
+
gcfFunction.httpsTrigger = {};
|
|
320
|
+
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.BLOCKING_LABEL]: BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
|
|
321
|
+
}
|
|
305
322
|
else {
|
|
306
323
|
gcfFunction.httpsTrigger = {};
|
|
307
324
|
if (backend.isCallableTriggered(endpoint)) {
|
|
@@ -311,11 +328,13 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
|
|
|
311
328
|
gcfFunction.httpsTrigger.securityLevel = endpoint.securityLevel;
|
|
312
329
|
}
|
|
313
330
|
}
|
|
314
|
-
proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "
|
|
331
|
+
proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "environmentVariables", "secretEnvironmentVariables");
|
|
332
|
+
proto.renameIfPresent(gcfFunction, endpoint, "timeout", "timeoutSeconds", proto.durationFromSeconds);
|
|
315
333
|
if (endpoint.vpc) {
|
|
316
334
|
proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnector", "connector");
|
|
317
335
|
proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnectorEgressSettings", "egressSettings");
|
|
318
336
|
}
|
|
337
|
+
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.CODEBASE_LABEL]: endpoint.codebase || projectConfig.DEFAULT_CODEBASE });
|
|
319
338
|
return gcfFunction;
|
|
320
339
|
}
|
|
321
340
|
exports.functionFromEndpoint = functionFromEndpoint;
|