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
package/lib/bin/firebase.js
CHANGED
|
@@ -20,7 +20,7 @@ marked.setOptions({
|
|
|
20
20
|
renderer: new TerminalRenderer(),
|
|
21
21
|
});
|
|
22
22
|
const updateMessage = `Update available ${clc.xterm(240)("{currentVersion}")} → ${clc.green("{latestVersion}")}\n` +
|
|
23
|
-
`To update to the latest version using npm, run
|
|
23
|
+
`To update to the latest version using npm, run\n${clc.cyan("npm install -g firebase-tools")}\n` +
|
|
24
24
|
`For other CLI management options, visit the ${marked("[CLI documentation](https://firebase.google.com/docs/cli#update-cli)")}`;
|
|
25
25
|
updateNotifier.notify({ defer: true, isGlobal: true, message: updateMessage });
|
|
26
26
|
const client = require("..");
|
package/lib/command.js
CHANGED
|
@@ -9,7 +9,7 @@ const rc_1 = require("./rc");
|
|
|
9
9
|
const config_1 = require("./config");
|
|
10
10
|
const configstore_1 = require("./configstore");
|
|
11
11
|
const detectProjectRoot_1 = require("./detectProjectRoot");
|
|
12
|
-
const
|
|
12
|
+
const track_1 = require("./track");
|
|
13
13
|
const clc = require("cli-color");
|
|
14
14
|
const auth_1 = require("./auth");
|
|
15
15
|
const projects_1 = require("./management/projects");
|
|
@@ -85,7 +85,7 @@ class Command {
|
|
|
85
85
|
}, null, 2));
|
|
86
86
|
}
|
|
87
87
|
const duration = new Date().getTime() - start;
|
|
88
|
-
void track(this.name, "success", duration).then(() => process.exit());
|
|
88
|
+
void (0, track_1.track)(this.name, "success", duration).then(() => process.exit());
|
|
89
89
|
})
|
|
90
90
|
.catch(async (err) => {
|
|
91
91
|
if ((0, utils_1.getInheritedOption)(options, "json")) {
|
|
@@ -96,7 +96,7 @@ class Command {
|
|
|
96
96
|
}
|
|
97
97
|
const duration = Date.now() - start;
|
|
98
98
|
const errorEvent = err.exit === 1 ? "Error (User)" : "Error (Unexpected)";
|
|
99
|
-
await Promise.all([track(this.name, "error", duration), track(errorEvent, "", duration)]);
|
|
99
|
+
await Promise.all([(0, track_1.track)(this.name, "error", duration), (0, track_1.track)(errorEvent, "", duration)]);
|
|
100
100
|
client.errorOut(err);
|
|
101
101
|
});
|
|
102
102
|
});
|
|
@@ -197,7 +197,7 @@ function validateProjectId(project) {
|
|
|
197
197
|
if (PROJECT_ID_REGEX.test(project)) {
|
|
198
198
|
return;
|
|
199
199
|
}
|
|
200
|
-
track("Project ID Check", "invalid");
|
|
200
|
+
(0, track_1.track)("Project ID Check", "invalid");
|
|
201
201
|
const invalidMessage = "Invalid project id: " + clc.bold(project) + ".";
|
|
202
202
|
if (project.toLowerCase() !== project) {
|
|
203
203
|
throw new error_1.FirebaseError(invalidMessage + "\nNote: Project id must be all lowercase.");
|
package/lib/commands/deploy.js
CHANGED
|
@@ -5,7 +5,7 @@ const { requirePermissions } = require("../requirePermissions");
|
|
|
5
5
|
const { checkServiceAccountIam } = require("../deploy/functions/checkIam");
|
|
6
6
|
const checkValidTargetFilters = require("../checkValidTargetFilters");
|
|
7
7
|
const { Command } = require("../command");
|
|
8
|
-
const deploy = require("../deploy");
|
|
8
|
+
const { deploy } = require("../deploy");
|
|
9
9
|
const { requireConfig } = require("../requireConfig");
|
|
10
10
|
const { filterTargets } = require("../filterTargets");
|
|
11
11
|
const { requireHostingSite } = require("../requireHostingSite");
|
|
@@ -8,6 +8,7 @@ const registry_1 = require("../emulator/registry");
|
|
|
8
8
|
const types_1 = require("../emulator/types");
|
|
9
9
|
const clc = require("cli-color");
|
|
10
10
|
const constants_1 = require("../emulator/constants");
|
|
11
|
+
const utils_1 = require("../utils");
|
|
11
12
|
const Table = require("cli-table");
|
|
12
13
|
function stylizeLink(url) {
|
|
13
14
|
return clc.underline(clc.bold(url));
|
|
@@ -22,8 +23,9 @@ module.exports = new command_1.Command("emulators:start")
|
|
|
22
23
|
.option(commandUtils.FLAG_EXPORT_ON_EXIT, commandUtils.DESC_EXPORT_ON_EXIT)
|
|
23
24
|
.action(async (options) => {
|
|
24
25
|
const killSignalPromise = commandUtils.shutdownWhenKilled(options);
|
|
26
|
+
let deprecationNotices;
|
|
25
27
|
try {
|
|
26
|
-
await controller.startAll(options);
|
|
28
|
+
({ deprecationNotices } = await controller.startAll(options));
|
|
27
29
|
}
|
|
28
30
|
catch (e) {
|
|
29
31
|
await controller.cleanShutdown();
|
|
@@ -61,7 +63,7 @@ module.exports = new command_1.Command("emulators:start")
|
|
|
61
63
|
.map((emulator) => {
|
|
62
64
|
const emulatorName = constants_1.Constants.description(emulator).replace(/ emulator/i, "");
|
|
63
65
|
const isSupportedByUi = types_1.EMULATORS_SUPPORTED_BY_UI.includes(emulator);
|
|
64
|
-
const info = registry_1.EmulatorRegistry.getInfo(emulator
|
|
66
|
+
const info = registry_1.EmulatorRegistry.getInfo(emulator);
|
|
65
67
|
if (!info) {
|
|
66
68
|
return [emulatorName, "Failed to initialize (see above)", "", ""];
|
|
67
69
|
}
|
|
@@ -75,6 +77,11 @@ module.exports = new command_1.Command("emulators:start")
|
|
|
75
77
|
})
|
|
76
78
|
.map((col) => col.slice(0, head.length))
|
|
77
79
|
.filter((v) => v));
|
|
80
|
+
let extensionsTable = "";
|
|
81
|
+
if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EXTENSIONS)) {
|
|
82
|
+
const extensionsEmulatorInstance = registry_1.EmulatorRegistry.get(types_1.Emulators.EXTENSIONS);
|
|
83
|
+
extensionsTable = extensionsEmulatorInstance.extensionsInfoTable(options);
|
|
84
|
+
}
|
|
78
85
|
logger_1.logger.info(`\n${successMessageTable}
|
|
79
86
|
|
|
80
87
|
${emulatorsTable}
|
|
@@ -82,8 +89,11 @@ ${hubInfo
|
|
|
82
89
|
? clc.blackBright(" Emulator Hub running at ") + registry_1.EmulatorRegistry.getInfoHostString(hubInfo)
|
|
83
90
|
: clc.blackBright(" Emulator Hub not running.")}
|
|
84
91
|
${clc.blackBright(" Other reserved ports:")} ${reservedPortsString}
|
|
85
|
-
|
|
92
|
+
${extensionsTable}
|
|
86
93
|
Issues? Report them at ${stylizeLink("https://github.com/firebase/firebase-tools/issues")} and attach the *-debug.log files.
|
|
87
94
|
`);
|
|
95
|
+
for (const notice of deprecationNotices) {
|
|
96
|
+
(0, utils_1.logLabeledWarning)("emulators", notice, "warn");
|
|
97
|
+
}
|
|
88
98
|
await killSignalPromise;
|
|
89
99
|
});
|
|
@@ -41,13 +41,22 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
41
41
|
throw new error_1.FirebaseError(`Command not supported in non-interactive mode, edit ./extensions/${instanceId}.env directly instead`);
|
|
42
42
|
}
|
|
43
43
|
const config = manifest.loadConfig(options);
|
|
44
|
-
const
|
|
45
|
-
const
|
|
44
|
+
const refOrPath = manifest.getInstanceTarget(instanceId, config);
|
|
45
|
+
const isLocalSource = (0, extensionsHelper_1.isLocalPath)(refOrPath);
|
|
46
|
+
let spec;
|
|
47
|
+
if (isLocalSource) {
|
|
48
|
+
const source = await (0, extensionsHelper_1.createSourceFromLocation)((0, projectUtils_1.needProjectId)({ projectId }), refOrPath);
|
|
49
|
+
spec = source.spec;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const extensionVersion = await extensionsApi.getExtensionVersion(refOrPath);
|
|
53
|
+
spec = extensionVersion.spec;
|
|
54
|
+
}
|
|
46
55
|
const oldParamValues = manifest.readInstanceParam({
|
|
47
56
|
instanceId,
|
|
48
57
|
projectDir: config.projectDir,
|
|
49
58
|
});
|
|
50
|
-
const [immutableParams, tbdParams] = (0, functional_1.partition)(
|
|
59
|
+
const [immutableParams, tbdParams] = (0, functional_1.partition)(spec.params, (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
|
|
51
60
|
infoImmutableParams(immutableParams, oldParamValues);
|
|
52
61
|
paramHelper.setNewDefaults(tbdParams, oldParamValues);
|
|
53
62
|
const mutableParamsBindingOptions = await paramHelper.getParams({
|
|
@@ -62,9 +71,10 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
62
71
|
await manifest.writeToManifest([
|
|
63
72
|
{
|
|
64
73
|
instanceId,
|
|
65
|
-
ref:
|
|
74
|
+
ref: !isLocalSource ? refs.parse(refOrPath) : undefined,
|
|
75
|
+
localPath: isLocalSource ? refOrPath : undefined,
|
|
66
76
|
params: newParamOptions,
|
|
67
|
-
|
|
77
|
+
extensionSpec: spec,
|
|
68
78
|
},
|
|
69
79
|
], config, {
|
|
70
80
|
nonInteractive: false,
|
|
@@ -19,9 +19,10 @@ module.exports = new command_1.Command("ext:dev:emulators:start")
|
|
|
19
19
|
.action(async (options) => {
|
|
20
20
|
const killSignalPromise = commandUtils.shutdownWhenKilled(options);
|
|
21
21
|
const emulatorOptions = await optionsHelper.buildOptions(options);
|
|
22
|
+
let deprecationNotices;
|
|
22
23
|
try {
|
|
23
24
|
commandUtils.beforeEmulatorCommand(emulatorOptions);
|
|
24
|
-
await controller.startAll(emulatorOptions);
|
|
25
|
+
({ deprecationNotices } = await controller.startAll(emulatorOptions));
|
|
25
26
|
}
|
|
26
27
|
catch (e) {
|
|
27
28
|
await controller.cleanShutdown();
|
|
@@ -31,5 +32,8 @@ module.exports = new command_1.Command("ext:dev:emulators:start")
|
|
|
31
32
|
throw e;
|
|
32
33
|
}
|
|
33
34
|
utils.logSuccess("All emulators ready, it is now safe to connect.");
|
|
35
|
+
for (const notice of deprecationNotices) {
|
|
36
|
+
utils.logLabeledWarning("emulators", notice, "warn");
|
|
37
|
+
}
|
|
34
38
|
await killSignalPromise;
|
|
35
39
|
});
|
|
@@ -22,16 +22,17 @@ module.exports = new command_1.Command("ext:export")
|
|
|
22
22
|
.action(async (options) => {
|
|
23
23
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
24
24
|
const projectNumber = await (0, getProjectNumber_1.getProjectNumber)(options);
|
|
25
|
-
const have = await Promise.all(
|
|
26
|
-
const subbed = await (0, export_1.setSecretParamsToLatest)(i);
|
|
27
|
-
return (0, export_1.parameterizeProject)(projectId, projectNumber, subbed);
|
|
28
|
-
}));
|
|
25
|
+
const have = await Promise.all(await planner.have(projectId));
|
|
29
26
|
if (have.length === 0) {
|
|
30
27
|
logger_1.logger.info(`No extension instances installed on ${projectId}, so there is nothing to export.`);
|
|
31
28
|
return;
|
|
32
29
|
}
|
|
33
30
|
const [withRef, withoutRef] = (0, functional_1.partition)(have, (s) => !!s.ref);
|
|
34
|
-
|
|
31
|
+
const withRefSubbed = await Promise.all(withRef.map(async (i) => {
|
|
32
|
+
const subbed = await (0, export_1.setSecretParamsToLatest)(i);
|
|
33
|
+
return (0, export_1.parameterizeProject)(projectId, projectNumber, subbed);
|
|
34
|
+
}));
|
|
35
|
+
(0, export_1.displayExportInfo)(withRefSubbed, withoutRef);
|
|
35
36
|
if (!options.nonInteractive &&
|
|
36
37
|
!options.force &&
|
|
37
38
|
!(await (0, prompt_1.promptOnce)({
|
|
@@ -46,7 +46,7 @@ exports.default = new command_1.Command("ext:install [extensionName]")
|
|
|
46
46
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
47
47
|
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
48
48
|
.action(async (extensionName, options) => {
|
|
49
|
-
var _a;
|
|
49
|
+
var _a, _b;
|
|
50
50
|
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
51
51
|
const paramsEnvPath = ((_a = options.params) !== null && _a !== void 0 ? _a : "");
|
|
52
52
|
let learnMore = false;
|
|
@@ -62,20 +62,23 @@ exports.default = new command_1.Command("ext:install [extensionName]")
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
let source;
|
|
65
|
-
let
|
|
65
|
+
let extensionVersion;
|
|
66
66
|
if ((0, extensionsHelper_1.isUrlPath)(extensionName)) {
|
|
67
|
-
|
|
67
|
+
throw new error_1.FirebaseError(`Installing with a source url is no longer supported in the CLI. Please use Firebase Console instead.`);
|
|
68
68
|
}
|
|
69
|
-
if ((0, extensionsHelper_1.
|
|
69
|
+
if ((0, extensionsHelper_1.isLocalPath)(extensionName)) {
|
|
70
|
+
source = await (0, extensionsHelper_1.createSourceFromLocation)((0, projectUtils_1.needProjectId)({ projectId }), extensionName);
|
|
71
|
+
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
|
|
70
72
|
void (0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
|
|
71
|
-
if (options.local) {
|
|
72
|
-
throw new error_1.FirebaseError("Installing a local source locally is not supported yet, please use ext:dev:emulator commands");
|
|
73
|
-
}
|
|
74
|
-
source = await infoInstallBySource((0, projectUtils_1.needProjectId)({ projectId }), extensionName);
|
|
75
73
|
}
|
|
76
74
|
else {
|
|
77
75
|
void (0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
|
|
78
|
-
|
|
76
|
+
extensionName = (0, extensionsHelper_1.canonicalizeRefInput)(extensionName);
|
|
77
|
+
extensionVersion = await extensionsApi.getExtensionVersion(extensionName);
|
|
78
|
+
await infoExtensionVersion({
|
|
79
|
+
extensionName,
|
|
80
|
+
extensionVersion,
|
|
81
|
+
});
|
|
79
82
|
}
|
|
80
83
|
if (!(await (0, extensionsHelper_1.confirm)({
|
|
81
84
|
nonInteractive: options.nonInteractive,
|
|
@@ -84,10 +87,10 @@ exports.default = new command_1.Command("ext:install [extensionName]")
|
|
|
84
87
|
}))) {
|
|
85
88
|
return;
|
|
86
89
|
}
|
|
87
|
-
if (!source && !
|
|
90
|
+
if (!source && !extensionVersion) {
|
|
88
91
|
throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
|
|
89
92
|
}
|
|
90
|
-
const spec = (source === null || source === void 0 ? void 0 : source.spec)
|
|
93
|
+
const spec = (_b = source === null || source === void 0 ? void 0 : source.spec) !== null && _b !== void 0 ? _b : extensionVersion === null || extensionVersion === void 0 ? void 0 : extensionVersion.spec;
|
|
91
94
|
if (!spec) {
|
|
92
95
|
throw new error_1.FirebaseError(`Could not find the extension.yaml for extension '${clc.bold(extensionName)}'. Please make sure this is a valid extension and try again.`);
|
|
93
96
|
}
|
|
@@ -103,7 +106,7 @@ exports.default = new command_1.Command("ext:install [extensionName]")
|
|
|
103
106
|
projectId,
|
|
104
107
|
extensionName,
|
|
105
108
|
source,
|
|
106
|
-
extVersion,
|
|
109
|
+
extVersion: extensionVersion,
|
|
107
110
|
nonInteractive: options.nonInteractive,
|
|
108
111
|
force: options.force,
|
|
109
112
|
});
|
|
@@ -123,7 +126,7 @@ exports.default = new command_1.Command("ext:install [extensionName]")
|
|
|
123
126
|
projectId: projectId,
|
|
124
127
|
extensionName,
|
|
125
128
|
source,
|
|
126
|
-
extVersion,
|
|
129
|
+
extVersion: extensionVersion,
|
|
127
130
|
nonInteractive: options.nonInteractive,
|
|
128
131
|
force: options.force,
|
|
129
132
|
});
|
|
@@ -137,37 +140,17 @@ exports.default = new command_1.Command("ext:install [extensionName]")
|
|
|
137
140
|
throw err;
|
|
138
141
|
}
|
|
139
142
|
});
|
|
140
|
-
async function
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, extensionName);
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}', ` +
|
|
147
|
-
`and encountered the following error when trying to create an instance of extension '${clc.bold(extensionName)}':\n ${err.message}`);
|
|
148
|
-
}
|
|
149
|
-
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
|
|
150
|
-
return source;
|
|
151
|
-
}
|
|
152
|
-
async function infoInstallByReference(extensionName, interactive) {
|
|
153
|
-
if (extensionName.split("/").length < 2) {
|
|
154
|
-
const [extensionID, version] = extensionName.split("@");
|
|
155
|
-
extensionName = `firebase/${extensionID}@${version || "latest"}`;
|
|
156
|
-
}
|
|
157
|
-
const ref = refs.parse(extensionName);
|
|
143
|
+
async function infoExtensionVersion(args) {
|
|
144
|
+
const ref = refs.parse(args.extensionName);
|
|
158
145
|
const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
extensionName = `${extensionName}@latest`;
|
|
162
|
-
}
|
|
163
|
-
const extVersion = await extensionsApi.getExtensionVersion(extensionName);
|
|
164
|
-
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, ref.publisherId, extVersion.spec, true);
|
|
165
|
-
await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, extVersion);
|
|
166
|
-
return extVersion;
|
|
146
|
+
(0, displayExtensionInfo_1.displayExtInfo)(args.extensionName, ref.publisherId, args.extensionVersion.spec, true);
|
|
147
|
+
await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, args.extensionVersion);
|
|
167
148
|
}
|
|
168
149
|
async function installToManifest(options) {
|
|
169
|
-
|
|
170
|
-
const
|
|
150
|
+
var _a;
|
|
151
|
+
const { projectId, extensionName, extVersion, source, paramsEnvPath, nonInteractive, force } = options;
|
|
152
|
+
const isLocalSource = (0, extensionsHelper_1.isLocalPath)(extensionName);
|
|
153
|
+
const spec = (_a = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec) !== null && _a !== void 0 ? _a : source === null || source === void 0 ? void 0 : source.spec;
|
|
171
154
|
if (!spec) {
|
|
172
155
|
throw new error_1.FirebaseError(`Could not find the extension.yaml for ${extensionName}. Please make sure this is a valid extension and try again.`);
|
|
173
156
|
}
|
|
@@ -183,13 +166,14 @@ async function installToManifest(options) {
|
|
|
183
166
|
paramsEnvPath,
|
|
184
167
|
instanceId,
|
|
185
168
|
});
|
|
186
|
-
const ref = refs.parse(extVersion.ref);
|
|
169
|
+
const ref = extVersion ? refs.parse(extVersion.ref) : undefined;
|
|
187
170
|
await manifest.writeToManifest([
|
|
188
171
|
{
|
|
189
172
|
instanceId,
|
|
190
|
-
ref,
|
|
173
|
+
ref: !isLocalSource ? ref : undefined,
|
|
174
|
+
localPath: isLocalSource ? extensionName : undefined,
|
|
191
175
|
params: paramBindingOptions,
|
|
192
|
-
|
|
176
|
+
extensionSpec: spec,
|
|
193
177
|
},
|
|
194
178
|
], config, { nonInteractive, force: force !== null && force !== void 0 ? force : false });
|
|
195
179
|
manifest.showPreviewWarning();
|
|
@@ -45,6 +45,13 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
|
|
|
45
45
|
if (options.local) {
|
|
46
46
|
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
47
47
|
const config = manifest.loadConfig(options);
|
|
48
|
+
const oldRefOrPath = manifest.getInstanceTarget(instanceId, config);
|
|
49
|
+
if ((0, extensionsHelper_1.isLocalPath)(oldRefOrPath)) {
|
|
50
|
+
throw new error_1.FirebaseError(`Updating an extension with local source is not neccessary. ` +
|
|
51
|
+
`Rerun "firebase deploy" or restart the emulator after making changes to your local extension source. ` +
|
|
52
|
+
`If you've edited the extension param spec, you can edit an extension instance's params ` +
|
|
53
|
+
`interactively by running "firebase ext:configure --local {instance-id}"`);
|
|
54
|
+
}
|
|
48
55
|
const oldRef = manifest.getInstanceRef(instanceId, config);
|
|
49
56
|
const oldExtensionVersion = await extensionsApi.getExtensionVersion(refs.toExtensionVersionRef(oldRef));
|
|
50
57
|
updateSource = (0, updateHelper_1.inferUpdateSource)(updateSource, refs.toExtensionRef(oldRef));
|
|
@@ -84,7 +91,8 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
|
|
|
84
91
|
instanceId,
|
|
85
92
|
ref: refs.parse(newExtensionVersion.ref),
|
|
86
93
|
params: newParamBindingOptions,
|
|
87
|
-
|
|
94
|
+
extensionSpec: newExtensionVersion.spec,
|
|
95
|
+
extensionVersion: newExtensionVersion,
|
|
88
96
|
},
|
|
89
97
|
], config, {
|
|
90
98
|
nonInteractive: options.nonInteractive,
|
|
@@ -29,7 +29,7 @@ exports.default = new command_1.Command("functions:delete [filters...]")
|
|
|
29
29
|
}
|
|
30
30
|
const context = {
|
|
31
31
|
projectId: (0, projectUtils_1.needProjectId)(options),
|
|
32
|
-
filters: filters.map((f) => f.split(".")),
|
|
32
|
+
filters: filters.map((f) => ({ idChunks: f.split(".") })),
|
|
33
33
|
};
|
|
34
34
|
const [config, existingBackend] = await Promise.all([
|
|
35
35
|
functionsConfig.getFirebaseConfig(options),
|
|
@@ -40,10 +40,7 @@ exports.default = new command_1.Command("functions:delete [filters...]")
|
|
|
40
40
|
if (options.region) {
|
|
41
41
|
existingBackend.endpoints = { [options.region]: existingBackend.endpoints[options.region] };
|
|
42
42
|
}
|
|
43
|
-
const plan = planner.createDeploymentPlan(backend.empty(), existingBackend,
|
|
44
|
-
filters: context.filters,
|
|
45
|
-
deleteAll: true,
|
|
46
|
-
});
|
|
43
|
+
const plan = planner.createDeploymentPlan(backend.empty(), existingBackend, context.filters, true);
|
|
47
44
|
const allEpToDelete = Object.values(plan)
|
|
48
45
|
.map((changes) => changes.endpointsToDelete)
|
|
49
46
|
.reduce(functional_1.reduceFlat, [])
|
|
@@ -1,21 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const command_1 = require("../command");
|
|
4
|
-
const logger_1 = require("../logger");
|
|
5
4
|
const projectUtils_1 = require("../projectUtils");
|
|
6
5
|
const secretManager_1 = require("../gcp/secretManager");
|
|
7
6
|
const prompt_1 = require("../prompt");
|
|
7
|
+
const utils_1 = require("../utils");
|
|
8
8
|
const secrets = require("../functions/secrets");
|
|
9
|
+
const backend = require("../deploy/functions/backend");
|
|
9
10
|
exports.default = new command_1.Command("functions:secrets:destroy <KEY>[@version]")
|
|
10
11
|
.description("Destroy a secret. Defaults to destroying the latest version.")
|
|
11
12
|
.withForce("Destroys a secret without confirmation.")
|
|
12
13
|
.action(async (key, options) => {
|
|
13
14
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
15
|
+
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
16
|
+
const haveBackend = await backend.existingBackend({ projectId });
|
|
14
17
|
let [name, version] = key.split("@");
|
|
15
18
|
if (!version) {
|
|
16
19
|
version = "latest";
|
|
17
20
|
}
|
|
18
21
|
const sv = await (0, secretManager_1.getSecretVersion)(projectId, name, version);
|
|
22
|
+
if (sv.state === "DESTROYED") {
|
|
23
|
+
(0, utils_1.logBullet)(`Secret ${sv.secret.name}@${version} is already destroyed. Nothing to do.`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const boundEndpoints = backend
|
|
27
|
+
.allEndpoints(haveBackend)
|
|
28
|
+
.filter((e) => secrets.inUse({ projectId, projectNumber }, sv.secret, e));
|
|
29
|
+
if (boundEndpoints.length > 0) {
|
|
30
|
+
const endpointsMsg = boundEndpoints
|
|
31
|
+
.map((e) => `${e.id}[${e.platform}](${e.region})`)
|
|
32
|
+
.join("\t\n");
|
|
33
|
+
(0, utils_1.logWarning)(`Secret ${name}@${version} is currently in use by following functions:\n\t${endpointsMsg}`);
|
|
34
|
+
if (!options.force) {
|
|
35
|
+
(0, utils_1.logWarning)("Refusing to destroy secret in use. Use -f to destroy the secret anyway.");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
19
39
|
if (!options.force) {
|
|
20
40
|
const confirm = await (0, prompt_1.promptOnce)({
|
|
21
41
|
name: "destroy",
|
|
@@ -28,12 +48,12 @@ exports.default = new command_1.Command("functions:secrets:destroy <KEY>[@versio
|
|
|
28
48
|
}
|
|
29
49
|
}
|
|
30
50
|
await (0, secretManager_1.destroySecretVersion)(projectId, name, version);
|
|
31
|
-
|
|
51
|
+
(0, utils_1.logBullet)(`Destroyed secret version ${name}@${sv.versionId}`);
|
|
32
52
|
const secret = await (0, secretManager_1.getSecret)(projectId, name);
|
|
33
53
|
if (secrets.isFirebaseManaged(secret)) {
|
|
34
54
|
const versions = await (0, secretManager_1.listSecretVersions)(projectId, name);
|
|
35
55
|
if (versions.filter((v) => v.state === "ENABLED").length === 0) {
|
|
36
|
-
|
|
56
|
+
(0, utils_1.logBullet)(`No active secret versions left. Destroying secret ${name}`);
|
|
37
57
|
await (0, secretManager_1.deleteSecret)(projectId, name);
|
|
38
58
|
}
|
|
39
59
|
}
|
|
@@ -10,6 +10,7 @@ const utils_1 = require("../utils");
|
|
|
10
10
|
const prompt_1 = require("../prompt");
|
|
11
11
|
const secretManager_1 = require("../gcp/secretManager");
|
|
12
12
|
exports.default = new command_1.Command("functions:secrets:prune")
|
|
13
|
+
.withForce("Destroys unused secrets without prompt")
|
|
13
14
|
.description("Destroys unused secrets")
|
|
14
15
|
.before(requirePermissions_1.requirePermissions, [
|
|
15
16
|
"cloudfunctions.functions.list",
|
|
@@ -32,18 +33,20 @@ exports.default = new command_1.Command("functions:secrets:prune")
|
|
|
32
33
|
}
|
|
33
34
|
(0, utils_1.logBullet)(`Found ${pruned.length} unused active secret versions:\n\t` +
|
|
34
35
|
pruned.map((sv) => `${sv.secret}@${sv.version}`).join("\n\t"));
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
if (!options.force) {
|
|
37
|
+
const confirm = await (0, prompt_1.promptOnce)({
|
|
38
|
+
name: "destroy",
|
|
39
|
+
type: "confirm",
|
|
40
|
+
default: true,
|
|
41
|
+
message: `Do you want to destroy unused secret versions?`,
|
|
42
|
+
}, options);
|
|
43
|
+
if (!confirm) {
|
|
44
|
+
(0, utils_1.logBullet)("Run the following commands to destroy each unused secret version:\n\t" +
|
|
45
|
+
pruned
|
|
46
|
+
.map((sv) => `firebase functions:secrets:destroy ${sv.secret}@${sv.version}`)
|
|
47
|
+
.join("\n\t"));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
47
50
|
}
|
|
48
51
|
await Promise.all(pruned.map((sv) => (0, secretManager_1.destroySecretVersion)(projectId, sv.secret, sv.version)));
|
|
49
52
|
(0, utils_1.logSuccess)("Destroyed all unused secrets!");
|
|
@@ -10,9 +10,11 @@ const prompt_1 = require("../prompt");
|
|
|
10
10
|
const utils_1 = require("../utils");
|
|
11
11
|
const projectUtils_1 = require("../projectUtils");
|
|
12
12
|
const secretManager_1 = require("../gcp/secretManager");
|
|
13
|
+
const secrets = require("../functions/secrets");
|
|
14
|
+
const backend = require("../deploy/functions/backend");
|
|
13
15
|
exports.default = new command_1.Command("functions:secrets:set <KEY>")
|
|
14
|
-
.description("Create or update a secret for use in Cloud Functions for Firebase")
|
|
15
|
-
.withForce("
|
|
16
|
+
.description("Create or update a secret for use in Cloud Functions for Firebase.")
|
|
17
|
+
.withForce("Automatically updates functions to use the new secret.")
|
|
16
18
|
.before(requirePermissions_1.requirePermissions, [
|
|
17
19
|
"secretmanager.secrets.create",
|
|
18
20
|
"secretmanager.secrets.get",
|
|
@@ -22,6 +24,7 @@ exports.default = new command_1.Command("functions:secrets:set <KEY>")
|
|
|
22
24
|
.option("--data-file <dataFile>", 'File path from which to read secret data. Set to "-" to read the secret data from stdin.')
|
|
23
25
|
.action(async (unvalidatedKey, options) => {
|
|
24
26
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
27
|
+
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
25
28
|
const key = await (0, secrets_1.ensureValidKey)(unvalidatedKey, options);
|
|
26
29
|
const secret = await (0, secrets_1.ensureSecret)(projectId, key, options);
|
|
27
30
|
let secretValue;
|
|
@@ -41,6 +44,50 @@ exports.default = new command_1.Command("functions:secrets:set <KEY>")
|
|
|
41
44
|
}
|
|
42
45
|
const secretVersion = await (0, secretManager_1.addVersion)(projectId, key, secretValue);
|
|
43
46
|
(0, utils_1.logSuccess)(`Created a new secret version ${(0, secretManager_1.toSecretVersionResourceName)(secretVersion)}`);
|
|
44
|
-
(
|
|
45
|
-
|
|
47
|
+
if (!secrets.isFirebaseManaged(secret)) {
|
|
48
|
+
(0, utils_1.logBullet)("Please deploy your functions for the change to take effect by running:\n\t" +
|
|
49
|
+
clc.bold("firebase deploy --only functions"));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const haveBackend = await backend.existingBackend({ projectId });
|
|
53
|
+
const endpointsToUpdate = backend
|
|
54
|
+
.allEndpoints(haveBackend)
|
|
55
|
+
.filter((e) => secrets.inUse({ projectId, projectNumber }, secret, e));
|
|
56
|
+
if (endpointsToUpdate.length === 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
(0, utils_1.logBullet)(`${endpointsToUpdate.length} functions are using stale version of secret ${secret.name}:\n\t` +
|
|
60
|
+
endpointsToUpdate.map((e) => `${e.id}(${e.region})`).join("\n\t"));
|
|
61
|
+
if (!options.force) {
|
|
62
|
+
const confirm = await (0, prompt_1.promptOnce)({
|
|
63
|
+
name: "redeploy",
|
|
64
|
+
type: "confirm",
|
|
65
|
+
default: true,
|
|
66
|
+
message: `Do you want to re-deploy the functions and destroy the stale version of secret ${secret.name}?`,
|
|
67
|
+
}, options);
|
|
68
|
+
if (!confirm) {
|
|
69
|
+
(0, utils_1.logBullet)("Please deploy your functions for the change to take effect by running:\n\t" +
|
|
70
|
+
clc.bold("firebase deploy --only functions"));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const updateOps = endpointsToUpdate.map(async (e) => {
|
|
75
|
+
(0, utils_1.logBullet)(`Updating function ${e.id}(${e.region})...`);
|
|
76
|
+
const updated = await secrets.updateEndpointSecret({ projectId, projectNumber }, secretVersion, e);
|
|
77
|
+
(0, utils_1.logBullet)(`Updated function ${e.id}(${e.region}).`);
|
|
78
|
+
return updated;
|
|
79
|
+
});
|
|
80
|
+
const updatedEndpoints = await Promise.all(updateOps);
|
|
81
|
+
(0, utils_1.logBullet)(`Pruning stale secrets...`);
|
|
82
|
+
const prunedResult = await (0, secrets_1.pruneAndDestroySecrets)({ projectId, projectNumber }, updatedEndpoints);
|
|
83
|
+
if (prunedResult.destroyed.length > 0) {
|
|
84
|
+
(0, utils_1.logBullet)(`Detroyed unused secret versions: ${prunedResult.destroyed
|
|
85
|
+
.map((s) => `${s.secret}@${s.version}`)
|
|
86
|
+
.join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
if (prunedResult.erred.length > 0) {
|
|
89
|
+
(0, utils_1.logWarning)(`Failed to destroy unused secret versions:\n\t${prunedResult.erred
|
|
90
|
+
.map((err) => err.message)
|
|
91
|
+
.join("\n\t")}`);
|
|
92
|
+
}
|
|
46
93
|
});
|
|
@@ -6,7 +6,7 @@ const error_1 = require("../error");
|
|
|
6
6
|
const api_1 = require("../hosting/api");
|
|
7
7
|
const normalizedHostingConfigs_1 = require("../hosting/normalizedHostingConfigs");
|
|
8
8
|
const requirePermissions_1 = require("../requirePermissions");
|
|
9
|
-
const
|
|
9
|
+
const deploy_1 = require("../deploy");
|
|
10
10
|
const projectUtils_1 = require("../projectUtils");
|
|
11
11
|
const logger_1 = require("../logger");
|
|
12
12
|
const requireConfig_1 = require("../requireConfig");
|
|
@@ -82,7 +82,7 @@ exports.default = new command_1.Command("hosting:channel:deploy [channelId]")
|
|
|
82
82
|
siteInfo.expireTime = chan.expireTime;
|
|
83
83
|
return;
|
|
84
84
|
}));
|
|
85
|
-
const { hosting } = await deploy(["hosting"], options, { hostingChannel: channelId });
|
|
85
|
+
const { hosting } = await (0, deploy_1.deploy)(["hosting"], options, { hostingChannel: channelId });
|
|
86
86
|
const versionNames = [];
|
|
87
87
|
if (typeof hosting === "string") {
|
|
88
88
|
versionNames.push(hosting);
|
|
@@ -10,7 +10,7 @@ const provisioningHelper_1 = require("../../extensions/provisioningHelper");
|
|
|
10
10
|
const secrets_1 = require("./secrets");
|
|
11
11
|
const validate_1 = require("./validate");
|
|
12
12
|
async function deploy(context, options, payload) {
|
|
13
|
-
var _a, _b, _c, _d, _e, _f;
|
|
13
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
14
14
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
15
15
|
await (0, validate_1.checkBilling)(projectId, options.nonInteractive);
|
|
16
16
|
await (0, provisioningHelper_1.bulkCheckProductsProvisioned)(projectId, [
|
|
@@ -25,15 +25,15 @@ async function deploy(context, options, payload) {
|
|
|
25
25
|
concurrency: 5,
|
|
26
26
|
handler: tasks.extensionsDeploymentHandler(errorHandler),
|
|
27
27
|
});
|
|
28
|
-
for (const create of (_d = payload.instancesToCreate) !== null &&
|
|
28
|
+
for (const create of (_e = (_d = payload.instancesToCreate) === null || _d === void 0 ? void 0 : _d.filter((i) => !!i.ref)) !== null && _e !== void 0 ? _e : []) {
|
|
29
29
|
const task = tasks.createExtensionInstanceTask(projectId, create, true);
|
|
30
30
|
void validationQueue.run(task);
|
|
31
31
|
}
|
|
32
|
-
for (const update of (
|
|
32
|
+
for (const update of (_g = (_f = payload.instancesToUpdate) === null || _f === void 0 ? void 0 : _f.filter((i) => !!i.ref)) !== null && _g !== void 0 ? _g : []) {
|
|
33
33
|
const task = tasks.updateExtensionInstanceTask(projectId, update, true);
|
|
34
34
|
void validationQueue.run(task);
|
|
35
35
|
}
|
|
36
|
-
for (const configure of (
|
|
36
|
+
for (const configure of (_j = (_h = payload.instancesToConfigure) === null || _h === void 0 ? void 0 : _h.filter((i) => !!i.ref)) !== null && _j !== void 0 ? _j : []) {
|
|
37
37
|
const task = tasks.configureExtensionInstanceTask(projectId, configure, true);
|
|
38
38
|
void validationQueue.run(task);
|
|
39
39
|
}
|
|
@@ -6,16 +6,19 @@ const refs = require("../../extensions/refs");
|
|
|
6
6
|
const humanReadable = (dep) => `${clc.bold(dep.instanceId)} (${dep.ref ? `${refs.toExtensionVersionRef(dep.ref)}` : `Installed from local source`})`;
|
|
7
7
|
exports.humanReadable = humanReadable;
|
|
8
8
|
const humanReadableUpdate = (from, to) => {
|
|
9
|
-
var _a
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
var _a;
|
|
10
|
+
if (from.ref &&
|
|
11
|
+
to.ref &&
|
|
12
|
+
from.ref.publisherId === to.ref.publisherId &&
|
|
13
|
+
from.ref.extensionId === to.ref.extensionId) {
|
|
14
|
+
return `\t${clc.bold(from.instanceId)} (${refs.toExtensionVersionRef(from.ref)} => ${(_a = to.ref) === null || _a === void 0 ? void 0 : _a.version})`;
|
|
13
15
|
}
|
|
14
16
|
else {
|
|
15
17
|
const fromRef = from.ref
|
|
16
18
|
? `${refs.toExtensionVersionRef(from.ref)}`
|
|
17
19
|
: `Installed from local source`;
|
|
18
|
-
|
|
20
|
+
const toRef = to.ref ? `${refs.toExtensionVersionRef(to.ref)}` : `Installed from local source`;
|
|
21
|
+
return `\t${clc.bold(from.instanceId)} (${fromRef} => ${toRef})`;
|
|
19
22
|
}
|
|
20
23
|
};
|
|
21
24
|
function createsSummary(toCreate) {
|