firebase-tools 14.27.0 → 15.1.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/appUtils.js +14 -15
- package/lib/archiveDirectory.js +7 -45
- package/lib/bin/cli.js +35 -8
- package/lib/bin/mcp.js +46 -8
- package/lib/command.js +5 -1
- package/lib/commands/dataconnect-execute.js +1 -1
- package/lib/commands/dataconnect-sdk-generate.js +7 -5
- package/lib/commands/dataconnect-sql-diff.js +7 -5
- package/lib/commands/dataconnect-sql-grant.js +12 -12
- package/lib/commands/dataconnect-sql-migrate.js +6 -4
- package/lib/commands/dataconnect-sql-setup.js +6 -4
- package/lib/commands/dataconnect-sql-shell.js +6 -4
- package/lib/commands/firestore-backups-list.js +1 -1
- package/lib/commands/functions-config-clone.js +2 -2
- package/lib/commands/functions-config-export.js +137 -92
- package/lib/commands/functions-config-get.js +1 -2
- package/lib/commands/functions-config-set.js +2 -2
- package/lib/commands/functions-config-unset.js +2 -2
- package/lib/commands/help.js +1 -1
- package/lib/commands/index.js +15 -10
- package/lib/commands/init.js +8 -0
- package/lib/config.js +1 -5
- package/lib/dataconnect/load.js +18 -21
- package/lib/deploy/database/prepare.js +1 -3
- package/lib/deploy/functions/prepare.js +5 -1
- package/lib/deploy/functions/prepareFunctionsUpload.js +1 -2
- package/lib/deploy/functions/runtimes/node/index.js +11 -12
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/validate.js +49 -2
- package/lib/emulator/commandUtils.js +1 -1
- package/lib/emulator/controller.js +1 -2
- package/lib/emulator/databaseEmulator.js +1 -5
- package/lib/emulator/downloadableEmulatorInfo.json +30 -30
- package/lib/emulator/functionsEmulator.js +0 -40
- package/lib/emulator/functionsEmulatorRuntime.js +1 -118
- package/lib/emulator/functionsEmulatorShared.js +1 -6
- package/lib/experiments.js +8 -1
- package/lib/extensions/extensionsHelper.js +0 -1
- package/lib/frameworks/constants.js +1 -1
- package/lib/fsAsync.js +11 -3
- package/lib/functionsConfig.js +39 -1
- package/lib/gcp/cloudsql/cloudsqladmin.js +1 -1
- package/lib/index.js +44 -1
- package/lib/init/features/dataconnect/resolver.js +111 -0
- package/lib/init/features/index.js +4 -1
- package/lib/init/features/project.js +1 -0
- package/lib/init/index.js +5 -0
- package/lib/mcp/index.js +31 -22
- package/lib/mcp/tools/core/get_environment.js +4 -1
- package/lib/mcp/tools/dataconnect/compile.js +13 -7
- package/lib/mcp/tools/dataconnect/execute.js +10 -7
- package/lib/mcp/tools/dataconnect/generate_operation.js +7 -3
- package/lib/mcp/tools/index.js +53 -44
- package/package.json +1 -2
- package/lib/deploy/functions/runtimes/node/extractTriggers.js +0 -23
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +0 -332
- package/lib/deploy/functions/runtimes/node/triggerParser.js +0 -72
- package/lib/functions/deprecationWarnings.js +0 -21
- package/lib/functions/runtimeConfigExport.js +0 -141
- package/lib/handlePreviewToggles.js +0 -38
- package/lib/parseBoltRules.js +0 -29
package/lib/appUtils.js
CHANGED
|
@@ -27,21 +27,20 @@ async function getPlatformsFromFolder(dirPath) {
|
|
|
27
27
|
}
|
|
28
28
|
exports.getPlatformsFromFolder = getPlatformsFromFolder;
|
|
29
29
|
async function detectApps(dirPath) {
|
|
30
|
-
const packageJsonFiles = await
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const flutterApps =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
.flat()
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
.filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)));
|
|
30
|
+
const [packageJsonFiles, pubSpecYamlFiles, srcMainFolders, xCodeProjects] = await Promise.all([
|
|
31
|
+
detectFiles(dirPath, "package.json"),
|
|
32
|
+
detectFiles(dirPath, "pubspec.yaml"),
|
|
33
|
+
detectFiles(dirPath, "src/main/"),
|
|
34
|
+
detectFiles(dirPath, "*.xcodeproj/"),
|
|
35
|
+
]);
|
|
36
|
+
const [adminAndWebApps, flutterApps, androidAppsRaw, iosAppsRaw] = await Promise.all([
|
|
37
|
+
Promise.all(packageJsonFiles.map((p) => packageJsonToAdminOrWebApp(dirPath, p))).then((r) => r.flat()),
|
|
38
|
+
Promise.all(pubSpecYamlFiles.map((f) => processFlutterDir(dirPath, f))).then((r) => r.flat()),
|
|
39
|
+
Promise.all(srcMainFolders.map((f) => processAndroidDir(dirPath, f))).then((r) => r.flat()),
|
|
40
|
+
Promise.all(xCodeProjects.map((f) => processIosDir(dirPath, f))).then((r) => r.flat()),
|
|
41
|
+
]);
|
|
42
|
+
const androidApps = androidAppsRaw.filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)));
|
|
43
|
+
const iosApps = iosAppsRaw.filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)));
|
|
45
44
|
return [...flutterApps, ...androidApps, ...iosApps, ...adminAndWebApps];
|
|
46
45
|
}
|
|
47
46
|
exports.detectApps = detectApps;
|
package/lib/archiveDirectory.js
CHANGED
|
@@ -5,17 +5,12 @@ const archiver = require("archiver");
|
|
|
5
5
|
const filesize = require("filesize");
|
|
6
6
|
const fs = require("fs");
|
|
7
7
|
const path = require("path");
|
|
8
|
-
const tar = require("tar");
|
|
9
8
|
const tmp = require("tmp");
|
|
10
9
|
const error_1 = require("./error");
|
|
11
|
-
const listFiles_1 = require("./listFiles");
|
|
12
10
|
const logger_1 = require("./logger");
|
|
13
11
|
const fsAsync = require("./fsAsync");
|
|
14
12
|
async function archiveDirectory(sourceDirectory, options = {}) {
|
|
15
|
-
|
|
16
|
-
if (options.type === "zip") {
|
|
17
|
-
postfix = ".zip";
|
|
18
|
-
}
|
|
13
|
+
const postfix = ".zip";
|
|
19
14
|
const tempFile = tmp.fileSync({
|
|
20
15
|
prefix: "firebase-archive-",
|
|
21
16
|
postfix,
|
|
@@ -23,13 +18,7 @@ async function archiveDirectory(sourceDirectory, options = {}) {
|
|
|
23
18
|
if (!options.ignore) {
|
|
24
19
|
options.ignore = [];
|
|
25
20
|
}
|
|
26
|
-
|
|
27
|
-
if (options.type === "zip") {
|
|
28
|
-
makeArchive = zipDirectory(sourceDirectory, tempFile, options);
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
makeArchive = tarDirectory(sourceDirectory, tempFile, options);
|
|
32
|
-
}
|
|
21
|
+
const makeArchive = zipDirectory(sourceDirectory, tempFile, options);
|
|
33
22
|
try {
|
|
34
23
|
const archive = await makeArchive;
|
|
35
24
|
logger_1.logger.debug(`Archived ${filesize(archive.size)} in ${sourceDirectory}.`);
|
|
@@ -43,37 +32,6 @@ async function archiveDirectory(sourceDirectory, options = {}) {
|
|
|
43
32
|
}
|
|
44
33
|
}
|
|
45
34
|
exports.archiveDirectory = archiveDirectory;
|
|
46
|
-
async function tarDirectory(sourceDirectory, tempFile, options) {
|
|
47
|
-
const allFiles = (0, listFiles_1.listFiles)(sourceDirectory, options.ignore);
|
|
48
|
-
try {
|
|
49
|
-
fs.statSync(sourceDirectory);
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
if (err.code === "ENOENT") {
|
|
53
|
-
throw new error_1.FirebaseError(`Could not read directory "${sourceDirectory}"`);
|
|
54
|
-
}
|
|
55
|
-
throw err;
|
|
56
|
-
}
|
|
57
|
-
if (!allFiles.length) {
|
|
58
|
-
throw new error_1.FirebaseError(`Cannot create a tar archive with 0 files from directory "${sourceDirectory}"`);
|
|
59
|
-
}
|
|
60
|
-
await tar.create({
|
|
61
|
-
gzip: true,
|
|
62
|
-
file: tempFile.name,
|
|
63
|
-
cwd: sourceDirectory,
|
|
64
|
-
follow: true,
|
|
65
|
-
noDirRecurse: true,
|
|
66
|
-
portable: true,
|
|
67
|
-
}, allFiles);
|
|
68
|
-
const stats = fs.statSync(tempFile.name);
|
|
69
|
-
return {
|
|
70
|
-
file: tempFile.name,
|
|
71
|
-
stream: fs.createReadStream(tempFile.name),
|
|
72
|
-
manifest: allFiles,
|
|
73
|
-
size: stats.size,
|
|
74
|
-
source: sourceDirectory,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
35
|
async function zipDirectory(sourceDirectory, tempFile, options) {
|
|
78
36
|
const archiveFileStream = fs.createWriteStream(tempFile.name, {
|
|
79
37
|
flags: "w",
|
|
@@ -84,7 +42,11 @@ async function zipDirectory(sourceDirectory, tempFile, options) {
|
|
|
84
42
|
const allFiles = [];
|
|
85
43
|
let files;
|
|
86
44
|
try {
|
|
87
|
-
files = await fsAsync.readdirRecursive({
|
|
45
|
+
files = await fsAsync.readdirRecursive({
|
|
46
|
+
path: sourceDirectory,
|
|
47
|
+
ignore: options.ignore,
|
|
48
|
+
ignoreSymlinks: true,
|
|
49
|
+
});
|
|
88
50
|
}
|
|
89
51
|
catch (err) {
|
|
90
52
|
if (err.code === "ENOENT") {
|
package/lib/bin/cli.js
CHANGED
|
@@ -9,13 +9,13 @@ marked_1.marked.use((0, marked_terminal_1.markedTerminal)());
|
|
|
9
9
|
const fs = require("node:fs");
|
|
10
10
|
const configstore_1 = require("../configstore");
|
|
11
11
|
const errorOut_1 = require("../errorOut");
|
|
12
|
-
const handlePreviewToggles_1 = require("../handlePreviewToggles");
|
|
13
12
|
const logger_1 = require("../logger");
|
|
14
13
|
const client = require("..");
|
|
15
14
|
const fsutils = require("../fsutils");
|
|
16
15
|
const utils = require("../utils");
|
|
17
16
|
const experiments_1 = require("../experiments");
|
|
18
17
|
const fetchMOTD_1 = require("../fetchMOTD");
|
|
18
|
+
const command_1 = require("../command");
|
|
19
19
|
function cli(pkg) {
|
|
20
20
|
const updateNotifier = updateNotifierPkg({ pkg });
|
|
21
21
|
const args = process.argv.slice(2);
|
|
@@ -87,13 +87,40 @@ function cli(pkg) {
|
|
|
87
87
|
process.on("uncaughtException", (err) => {
|
|
88
88
|
(0, errorOut_1.errorOut)(err);
|
|
89
89
|
});
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
const commandName = args[0];
|
|
91
|
+
const isHelp = !args.length ||
|
|
92
|
+
commandName === "help" ||
|
|
93
|
+
(args.length === 1 && commandName === "ext") ||
|
|
94
|
+
commandName === "--help";
|
|
95
|
+
const hasHelpFlag = args.includes("--help") || args.includes("-h");
|
|
96
|
+
if (hasHelpFlag) {
|
|
97
|
+
client.getCommand(commandName);
|
|
98
|
+
}
|
|
99
|
+
if (isHelp) {
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
const loadAll = (obj) => {
|
|
102
|
+
if (seen.has(obj))
|
|
103
|
+
return;
|
|
104
|
+
seen.add(obj);
|
|
105
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
106
|
+
if ((0, command_1.isCommandModule)(value)) {
|
|
107
|
+
value.load();
|
|
108
|
+
}
|
|
109
|
+
else if (typeof value === "object" &&
|
|
110
|
+
value !== null &&
|
|
111
|
+
!Array.isArray(value) &&
|
|
112
|
+
key !== "cli") {
|
|
113
|
+
loadAll(value);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
loadAll(client);
|
|
118
|
+
}
|
|
119
|
+
if (!args.length) {
|
|
120
|
+
client.cli.help();
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
cmd = client.cli.parse(process.argv);
|
|
97
124
|
}
|
|
98
125
|
}
|
|
99
126
|
exports.cli = cli;
|
package/lib/bin/mcp.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.mcp = void 0;
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const util_1 = require("util");
|
|
5
7
|
const logger_1 = require("../logger");
|
|
6
8
|
const index_1 = require("../mcp/index");
|
|
7
|
-
const
|
|
9
|
+
const index_js_1 = require("../mcp/prompts/index.js");
|
|
10
|
+
const index_js_2 = require("../mcp/resources/index.js");
|
|
11
|
+
const index_js_3 = require("../mcp/tools/index.js");
|
|
8
12
|
const types_1 = require("../mcp/types");
|
|
9
|
-
const index_js_1 = require("../mcp/tools/index.js");
|
|
10
|
-
const index_js_2 = require("../mcp/prompts/index.js");
|
|
11
|
-
const index_js_3 = require("../mcp/resources/index.js");
|
|
12
|
-
const path_1 = require("path");
|
|
13
13
|
const STARTUP_MESSAGE = `
|
|
14
14
|
This is a running process of the Firebase MCP server. This command should only be executed by an MCP client. An example MCP client configuration might be:
|
|
15
15
|
|
|
@@ -22,28 +22,60 @@ This is a running process of the Firebase MCP server. This command should only b
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
`;
|
|
25
|
+
const HELP_TEXT = `Usage: firebase mcp [options]
|
|
26
|
+
|
|
27
|
+
Description:
|
|
28
|
+
Starts the Model Context Protocol (MCP) server for the Firebase CLI. This server provides a
|
|
29
|
+
standardized way for AI agents and IDEs to interact with your Firebase project.
|
|
30
|
+
|
|
31
|
+
Tool Discovery & Loading:
|
|
32
|
+
The server automatically determines which tools to expose based on your project context.
|
|
33
|
+
|
|
34
|
+
1. Auto-Detection (Default):
|
|
35
|
+
- Scans 'firebase.json' for configured services (e.g., Hosting, Firestore).
|
|
36
|
+
- Checks enabled Google Cloud APIs for the active project.
|
|
37
|
+
- Inspects project files for specific SDKs (e.g., Crashlytics in Android/iOS).
|
|
38
|
+
|
|
39
|
+
2. Manual Overrides:
|
|
40
|
+
- Use '--only' to restrict tool discovery to specific feature sets (e.g., core, firestore).
|
|
41
|
+
- Use '--tools' to disable auto-detection entirely and load specific tools by name.
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
--dir <path> Project root directory (defaults to current working directory).
|
|
45
|
+
--only <features> Comma-separated list of features to enable (e.g. core, firestore).
|
|
46
|
+
If specified, auto-detection is disabled for other features.
|
|
47
|
+
--tools <tools> Comma-separated list of specific tools to enable. Disables
|
|
48
|
+
auto-detection entirely.
|
|
49
|
+
-h, --help Show this help message.
|
|
50
|
+
`;
|
|
25
51
|
async function mcp() {
|
|
26
52
|
const { values } = (0, util_1.parseArgs)({
|
|
27
53
|
options: {
|
|
28
54
|
only: { type: "string", default: "" },
|
|
55
|
+
tools: { type: "string", default: "" },
|
|
29
56
|
dir: { type: "string" },
|
|
30
57
|
"generate-tool-list": { type: "boolean", default: false },
|
|
31
58
|
"generate-prompt-list": { type: "boolean", default: false },
|
|
32
59
|
"generate-resource-list": { type: "boolean", default: false },
|
|
60
|
+
help: { type: "boolean", default: false, short: "h" },
|
|
33
61
|
},
|
|
34
62
|
allowPositionals: true,
|
|
35
63
|
});
|
|
36
64
|
let earlyExit = false;
|
|
65
|
+
if (values.help) {
|
|
66
|
+
console.log(HELP_TEXT);
|
|
67
|
+
earlyExit = true;
|
|
68
|
+
}
|
|
37
69
|
if (values["generate-tool-list"]) {
|
|
38
|
-
console.log((0,
|
|
70
|
+
console.log((0, index_js_3.markdownDocsOfTools)());
|
|
39
71
|
earlyExit = true;
|
|
40
72
|
}
|
|
41
73
|
if (values["generate-prompt-list"]) {
|
|
42
|
-
console.log((0,
|
|
74
|
+
console.log((0, index_js_1.markdownDocsOfPrompts)());
|
|
43
75
|
earlyExit = true;
|
|
44
76
|
}
|
|
45
77
|
if (values["generate-resource-list"]) {
|
|
46
|
-
console.log((0,
|
|
78
|
+
console.log((0, index_js_2.markdownDocsOfResources)());
|
|
47
79
|
earlyExit = true;
|
|
48
80
|
}
|
|
49
81
|
if (earlyExit)
|
|
@@ -52,9 +84,15 @@ async function mcp() {
|
|
|
52
84
|
(0, logger_1.useFileLogger)();
|
|
53
85
|
const activeFeatures = (values.only || "")
|
|
54
86
|
.split(",")
|
|
87
|
+
.map((f) => f.trim())
|
|
55
88
|
.filter((f) => types_1.SERVER_FEATURES.includes(f));
|
|
89
|
+
const enabledTools = (values.tools || "")
|
|
90
|
+
.split(",")
|
|
91
|
+
.map((t) => t.trim())
|
|
92
|
+
.filter((t) => t.length > 0);
|
|
56
93
|
const server = new index_1.FirebaseMcpServer({
|
|
57
94
|
activeFeatures,
|
|
95
|
+
enabledTools,
|
|
58
96
|
projectRoot: values.dir ? (0, path_1.resolve)(values.dir) : undefined,
|
|
59
97
|
});
|
|
60
98
|
await server.start();
|
package/lib/command.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateProjectId = exports.Command = void 0;
|
|
3
|
+
exports.validateProjectId = exports.Command = exports.isCommandModule = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const path = require("node:path");
|
|
6
6
|
const lodash_1 = require("lodash");
|
|
@@ -17,6 +17,10 @@ const studio_1 = require("./management/studio");
|
|
|
17
17
|
const requireAuth_1 = require("./requireAuth");
|
|
18
18
|
const logger_1 = require("./logger");
|
|
19
19
|
const env_1 = require("./env");
|
|
20
|
+
function isCommandModule(value) {
|
|
21
|
+
return typeof value === "function" && typeof value.load === "function";
|
|
22
|
+
}
|
|
23
|
+
exports.isCommandModule = isCommandModule;
|
|
20
24
|
class Command {
|
|
21
25
|
constructor(cmd) {
|
|
22
26
|
this.cmd = cmd;
|
|
@@ -165,7 +165,7 @@ exports.command = new command_1.Command("dataconnect:execute [file] [operationNa
|
|
|
165
165
|
return query;
|
|
166
166
|
}
|
|
167
167
|
async function getServiceInfo() {
|
|
168
|
-
return (0, load_1.
|
|
168
|
+
return (0, load_1.pickOneService)(projectId, options.config, serviceId || undefined, locationId || undefined).catch((e) => {
|
|
169
169
|
if (!(e instanceof error_1.FirebaseError)) {
|
|
170
170
|
return Promise.reject(e);
|
|
171
171
|
}
|
|
@@ -15,7 +15,9 @@ const error_1 = require("../error");
|
|
|
15
15
|
const init_1 = require("./init");
|
|
16
16
|
const hub_1 = require("../emulator/hub");
|
|
17
17
|
exports.command = new command_1.Command("dataconnect:sdk:generate")
|
|
18
|
-
.description("generate typed SDKs
|
|
18
|
+
.description("generate typed SDKs to use Data Connect in your apps")
|
|
19
|
+
.option("--service <serviceId>", "the serviceId of the Data Connect service. If not provided, generates SDKs for all services.")
|
|
20
|
+
.option("--location <location>", "the location of the Data Connect service. Only needed if service ID is used in multiple locations.")
|
|
19
21
|
.option("--watch", "watch for changes to your connector GQL files and regenerate your SDKs when updates occur")
|
|
20
22
|
.action(async (options) => {
|
|
21
23
|
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
@@ -46,7 +48,7 @@ exports.command = new command_1.Command("dataconnect:sdk:generate")
|
|
|
46
48
|
justRanInit = true;
|
|
47
49
|
options.config = config;
|
|
48
50
|
}
|
|
49
|
-
let serviceInfosWithSDKs = await loadAllWithSDKs(projectId, config);
|
|
51
|
+
let serviceInfosWithSDKs = await loadAllWithSDKs(projectId, config, options);
|
|
50
52
|
if (!serviceInfosWithSDKs.length) {
|
|
51
53
|
if (justRanInit || options.nonInteractive) {
|
|
52
54
|
throw new error_1.FirebaseError(`No generated SDKs are configured during init. Please run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
|
|
@@ -65,12 +67,12 @@ exports.command = new command_1.Command("dataconnect:sdk:generate")
|
|
|
65
67
|
await dataconnectSdkInit.askQuestions(setup);
|
|
66
68
|
await dataconnectSdkInit.actuate(setup, config);
|
|
67
69
|
justRanInit = true;
|
|
68
|
-
serviceInfosWithSDKs = await loadAllWithSDKs(projectId, config);
|
|
70
|
+
serviceInfosWithSDKs = await loadAllWithSDKs(projectId, config, options);
|
|
69
71
|
}
|
|
70
72
|
await generateSDKsInAll(options, serviceInfosWithSDKs, justRanInit);
|
|
71
73
|
});
|
|
72
|
-
async function loadAllWithSDKs(projectId, config) {
|
|
73
|
-
const serviceInfos = await (0, load_1.
|
|
74
|
+
async function loadAllWithSDKs(projectId, config, options) {
|
|
75
|
+
const serviceInfos = await (0, load_1.pickServices)(projectId || hub_1.EmulatorHub.MISSING_PROJECT_PLACEHOLDER, config, options.service, options.location);
|
|
74
76
|
return serviceInfos.filter((serviceInfo) => serviceInfo.connectorInfo.some((c) => {
|
|
75
77
|
var _a, _b, _c, _d, _e;
|
|
76
78
|
return (((_a = c.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) ||
|
|
@@ -9,19 +9,21 @@ const load_1 = require("../dataconnect/load");
|
|
|
9
9
|
const schemaMigration_1 = require("../dataconnect/schemaMigration");
|
|
10
10
|
const requireAuth_1 = require("../requireAuth");
|
|
11
11
|
const types_1 = require("../dataconnect/types");
|
|
12
|
-
exports.command = new command_1.Command("dataconnect:sql:diff
|
|
13
|
-
.description("display the differences between
|
|
12
|
+
exports.command = new command_1.Command("dataconnect:sql:diff")
|
|
13
|
+
.description("display the differences between the local Data Connect schema and your CloudSQL database's schema")
|
|
14
|
+
.option("--service <serviceId>", "the serviceId of the Data Connect service")
|
|
15
|
+
.option("--location <location>", "the location of the Data Connect service. Only needed if service ID is used in multiple locations.")
|
|
14
16
|
.before(requirePermissions_1.requirePermissions, [
|
|
15
17
|
"firebasedataconnect.services.list",
|
|
16
18
|
"firebasedataconnect.schemas.list",
|
|
17
19
|
"firebasedataconnect.schemas.update",
|
|
18
20
|
])
|
|
19
21
|
.before(requireAuth_1.requireAuth)
|
|
20
|
-
.action(async (
|
|
22
|
+
.action(async (options) => {
|
|
21
23
|
var _a;
|
|
22
24
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
23
25
|
await (0, ensureApis_1.ensureApis)(projectId);
|
|
24
|
-
const serviceInfo = await (0, load_1.
|
|
26
|
+
const serviceInfo = await (0, load_1.pickOneService)(projectId, options.config, options.service, options.location);
|
|
25
27
|
const diffs = await (0, schemaMigration_1.diffSchema)(options, (0, types_1.mainSchema)(serviceInfo.schemas), (_a = (0, types_1.mainSchemaYaml)(serviceInfo.dataConnectYaml).datasource.postgresql) === null || _a === void 0 ? void 0 : _a.schemaValidation);
|
|
26
|
-
return { projectId,
|
|
28
|
+
return { projectId, diffs };
|
|
27
29
|
});
|
|
@@ -13,31 +13,31 @@ const permissionsSetup_1 = require("../gcp/cloudsql/permissionsSetup");
|
|
|
13
13
|
const cloudsqladmin_1 = require("../gcp/cloudsql/cloudsqladmin");
|
|
14
14
|
const types_1 = require("../dataconnect/types");
|
|
15
15
|
const allowedRoles = Object.keys(permissionsSetup_1.fdcSqlRoleMap);
|
|
16
|
-
exports.command = new command_1.Command("dataconnect:sql:grant
|
|
16
|
+
exports.command = new command_1.Command("dataconnect:sql:grant")
|
|
17
17
|
.description("grants the SQL role <role> to the provided user or service account <email>")
|
|
18
18
|
.option("-R, --role <role>", "The SQL role to grant. One of: owner, writer, or reader.")
|
|
19
19
|
.option("-E, --email <email>", "The email of the user or service account we would like to grant the role to.")
|
|
20
|
+
.option("--service <serviceId>", "the serviceId of the Data Connect service")
|
|
21
|
+
.option("--location <location>", "the location of the Data Connect service. Only needed if service ID is used in multiple locations.")
|
|
20
22
|
.before(requirePermissions_1.requirePermissions, ["firebasedataconnect.services.list"])
|
|
21
23
|
.before(requireAuth_1.requireAuth)
|
|
22
|
-
.action(async (
|
|
23
|
-
|
|
24
|
-
const email = options.email;
|
|
25
|
-
if (!role) {
|
|
24
|
+
.action(async (options) => {
|
|
25
|
+
if (!options.role) {
|
|
26
26
|
throw new error_1.FirebaseError("-R, --role <role> is required. Run the command with -h for more info.");
|
|
27
27
|
}
|
|
28
|
-
if (!email) {
|
|
28
|
+
if (!options.email) {
|
|
29
29
|
throw new error_1.FirebaseError("-E, --email <email> is required. Run the command with -h for more info.");
|
|
30
30
|
}
|
|
31
|
-
if (!allowedRoles.includes(role.toLowerCase())) {
|
|
31
|
+
if (!allowedRoles.includes(options.role.toLowerCase())) {
|
|
32
32
|
throw new error_1.FirebaseError(`Role should be one of ${allowedRoles.join(" | ")}.`);
|
|
33
33
|
}
|
|
34
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
35
|
+
await (0, ensureApis_1.ensureApis)(projectId);
|
|
36
|
+
const serviceInfo = await (0, load_1.pickOneService)(projectId, options.config, options.service, options.location);
|
|
34
37
|
const userIsCSQLAdmin = await (0, cloudsqladmin_1.iamUserIsCSQLAdmin)(options);
|
|
35
38
|
if (!userIsCSQLAdmin) {
|
|
36
|
-
throw new error_1.FirebaseError(`Only users with 'roles/cloudsql.admin' can grant SQL roles. If you do not have this role, ask your database administrator to run this command or manually grant ${role} to ${email}`);
|
|
39
|
+
throw new error_1.FirebaseError(`Only users with 'roles/cloudsql.admin' can grant SQL roles. If you do not have this role, ask your database administrator to run this command or manually grant ${options.role} to ${options.email}`);
|
|
37
40
|
}
|
|
38
|
-
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
39
|
-
await (0, ensureApis_1.ensureApis)(projectId);
|
|
40
|
-
const serviceInfo = await (0, load_1.pickService)(projectId, options.config, serviceId);
|
|
41
41
|
await (0, schemaMigration_1.grantRoleToUserInSchema)(options, (0, types_1.mainSchema)(serviceInfo.schemas));
|
|
42
|
-
return { projectId
|
|
42
|
+
return { projectId };
|
|
43
43
|
});
|
|
@@ -11,8 +11,10 @@ const requirePermissions_1 = require("../requirePermissions");
|
|
|
11
11
|
const ensureApis_1 = require("../dataconnect/ensureApis");
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
13
|
const types_1 = require("../dataconnect/types");
|
|
14
|
-
exports.command = new command_1.Command("dataconnect:sql:migrate
|
|
14
|
+
exports.command = new command_1.Command("dataconnect:sql:migrate")
|
|
15
15
|
.description("migrate your CloudSQL database's schema to match your local Data Connect schema")
|
|
16
|
+
.option("--service <serviceId>", "the serviceId of the Data Connect service")
|
|
17
|
+
.option("--location <location>", "the location of the Data Connect service. Only needed if service ID is used in multiple locations.")
|
|
16
18
|
.before(requirePermissions_1.requirePermissions, [
|
|
17
19
|
"firebasedataconnect.services.list",
|
|
18
20
|
"firebasedataconnect.schemas.list",
|
|
@@ -21,11 +23,11 @@ exports.command = new command_1.Command("dataconnect:sql:migrate [serviceId]")
|
|
|
21
23
|
])
|
|
22
24
|
.before(requireAuth_1.requireAuth)
|
|
23
25
|
.withForce("execute any required database changes without prompting")
|
|
24
|
-
.action(async (
|
|
26
|
+
.action(async (options) => {
|
|
25
27
|
var _a, _b;
|
|
26
28
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
27
29
|
await (0, ensureApis_1.ensureApis)(projectId);
|
|
28
|
-
const serviceInfo = await (0, load_1.
|
|
30
|
+
const serviceInfo = await (0, load_1.pickOneService)(projectId, options.config, options.service, options.location);
|
|
29
31
|
const instanceId = (_a = (0, types_1.mainSchemaYaml)(serviceInfo.dataConnectYaml).datasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instanceId;
|
|
30
32
|
if (!instanceId) {
|
|
31
33
|
throw new error_1.FirebaseError("dataconnect.yaml is missing field schema.datasource.postgresql.cloudsql.instanceId");
|
|
@@ -42,5 +44,5 @@ exports.command = new command_1.Command("dataconnect:sql:migrate [serviceId]")
|
|
|
42
44
|
else {
|
|
43
45
|
(0, utils_1.logLabeledSuccess)("dataconnect", "Database schema is already up to date!");
|
|
44
46
|
}
|
|
45
|
-
return { projectId,
|
|
47
|
+
return { projectId, diffs };
|
|
46
48
|
});
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.command = void 0;
|
|
4
4
|
const command_1 = require("../command");
|
|
5
5
|
const projectUtils_1 = require("../projectUtils");
|
|
6
|
-
const load_1 = require("../dataconnect/load");
|
|
7
6
|
const error_1 = require("../error");
|
|
8
7
|
const requireAuth_1 = require("../requireAuth");
|
|
9
8
|
const requirePermissions_1 = require("../requirePermissions");
|
|
@@ -12,9 +11,12 @@ const permissionsSetup_1 = require("../gcp/cloudsql/permissionsSetup");
|
|
|
12
11
|
const permissions_1 = require("../gcp/cloudsql/permissions");
|
|
13
12
|
const schemaMigration_1 = require("../dataconnect/schemaMigration");
|
|
14
13
|
const connect_1 = require("../gcp/cloudsql/connect");
|
|
14
|
+
const load_1 = require("../dataconnect/load");
|
|
15
15
|
const types_1 = require("../dataconnect/types");
|
|
16
|
-
exports.command = new command_1.Command("dataconnect:sql:setup
|
|
16
|
+
exports.command = new command_1.Command("dataconnect:sql:setup")
|
|
17
17
|
.description("set up your CloudSQL database")
|
|
18
|
+
.option("--service <serviceId>", "the serviceId of the Data Connect service")
|
|
19
|
+
.option("--location <location>", "the location of the Data Connect service. Only needed if service ID is used in multiple locations.")
|
|
18
20
|
.before(requirePermissions_1.requirePermissions, [
|
|
19
21
|
"firebasedataconnect.services.list",
|
|
20
22
|
"firebasedataconnect.schemas.list",
|
|
@@ -22,11 +24,11 @@ exports.command = new command_1.Command("dataconnect:sql:setup [serviceId]")
|
|
|
22
24
|
"cloudsql.instances.connect",
|
|
23
25
|
])
|
|
24
26
|
.before(requireAuth_1.requireAuth)
|
|
25
|
-
.action(async (
|
|
27
|
+
.action(async (options) => {
|
|
26
28
|
var _a;
|
|
27
29
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
28
30
|
await (0, ensureApis_1.ensureApis)(projectId);
|
|
29
|
-
const serviceInfo = await (0, load_1.
|
|
31
|
+
const serviceInfo = await (0, load_1.pickOneService)(projectId, options.config, options.service, options.location);
|
|
30
32
|
const instanceId = (_a = (0, types_1.mainSchemaYaml)(serviceInfo.dataConnectYaml).datasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instanceId;
|
|
31
33
|
if (!instanceId) {
|
|
32
34
|
throw new error_1.FirebaseError("dataconnect.yaml is missing field schema.datasource.postgresql.cloudsql.instanceId");
|
|
@@ -73,14 +73,16 @@ async function mainShellLoop(conn) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
exports.command = new command_1.Command("dataconnect:sql:shell
|
|
76
|
+
exports.command = new command_1.Command("dataconnect:sql:shell")
|
|
77
77
|
.description("start a shell connected directly to your Data Connect service's linked CloudSQL instance")
|
|
78
|
+
.option("--service <serviceId>", "the serviceId of the Data Connect service")
|
|
79
|
+
.option("--location <location>", "the location of the Data Connect service. Only needed if service ID is used in multiple locations.")
|
|
78
80
|
.before(requirePermissions_1.requirePermissions, ["firebasedataconnect.services.list", "cloudsql.instances.connect"])
|
|
79
81
|
.before(requireAuth_1.requireAuth)
|
|
80
|
-
.action(async (
|
|
82
|
+
.action(async (options) => {
|
|
81
83
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
82
84
|
await (0, ensureApis_1.ensureApis)(projectId);
|
|
83
|
-
const serviceInfo = await (0, load_1.
|
|
85
|
+
const serviceInfo = await (0, load_1.pickOneService)(projectId, options.config, options.service, options.location);
|
|
84
86
|
const { instanceId, databaseId } = (0, schemaMigration_1.getIdentifiers)((0, types_1.mainSchema)(serviceInfo.schemas));
|
|
85
87
|
const { user: username } = await (0, connect_1.getIAMUser)(options);
|
|
86
88
|
const instance = await cloudSqlAdminClient.getInstance(projectId, instanceId);
|
|
@@ -106,5 +108,5 @@ exports.command = new command_1.Command("dataconnect:sql:shell [serviceId]")
|
|
|
106
108
|
conn.release();
|
|
107
109
|
await pool.end();
|
|
108
110
|
connector.close();
|
|
109
|
-
return { projectId
|
|
111
|
+
return { projectId };
|
|
110
112
|
});
|
|
@@ -10,12 +10,12 @@ const requirePermissions_1 = require("../requirePermissions");
|
|
|
10
10
|
const functionsConfig = require("../functionsConfig");
|
|
11
11
|
const functionsConfigClone_1 = require("../functionsConfigClone");
|
|
12
12
|
const utils = require("../utils");
|
|
13
|
-
const deprecationWarnings_1 = require("../functions/deprecationWarnings");
|
|
14
13
|
exports.command = new command_1.Command("functions:config:clone")
|
|
15
14
|
.description("clone environment config from another project")
|
|
16
15
|
.option("--from <projectId>", "the project from which to clone configuration")
|
|
17
16
|
.option("--only <keys>", "a comma-separated list of keys to clone")
|
|
18
17
|
.option("--except <keys>", "a comma-separated list of keys to not clone")
|
|
18
|
+
.before(functionsConfig.ensureLegacyRuntimeConfigCommandsEnabled)
|
|
19
19
|
.before(requirePermissions_1.requirePermissions, [
|
|
20
20
|
"runtimeconfig.configs.list",
|
|
21
21
|
"runtimeconfig.configs.create",
|
|
@@ -51,5 +51,5 @@ exports.command = new command_1.Command("functions:config:clone")
|
|
|
51
51
|
await (0, functionsConfigClone_1.functionsConfigClone)(options.from, projectId, only, except);
|
|
52
52
|
utils.logSuccess(`Cloned functions config from ${clc.bold(options.from)} into ${clc.bold(projectId)}`);
|
|
53
53
|
logger_1.logger.info(`\nPlease deploy your functions for the change to take effect by running ${clc.bold("firebase deploy --only functions")}\n`);
|
|
54
|
-
|
|
54
|
+
functionsConfig.logFunctionsConfigDeprecationWarning();
|
|
55
55
|
});
|