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.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
|
|
3
|
+
exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const ora = require("ora");
|
|
@@ -305,7 +305,7 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
305
305
|
"Please create one and add an entry for this version. " +
|
|
306
306
|
marked("See https://firebase.google.com/docs/extensions/alpha/create-user-docs#writing-changelog for more details."));
|
|
307
307
|
}
|
|
308
|
-
if (!notes && extension) {
|
|
308
|
+
if (!notes && !semver.prerelease(extensionSpec.version) && extension) {
|
|
309
309
|
throw new error_1.FirebaseError(`No entry for version ${extensionSpec.version} found in CHANGELOG.md. ` +
|
|
310
310
|
"Please add one so users know what has changed in this version. " +
|
|
311
311
|
marked("See https://firebase.google.com/docs/extensions/alpha/create-user-docs#writing-changelog for more details."));
|
|
@@ -363,32 +363,26 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
363
363
|
}
|
|
364
364
|
exports.publishExtensionVersionFromLocalSource = publishExtensionVersionFromLocalSource;
|
|
365
365
|
async function createSourceFromLocation(projectId, sourceUri) {
|
|
366
|
+
const extensionRoot = "/";
|
|
366
367
|
let packageUri;
|
|
367
|
-
let extensionRoot;
|
|
368
368
|
let objectPath = "";
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
uploadSpinner.fail();
|
|
380
|
-
throw new error_1.FirebaseError(`Failed to archive and upload extension source, ${err}`, {
|
|
381
|
-
original: err,
|
|
382
|
-
});
|
|
383
|
-
}
|
|
369
|
+
const spinner = ora(" Archiving and uploading extension source code");
|
|
370
|
+
try {
|
|
371
|
+
spinner.start();
|
|
372
|
+
objectPath = await archiveAndUploadSource(sourceUri, exports.EXTENSIONS_BUCKET_NAME);
|
|
373
|
+
spinner.succeed(" Uploaded extension source code");
|
|
374
|
+
packageUri = api_1.storageOrigin + objectPath + "?alt=media";
|
|
375
|
+
const res = await (0, extensionsApi_1.createSource)(projectId, packageUri, extensionRoot);
|
|
376
|
+
logger_1.logger.debug("Created new Extension Source %s", res.name);
|
|
377
|
+
await deleteUploadedSource(objectPath);
|
|
378
|
+
return res;
|
|
384
379
|
}
|
|
385
|
-
|
|
386
|
-
|
|
380
|
+
catch (err) {
|
|
381
|
+
spinner.fail();
|
|
382
|
+
throw new error_1.FirebaseError(`Failed to archive and upload extension source from ${sourceUri}, ${err}`, {
|
|
383
|
+
original: err,
|
|
384
|
+
});
|
|
387
385
|
}
|
|
388
|
-
const res = await (0, extensionsApi_1.createSource)(projectId, packageUri, extensionRoot);
|
|
389
|
-
logger_1.logger.debug("Created new Extension Source %s", res.name);
|
|
390
|
-
await deleteUploadedSource(objectPath);
|
|
391
|
-
return res;
|
|
392
386
|
}
|
|
393
387
|
exports.createSourceFromLocation = createSourceFromLocation;
|
|
394
388
|
async function deleteUploadedSource(objectPath) {
|
|
@@ -535,3 +529,15 @@ async function diagnoseAndFixProject(options) {
|
|
|
535
529
|
}
|
|
536
530
|
}
|
|
537
531
|
exports.diagnoseAndFixProject = diagnoseAndFixProject;
|
|
532
|
+
function canonicalizeRefInput(extensionName) {
|
|
533
|
+
if (extensionName.split("/").length < 2) {
|
|
534
|
+
const [extensionID, version] = extensionName.split("@");
|
|
535
|
+
extensionName = `firebase/${extensionID}@${version || "latest"}`;
|
|
536
|
+
}
|
|
537
|
+
const ref = refs.parse(extensionName);
|
|
538
|
+
if (!ref.version) {
|
|
539
|
+
extensionName = `${extensionName}@latest`;
|
|
540
|
+
}
|
|
541
|
+
return extensionName;
|
|
542
|
+
}
|
|
543
|
+
exports.canonicalizeRefInput = canonicalizeRefInput;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeLocalSecrets = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
|
|
3
|
+
exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.getInstanceTarget = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeLocalSecrets = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const refs = require("./refs");
|
|
7
7
|
const config_1 = require("../config");
|
|
8
|
+
const planner_1 = require("../deploy/extensions/planner");
|
|
8
9
|
const logger_1 = require("../logger");
|
|
9
10
|
const prompt_1 = require("../prompt");
|
|
10
11
|
const paramHelper_1 = require("./paramHelper");
|
|
@@ -42,11 +43,12 @@ async function writeToManifest(specs, config, options, allowOverwrite = false) {
|
|
|
42
43
|
exports.writeToManifest = writeToManifest;
|
|
43
44
|
async function writeLocalSecrets(specs, config, force) {
|
|
44
45
|
for (const spec of specs) {
|
|
45
|
-
|
|
46
|
+
const extensionSpec = await (0, planner_1.getExtensionSpec)(spec);
|
|
47
|
+
if (!extensionSpec.params) {
|
|
46
48
|
continue;
|
|
47
49
|
}
|
|
48
50
|
const writeBuffer = {};
|
|
49
|
-
const locallyOverridenSecretParams =
|
|
51
|
+
const locallyOverridenSecretParams = extensionSpec.params.filter((p) => p.type === extensionsApi_1.ParamType.SECRET && spec.params[p.param].local);
|
|
50
52
|
for (const paramSpec of locallyOverridenSecretParams) {
|
|
51
53
|
const key = paramSpec.param;
|
|
52
54
|
const localValue = spec.params[key].local;
|
|
@@ -97,18 +99,35 @@ function instanceExists(instanceId, config) {
|
|
|
97
99
|
return !!config.get("extensions", {})[instanceId];
|
|
98
100
|
}
|
|
99
101
|
exports.instanceExists = instanceExists;
|
|
100
|
-
function
|
|
102
|
+
function getInstanceTarget(instanceId, config) {
|
|
101
103
|
if (!instanceExists(instanceId, config)) {
|
|
102
104
|
throw new error_1.FirebaseError(`Could not find extension instance ${instanceId} in firebase.json`);
|
|
103
105
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
return config.get("extensions", {})[instanceId];
|
|
107
|
+
}
|
|
108
|
+
exports.getInstanceTarget = getInstanceTarget;
|
|
109
|
+
function getInstanceRef(instanceId, config) {
|
|
110
|
+
const source = getInstanceTarget(instanceId, config);
|
|
111
|
+
if ((0, extensionsHelper_1.isLocalPath)(source)) {
|
|
112
|
+
throw new error_1.FirebaseError(`Extension instance ${instanceId} doesn't have a ref because it is from a local source`);
|
|
113
|
+
}
|
|
114
|
+
return refs.parse(source);
|
|
106
115
|
}
|
|
107
116
|
exports.getInstanceRef = getInstanceRef;
|
|
108
117
|
function writeExtensionsToFirebaseJson(specs, config) {
|
|
109
118
|
const extensions = config.get("extensions", {});
|
|
110
119
|
for (const s of specs) {
|
|
111
|
-
|
|
120
|
+
let target;
|
|
121
|
+
if (s.ref) {
|
|
122
|
+
target = refs.toExtensionVersionRef(s.ref);
|
|
123
|
+
}
|
|
124
|
+
else if (s.localPath) {
|
|
125
|
+
target = s.localPath;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
throw new error_1.FirebaseError(`Unable to resolve ManifestInstanceSpec, make sure you provide either extension ref or a local path to extension source code`);
|
|
129
|
+
}
|
|
130
|
+
extensions[s.instanceId] = target;
|
|
112
131
|
}
|
|
113
132
|
config.set("extensions", extensions);
|
|
114
133
|
config.writeProjectFile("firebase.json", config.src);
|
|
@@ -117,6 +136,7 @@ function writeExtensionsToFirebaseJson(specs, config) {
|
|
|
117
136
|
async function writeEnvFiles(specs, config, force) {
|
|
118
137
|
for (const spec of specs) {
|
|
119
138
|
const content = Object.entries(spec.params)
|
|
139
|
+
.filter((r) => r[1].baseValue !== "")
|
|
120
140
|
.sort((a, b) => {
|
|
121
141
|
return a[0].localeCompare(b[0]);
|
|
122
142
|
})
|
|
@@ -170,7 +190,7 @@ function showDeprecationWarning() {
|
|
|
170
190
|
}
|
|
171
191
|
exports.showDeprecationWarning = showDeprecationWarning;
|
|
172
192
|
function showPreviewWarning() {
|
|
173
|
-
utils.logLabeledWarning(extensionsHelper_1.logPrefix,
|
|
193
|
+
utils.logLabeledWarning(extensionsHelper_1.logPrefix, `See these changes in your Firebase Emulator by running "firebase emulators:start". ` +
|
|
174
194
|
`Run ${clc.bold("firebase deploy (--only extensions)")} to deploy the changes to your Firebase project. `);
|
|
175
195
|
}
|
|
176
196
|
exports.showPreviewWarning = showPreviewWarning;
|
|
@@ -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.configForCodebase = 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,16 +20,41 @@ 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 });
|
|
23
31
|
}
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
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);
|
|
27
40
|
}
|
|
28
|
-
|
|
41
|
+
}
|
|
42
|
+
function validate(config) {
|
|
43
|
+
const validated = config.map((cfg) => validateSingle(cfg));
|
|
44
|
+
assertUnique(validated, "source");
|
|
45
|
+
assertUnique(validated, "codebase");
|
|
46
|
+
return validated;
|
|
29
47
|
}
|
|
30
48
|
exports.validate = validate;
|
|
31
49
|
function normalizeAndValidate(config) {
|
|
32
50
|
return validate(normalize(config));
|
|
33
51
|
}
|
|
34
52
|
exports.normalizeAndValidate = normalizeAndValidate;
|
|
53
|
+
function configForCodebase(config, codebase) {
|
|
54
|
+
const codebaseCfg = config.find((c) => c.codebase === codebase);
|
|
55
|
+
if (!codebaseCfg) {
|
|
56
|
+
throw new error_1.FirebaseError(`No functions config found for codebase ${codebase}`);
|
|
57
|
+
}
|
|
58
|
+
return codebaseCfg;
|
|
59
|
+
}
|
|
60
|
+
exports.configForCodebase = configForCodebase;
|
|
@@ -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;
|
|
@@ -55,7 +55,7 @@ const actionFunction = async (options) => {
|
|
|
55
55
|
})
|
|
56
56
|
.then(() => {
|
|
57
57
|
const instance = serveFunctions.get();
|
|
58
|
-
const emulator = new shell.FunctionsEmulatorShell(instance
|
|
58
|
+
const emulator = new shell.FunctionsEmulatorShell(instance);
|
|
59
59
|
if (emulator.emulatedFunctions && emulator.emulatedFunctions.length === 0) {
|
|
60
60
|
logger_1.logger.info("No functions emulated.");
|
|
61
61
|
process.exit();
|