firebase-tools 14.4.0 → 14.5.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/apphosting/backend.js +2 -0
- package/lib/bin/mcp.js +1 -0
- package/lib/commands/init.js +0 -3
- package/lib/config.js +42 -24
- package/lib/dataconnect/cloudAICompanionClient.js +7 -2
- package/lib/dataconnect/cloudAICompanionTypes.js +2 -0
- package/lib/dataconnect/fileUtils.js +11 -4
- package/lib/dataconnect/schemaMigration.js +6 -7
- package/lib/deploy/apphosting/deploy.js +3 -0
- package/lib/deploy/apphosting/prepare.js +34 -28
- package/lib/deploy/apphosting/release.js +3 -0
- package/lib/deploy/firestore/deploy.js +47 -4
- package/lib/emulator/apphosting/serve.js +1 -1
- package/lib/emulator/downloadableEmulatorInfo.json +39 -18
- package/lib/emulator/downloadableEmulators.js +15 -59
- package/lib/extensions/manifest.js +0 -3
- package/lib/frameworks/angular/index.js +1 -1
- package/lib/frameworks/angular/utils.js +17 -6
- package/lib/gcp/apphosting.js +13 -1
- package/lib/gcp/run.js +19 -1
- package/lib/init/features/apphosting.js +3 -2
- package/lib/init/features/database.js +11 -19
- package/lib/init/features/dataconnect/index.js +27 -26
- package/lib/init/features/dataconnect/sdk.js +19 -6
- package/lib/init/features/emulators.js +4 -3
- package/lib/init/features/firestore/index.js +44 -34
- package/lib/init/features/firestore/indexes.js +12 -13
- package/lib/init/features/firestore/rules.js +8 -15
- package/lib/init/features/genkit/index.js +16 -9
- package/lib/init/features/hosting/index.js +9 -8
- package/lib/init/features/index.js +3 -2
- package/lib/init/features/storage.js +31 -8
- package/lib/init/index.js +5 -1
- package/lib/mcp/index.js +10 -6
- package/lib/mcp/tool.js +2 -1
- package/lib/mcp/tools/apphosting/fetch_logs.js +69 -0
- package/lib/mcp/tools/apphosting/index.js +6 -0
- package/lib/mcp/tools/apphosting/list_backends.js +51 -0
- package/lib/mcp/tools/core/index.js +2 -0
- package/lib/mcp/tools/core/init.js +26 -2
- package/lib/mcp/tools/core/list_apps.js +10 -5
- package/lib/mcp/tools/core/list_projects.js +45 -0
- package/lib/mcp/tools/dataconnect/generate_operation.js +1 -1
- package/lib/mcp/tools/firestore/query_collection.js +13 -7
- package/lib/mcp/tools/index.js +2 -0
- package/lib/mcp/tools/storage/get_download_url.js +1 -1
- package/lib/mcp/types.js +1 -0
- package/lib/mcp/util.js +137 -1
- package/lib/mcp/util.test.js +468 -0
- package/lib/prompt.js +1 -1
- package/lib/track.js +1 -1
- package/package.json +1 -1
- package/schema/connector-yaml.json +12 -0
- package/schema/extension-yaml.json +17 -4
- package/schema/firebase-config.json +3 -0
- package/standalone/package.json +1 -1
- package/templates/dataconnect-prompts/operation-generation-cursor-windsurf-rule.txt +273 -0
- package/templates/dataconnect-prompts/schema-generation-cursor-windsurf-rule.txt +653 -0
- package/templates/genkit/firebase.1.0.0.template +5 -0
- package/templates/init/firestore/firestore.rules +2 -0
- package/templates/init/functions/typescript/package.lint.json +1 -1
- package/templates/init/functions/typescript/package.nolint.json +1 -1
|
@@ -108,10 +108,12 @@ async function doSetup(projectId, webAppName, serviceAccount) {
|
|
|
108
108
|
exports.doSetup = doSetup;
|
|
109
109
|
async function doSetupSourceDeploy(projectId, backendId) {
|
|
110
110
|
const location = await promptLocation(projectId, "Select a primary region to host your backend:\n");
|
|
111
|
+
const webAppSpinner = ora("Creating a new web app...\n").start();
|
|
111
112
|
const webApp = await app_1.webApps.getOrCreateWebApp(projectId, null, backendId);
|
|
112
113
|
if (!webApp) {
|
|
113
114
|
(0, utils_1.logWarning)(`Firebase web app not set`);
|
|
114
115
|
}
|
|
116
|
+
webAppSpinner.stop();
|
|
115
117
|
const createBackendSpinner = ora("Creating your new backend...").start();
|
|
116
118
|
const backend = await createBackend(projectId, location, backendId, null, undefined, webApp === null || webApp === void 0 ? void 0 : webApp.id);
|
|
117
119
|
createBackendSpinner.succeed(`Successfully created backend!\n\t${backend.name}\n`);
|
package/lib/bin/mcp.js
CHANGED
package/lib/commands/init.js
CHANGED
|
@@ -186,12 +186,9 @@ async function initAction(feature, options) {
|
|
|
186
186
|
}
|
|
187
187
|
await (0, init_1.init)(setup, config, options);
|
|
188
188
|
logger_1.logger.info();
|
|
189
|
-
utils.logBullet("Writing configuration info to " + clc.bold("firebase.json") + "...");
|
|
190
189
|
config.writeProjectFile("firebase.json", setup.config);
|
|
191
|
-
utils.logBullet("Writing project information to " + clc.bold(".firebaserc") + "...");
|
|
192
190
|
config.writeProjectFile(".firebaserc", setup.rcfile);
|
|
193
191
|
if (!fsutils.fileExistsSync(config.path(".gitignore"))) {
|
|
194
|
-
utils.logBullet("Writing gitignore file to " + clc.bold(".gitignore") + "...");
|
|
195
192
|
config.writeProjectFile(".gitignore", GITIGNORE_TEMPLATE);
|
|
196
193
|
}
|
|
197
194
|
logger_1.logger.info();
|
package/lib/config.js
CHANGED
|
@@ -156,11 +156,20 @@ class Config {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
writeProjectFile(p, content) {
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
const path = this.path(p);
|
|
160
|
+
fs.ensureFileSync(path);
|
|
161
|
+
fs.writeFileSync(path, stringifyContent(content), "utf8");
|
|
162
|
+
switch (p) {
|
|
163
|
+
case "firebase.json":
|
|
164
|
+
utils.logSuccess("Wrote configuration info to " + clc.bold("firebase.json"));
|
|
165
|
+
break;
|
|
166
|
+
case ".firebaserc":
|
|
167
|
+
utils.logSuccess("Wrote project information to " + clc.bold(".firebaserc"));
|
|
168
|
+
break;
|
|
169
|
+
default:
|
|
170
|
+
utils.logSuccess("Wrote " + clc.bold(p));
|
|
171
|
+
break;
|
|
161
172
|
}
|
|
162
|
-
fs.ensureFileSync(this.path(p));
|
|
163
|
-
fs.writeFileSync(this.path(p), content, "utf8");
|
|
164
173
|
}
|
|
165
174
|
projectFileExists(p) {
|
|
166
175
|
return fs.existsSync(this.path(p));
|
|
@@ -168,32 +177,35 @@ class Config {
|
|
|
168
177
|
deleteProjectFile(p) {
|
|
169
178
|
fs.removeSync(this.path(p));
|
|
170
179
|
}
|
|
171
|
-
async
|
|
180
|
+
async confirmWriteProjectFile(path, content, confirmByDefault) {
|
|
172
181
|
const writeTo = this.path(path);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
content = JSON.stringify(content, null, 2) + "\n";
|
|
176
|
-
}
|
|
177
|
-
let existingContent;
|
|
178
|
-
if (fsutils.fileExistsSync(writeTo)) {
|
|
179
|
-
existingContent = fsutils.readFile(writeTo);
|
|
180
|
-
}
|
|
181
|
-
if (existingContent && existingContent !== content && !force) {
|
|
182
|
-
next = await (0, prompt_1.confirm)({
|
|
183
|
-
message: "File " + clc.underline(path) + " already exists. Overwrite?",
|
|
184
|
-
default: !!confirmByDefault,
|
|
185
|
-
});
|
|
182
|
+
if (!fsutils.fileExistsSync(writeTo)) {
|
|
183
|
+
return true;
|
|
186
184
|
}
|
|
187
|
-
|
|
185
|
+
const existingContent = fsutils.readFile(writeTo);
|
|
186
|
+
const newContent = stringifyContent(content);
|
|
187
|
+
if (existingContent === newContent) {
|
|
188
188
|
utils.logBullet(clc.bold(path) + " is unchanged");
|
|
189
|
+
return false;
|
|
189
190
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
191
|
+
const shouldWrite = await (0, prompt_1.confirm)({
|
|
192
|
+
message: "File " + clc.underline(path) + " already exists. Overwrite?",
|
|
193
|
+
default: !!confirmByDefault,
|
|
194
|
+
});
|
|
195
|
+
if (!shouldWrite) {
|
|
195
196
|
utils.logBullet("Skipping write of " + clc.bold(path));
|
|
197
|
+
return false;
|
|
196
198
|
}
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
async askWriteProjectFile(path, content, force, confirmByDefault) {
|
|
202
|
+
if (!force) {
|
|
203
|
+
const shouldWrite = await this.confirmWriteProjectFile(path, content, confirmByDefault);
|
|
204
|
+
if (!shouldWrite) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
this.writeProjectFile(path, content);
|
|
197
209
|
}
|
|
198
210
|
static load(options, allowMissing) {
|
|
199
211
|
const pd = (0, detectProjectRoot_1.detectProjectRoot)(options);
|
|
@@ -241,3 +253,9 @@ Config.MATERIALIZE_TARGETS = [
|
|
|
241
253
|
"dataconnect",
|
|
242
254
|
"apphosting",
|
|
243
255
|
];
|
|
256
|
+
function stringifyContent(content) {
|
|
257
|
+
if (typeof content === "string") {
|
|
258
|
+
return content;
|
|
259
|
+
}
|
|
260
|
+
return JSON.stringify(content, null, 2) + "\n";
|
|
261
|
+
}
|
|
@@ -21,8 +21,13 @@ async function callCloudAICompanion(client, vscodeRequest, type) {
|
|
|
21
21
|
const request = buildRequest(vscodeRequest, type);
|
|
22
22
|
const { projectId } = getServiceParts(vscodeRequest.servicePath);
|
|
23
23
|
const instance = toChatResourceName(projectId);
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
try {
|
|
25
|
+
const res = await client.post(`${instance}:completeTask`, request);
|
|
26
|
+
return res.body;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return { output: { messages: [] }, error: error };
|
|
30
|
+
}
|
|
26
31
|
}
|
|
27
32
|
exports.callCloudAICompanion = callCloudAICompanion;
|
|
28
33
|
function buildRequest({ servicePath, naturalLanguageQuery, chatHistory }, type) {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getFrameworksFromPackageJson = exports.frameworksMap = exports.SUPPORTED_FRAMEWORKS = exports.resolvePackageJson = exports.getPlatformFromFolder = exports.pickService = exports.readGQLFiles = exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = void 0;
|
|
4
4
|
const fs = require("fs-extra");
|
|
5
5
|
const path = require("path");
|
|
6
|
+
const clc = require("colorette");
|
|
6
7
|
const error_1 = require("../error");
|
|
7
8
|
const types_1 = require("./types");
|
|
8
9
|
const utils_1 = require("../utils");
|
|
@@ -77,10 +78,16 @@ async function pickService(projectId, config, serviceId) {
|
|
|
77
78
|
const serviceCfgs = readFirebaseJson(config);
|
|
78
79
|
let serviceInfo;
|
|
79
80
|
if (serviceCfgs.length === 0) {
|
|
80
|
-
throw new error_1.FirebaseError("No Data Connect services found in firebase.json."
|
|
81
|
+
throw new error_1.FirebaseError("No Data Connect services found in firebase.json." +
|
|
82
|
+
`\nYou can run ${clc.bold("firebase init dataconnect")} to add a Data Connect service.`);
|
|
81
83
|
}
|
|
82
84
|
else if (serviceCfgs.length === 1) {
|
|
83
85
|
serviceInfo = await (0, load_1.load)(projectId, config, serviceCfgs[0].source);
|
|
86
|
+
if (serviceId && serviceId !== serviceInfo.dataConnectYaml.serviceId) {
|
|
87
|
+
throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json. Found ${serviceInfo.dataConnectYaml.serviceId}.` +
|
|
88
|
+
`\nYou can run ${clc.bold("firebase init dataconnect")} to add this Data Connect service.`);
|
|
89
|
+
}
|
|
90
|
+
return serviceInfo;
|
|
84
91
|
}
|
|
85
92
|
else {
|
|
86
93
|
if (!serviceId) {
|
|
@@ -89,11 +96,11 @@ async function pickService(projectId, config, serviceId) {
|
|
|
89
96
|
const infos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, config, c.source)));
|
|
90
97
|
const maybe = infos.find((i) => i.dataConnectYaml.serviceId === serviceId);
|
|
91
98
|
if (!maybe) {
|
|
92
|
-
throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json.`
|
|
99
|
+
throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json. Found ${infos.map((i) => i.dataConnectYaml.serviceId).join(", ")}.` +
|
|
100
|
+
`\nYou can run ${clc.bold("firebase init dataconnect")} to add this Data Connect service.`);
|
|
93
101
|
}
|
|
94
|
-
|
|
102
|
+
return maybe;
|
|
95
103
|
}
|
|
96
|
-
return serviceInfo;
|
|
97
104
|
}
|
|
98
105
|
exports.pickService = pickService;
|
|
99
106
|
const WEB_INDICATORS = ["package.json", "package-lock.json", "node_modules"];
|
|
@@ -29,6 +29,7 @@ async function setupSchemaIfNecessary(instanceId, databaseId, options) {
|
|
|
29
29
|
return schemaInfo.setupStatus;
|
|
30
30
|
}
|
|
31
31
|
async function diffSchema(options, schema, schemaValidation) {
|
|
32
|
+
(0, utils_1.logLabeledBullet)("dataconnect", `generating required schema changes...`);
|
|
32
33
|
const { serviceName, instanceName, databaseId, instanceId } = getIdentifiers(schema);
|
|
33
34
|
await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, false);
|
|
34
35
|
let diffs = [];
|
|
@@ -36,15 +37,12 @@ async function diffSchema(options, schema, schemaValidation) {
|
|
|
36
37
|
let validationMode = schemaValidation !== null && schemaValidation !== void 0 ? schemaValidation : "COMPATIBLE";
|
|
37
38
|
setSchemaValidationMode(schema, validationMode);
|
|
38
39
|
try {
|
|
39
|
-
if (!schemaValidation) {
|
|
40
|
-
(0, utils_1.logLabeledBullet)("dataconnect", `generating required schema changes...`);
|
|
41
|
-
}
|
|
42
40
|
await (0, client_1.upsertSchema)(schema, true);
|
|
43
41
|
if (validationMode === "STRICT") {
|
|
44
|
-
(0, utils_1.logLabeledSuccess)("dataconnect", `
|
|
42
|
+
(0, utils_1.logLabeledSuccess)("dataconnect", `database schema of ${instanceId}:${databaseId} is up to date.`);
|
|
45
43
|
}
|
|
46
44
|
else {
|
|
47
|
-
(0, utils_1.logLabeledSuccess)("dataconnect", `
|
|
45
|
+
(0, utils_1.logLabeledSuccess)("dataconnect", `database schema of ${instanceId}:${databaseId} is compatible.`);
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
48
|
catch (err) {
|
|
@@ -97,6 +95,7 @@ async function diffSchema(options, schema, schemaValidation) {
|
|
|
97
95
|
}
|
|
98
96
|
exports.diffSchema = diffSchema;
|
|
99
97
|
async function migrateSchema(args) {
|
|
98
|
+
(0, utils_1.logLabeledBullet)("dataconnect", `generating required schema changes...`);
|
|
100
99
|
const { options, schema, validateOnly, schemaValidation } = args;
|
|
101
100
|
const { serviceName, instanceId, instanceName, databaseId } = getIdentifiers(schema);
|
|
102
101
|
await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, true);
|
|
@@ -107,7 +106,7 @@ async function migrateSchema(args) {
|
|
|
107
106
|
setSchemaValidationMode(schema, validationMode);
|
|
108
107
|
try {
|
|
109
108
|
await (0, client_1.upsertSchema)(schema, validateOnly);
|
|
110
|
-
|
|
109
|
+
(0, utils_1.logLabeledBullet)("dataconnect", `database schema of ${instanceId}:${databaseId} is up to date.`);
|
|
111
110
|
}
|
|
112
111
|
catch (err) {
|
|
113
112
|
if ((err === null || err === void 0 ? void 0 : err.status) !== 400) {
|
|
@@ -355,7 +354,7 @@ async function ensureServiceIsConnectedToCloudSql(serviceName, instanceId, datab
|
|
|
355
354
|
(0, utils_1.logLabeledWarning)("dataconnect", `Not yet linked to the Cloud SQL instance.`);
|
|
356
355
|
return;
|
|
357
356
|
}
|
|
358
|
-
(0, utils_1.logLabeledBullet)("dataconnect", `
|
|
357
|
+
(0, utils_1.logLabeledBullet)("dataconnect", `linking the Cloud SQL instance...`);
|
|
359
358
|
currentSchema = {
|
|
360
359
|
name: `${serviceName}/schemas/${types_1.SCHEMA_ID}`,
|
|
361
360
|
source: {
|
|
@@ -9,6 +9,9 @@ const projectUtils_1 = require("../../projectUtils");
|
|
|
9
9
|
const utils_1 = require("../../utils");
|
|
10
10
|
const util_1 = require("./util");
|
|
11
11
|
async function default_1(context, options) {
|
|
12
|
+
if (context.backendConfigs.size === 0) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
12
15
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
13
16
|
options.projectNumber = await (0, getProjectNumber_1.getProjectNumber)(options);
|
|
14
17
|
if (!context.backendConfigs) {
|
|
@@ -30,6 +30,7 @@ async function default_1(context, options) {
|
|
|
30
30
|
const foundBackends = [];
|
|
31
31
|
const notFoundBackends = [];
|
|
32
32
|
const ambiguousBackends = [];
|
|
33
|
+
const skippedBackends = [];
|
|
33
34
|
for (const cfg of configs) {
|
|
34
35
|
const filteredBackends = backends.filter((backend) => (0, apphosting_1.parseBackendName)(backend.name).id === cfg.backendId);
|
|
35
36
|
if (filteredBackends.length === 0) {
|
|
@@ -54,6 +55,7 @@ async function default_1(context, options) {
|
|
|
54
55
|
for (const cfg of foundBackends) {
|
|
55
56
|
const filteredBackends = backends.filter((backend) => (0, apphosting_1.parseBackendName)(backend.name).id === cfg.backendId);
|
|
56
57
|
if (cfg.alwaysDeployFromSource === false) {
|
|
58
|
+
skippedBackends.push(cfg);
|
|
57
59
|
continue;
|
|
58
60
|
}
|
|
59
61
|
const backend = filteredBackends[0];
|
|
@@ -69,9 +71,9 @@ async function default_1(context, options) {
|
|
|
69
71
|
cfg.alwaysDeployFromSource = confirmDeploy;
|
|
70
72
|
const configPath = path.join(options.projectRoot || "", "firebase.json");
|
|
71
73
|
options.config.writeProjectFile(configPath, options.config.src);
|
|
72
|
-
(0, utils_1.logLabeledBullet)("apphosting", `
|
|
74
|
+
(0, utils_1.logLabeledBullet)("apphosting", `On future invocations of "firebase deploy", your local source will ${!confirmDeploy ? "not " : ""}be deployed to ${cfg.backendId}. You can edit this setting in your firebase.json at any time.`);
|
|
73
75
|
if (!confirmDeploy) {
|
|
74
|
-
|
|
76
|
+
skippedBackends.push(cfg);
|
|
75
77
|
continue;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
@@ -79,33 +81,37 @@ async function default_1(context, options) {
|
|
|
79
81
|
context.backendConfigs.set(cfg.backendId, cfg);
|
|
80
82
|
context.backendLocations.set(cfg.backendId, location);
|
|
81
83
|
}
|
|
82
|
-
if (notFoundBackends.length
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
if (notFoundBackends.length > 0) {
|
|
85
|
+
if (options.force) {
|
|
86
|
+
(0, utils_1.logLabeledWarning)("apphosting", `Skipping deployments of backend(s) ${notFoundBackends.map((cfg) => cfg.backendId).join(", ")}; ` +
|
|
87
|
+
"the backend(s) do not exist yet and we cannot create them for you because you must choose primary regions for each one. " +
|
|
88
|
+
"Please run 'firebase deploy' without the --force flag, or 'firebase apphosting:backends:create' to create the backend, " +
|
|
89
|
+
"then retry deployment.");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const confirmCreate = await (0, prompt_1.confirm)({
|
|
93
|
+
default: true,
|
|
94
|
+
message: `Did not find backend(s) ${notFoundBackends.map((cfg) => cfg.backendId).join(", ")}. Do you want to create them (you'll have the option to select which to create in the next step)?`,
|
|
95
|
+
});
|
|
96
|
+
if (confirmCreate) {
|
|
97
|
+
const selected = await (0, prompt_1.checkbox)({
|
|
98
|
+
message: "Which backends do you want to create and deploy to?",
|
|
99
|
+
choices: notFoundBackends.map((cfg) => cfg.backendId),
|
|
100
|
+
});
|
|
101
|
+
const selectedBackends = selected.map((id) => notFoundBackends.find((backend) => backend.backendId === id));
|
|
102
|
+
for (const cfg of selectedBackends) {
|
|
103
|
+
(0, utils_1.logLabeledBullet)("apphosting", `Creating a new backend ${cfg.backendId}...`);
|
|
104
|
+
const { location } = await (0, backend_1.doSetupSourceDeploy)(projectId, cfg.backendId);
|
|
105
|
+
context.backendConfigs.set(cfg.backendId, cfg);
|
|
106
|
+
context.backendLocations.set(cfg.backendId, location);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
skippedBackends.push(...notFoundBackends);
|
|
111
|
+
}
|
|
98
112
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
choices: notFoundBackends.map((cfg) => cfg.backendId),
|
|
102
|
-
});
|
|
103
|
-
const selectedBackends = selected.map((id) => notFoundBackends.find((backend) => backend.backendId === id));
|
|
104
|
-
for (const cfg of selectedBackends) {
|
|
105
|
-
(0, utils_1.logLabeledBullet)("apphosting", `Creating a new backend ${cfg.backendId}...`);
|
|
106
|
-
const { location } = await (0, backend_1.doSetupSourceDeploy)(projectId, cfg.backendId);
|
|
107
|
-
context.backendConfigs.set(cfg.backendId, cfg);
|
|
108
|
-
context.backendLocations.set(cfg.backendId, location);
|
|
113
|
+
if (skippedBackends.length > 0) {
|
|
114
|
+
(0, utils_1.logLabeledWarning)("apphosting", `Skipping deployment of backend(s) ${skippedBackends.map((cfg) => cfg.backendId).join(", ")}.`);
|
|
109
115
|
}
|
|
110
116
|
return;
|
|
111
117
|
}
|
|
@@ -7,6 +7,9 @@ const rollout_1 = require("../../apphosting/rollout");
|
|
|
7
7
|
const projectUtils_1 = require("../../projectUtils");
|
|
8
8
|
const utils_1 = require("../../utils");
|
|
9
9
|
async function default_1(context, options) {
|
|
10
|
+
if (context.backendConfigs.size === 0) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
10
13
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
11
14
|
const rollouts = [];
|
|
12
15
|
const backendIds = [];
|
|
@@ -2,9 +2,42 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const clc = require("colorette");
|
|
4
4
|
const api_1 = require("../../firestore/api");
|
|
5
|
+
const types = require("../../firestore/api-types");
|
|
5
6
|
const logger_1 = require("../../logger");
|
|
6
7
|
const utils = require("../../utils");
|
|
7
8
|
const rulesDeploy_1 = require("../../rulesDeploy");
|
|
9
|
+
const utils_1 = require("../../utils");
|
|
10
|
+
const error_1 = require("../../error");
|
|
11
|
+
async function createDatabase(context, options) {
|
|
12
|
+
let firestoreCfg = options.config.data.firestore;
|
|
13
|
+
if (Array.isArray(firestoreCfg)) {
|
|
14
|
+
firestoreCfg = firestoreCfg[0];
|
|
15
|
+
}
|
|
16
|
+
if (!options.projectId) {
|
|
17
|
+
throw new error_1.FirebaseError("Project ID is required to create a Firestore database.");
|
|
18
|
+
}
|
|
19
|
+
if (!firestoreCfg || !firestoreCfg.database) {
|
|
20
|
+
throw new error_1.FirebaseError("Firestore database configuration is missing in firebase.json.");
|
|
21
|
+
}
|
|
22
|
+
const api = new api_1.FirestoreApi();
|
|
23
|
+
try {
|
|
24
|
+
await api.getDatabase(options.projectId, firestoreCfg.database);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
if (e.status === 404) {
|
|
28
|
+
utils.logLabeledBullet("firetore", `Creating the new Firestore database ${firestoreCfg.database}...`);
|
|
29
|
+
const createDatabaseReq = {
|
|
30
|
+
project: options.projectId,
|
|
31
|
+
databaseId: firestoreCfg.database,
|
|
32
|
+
locationId: firestoreCfg.location || "nam5",
|
|
33
|
+
type: types.DatabaseType.FIRESTORE_NATIVE,
|
|
34
|
+
deleteProtectionState: types.DatabaseDeleteProtectionState.DISABLED,
|
|
35
|
+
pointInTimeRecoveryEnablement: types.PointInTimeRecoveryEnablement.DISABLED,
|
|
36
|
+
};
|
|
37
|
+
await api.createDatabase(createDatabaseReq);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
8
41
|
async function deployRules(context) {
|
|
9
42
|
var _a;
|
|
10
43
|
const rulesDeploy = (_a = context === null || context === void 0 ? void 0 : context.firestore) === null || _a === void 0 ? void 0 : _a.rulesDeploy;
|
|
@@ -33,12 +66,22 @@ async function deployIndexes(context, options) {
|
|
|
33
66
|
return;
|
|
34
67
|
}
|
|
35
68
|
const fieldOverrides = indexesRawSpec.fieldOverrides || [];
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
69
|
+
try {
|
|
70
|
+
await firestoreIndexes.deploy(options, indexes, fieldOverrides, databaseId);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
if (err.status !== 404) {
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
await (0, utils_1.sleep)(1000);
|
|
77
|
+
await firestoreIndexes.deploy(options, indexes, fieldOverrides, databaseId);
|
|
78
|
+
}
|
|
79
|
+
utils.logSuccess(`${clc.bold(clc.green("firestore:"))} deployed indexes in ${clc.bold(indexesFileName)} successfully for ${databaseId} database`);
|
|
39
80
|
}));
|
|
40
81
|
}
|
|
41
82
|
async function default_1(context, options) {
|
|
42
|
-
await
|
|
83
|
+
await createDatabase(context, options);
|
|
84
|
+
await deployRules(context);
|
|
85
|
+
await deployIndexes(context, options);
|
|
43
86
|
}
|
|
44
87
|
exports.default = default_1;
|
|
@@ -135,7 +135,7 @@ async function tripFirebasePostinstall(rootDirectory, env) {
|
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
137
137
|
const npmLsResults = JSON.parse(npmLs.stdout.toString().trim());
|
|
138
|
-
const dependenciesToSearch = Object.values(npmLsResults.dependencies);
|
|
138
|
+
const dependenciesToSearch = Object.values(npmLsResults.dependencies || {});
|
|
139
139
|
const firebaseUtilPaths = [];
|
|
140
140
|
for (const dependency of dependenciesToSearch) {
|
|
141
141
|
if (dependency.name === "@firebase/util" &&
|
|
@@ -3,58 +3,79 @@
|
|
|
3
3
|
"version": "4.11.2",
|
|
4
4
|
"expectedSize": 34495935,
|
|
5
5
|
"expectedChecksum": "2fd771101c0e1f7898c04c9204f2ce63",
|
|
6
|
-
"expectedChecksumSHA256": "b70d99344caf17c98b6f910fa8f6edf32a7c016cb1035e8915f70d38901eb97f"
|
|
6
|
+
"expectedChecksumSHA256": "b70d99344caf17c98b6f910fa8f6edf32a7c016cb1035e8915f70d38901eb97f",
|
|
7
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/firebase-database-emulator-v4.11.2.jar",
|
|
8
|
+
"downloadPathRelativeToCacheDir": "firebase-database-emulator-v4.11.2.jar"
|
|
7
9
|
},
|
|
8
10
|
"firestore": {
|
|
9
11
|
"version": "1.19.8",
|
|
10
12
|
"expectedSize": 63634791,
|
|
11
13
|
"expectedChecksum": "9b43a6daa590678de9b7df6d68260395",
|
|
12
|
-
"expectedChecksumSHA256": "9d43599ed6151199e8d604dc87fac51218e49e5f3a48519b1ae560bbe5e3382d"
|
|
14
|
+
"expectedChecksumSHA256": "9d43599ed6151199e8d604dc87fac51218e49e5f3a48519b1ae560bbe5e3382d",
|
|
15
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.19.8.jar",
|
|
16
|
+
"downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.19.8.jar"
|
|
13
17
|
},
|
|
14
18
|
"storage": {
|
|
15
19
|
"version": "1.1.3",
|
|
16
20
|
"expectedSize": 52892936,
|
|
17
21
|
"expectedChecksum": "2ca11ec1193003bea89f806cc085fa25",
|
|
18
|
-
"expectedChecksumSHA256": "0cd52db6f6271d62078f805220706377c849220b73bd68aa27078d977df9c900"
|
|
22
|
+
"expectedChecksumSHA256": "0cd52db6f6271d62078f805220706377c849220b73bd68aa27078d977df9c900",
|
|
23
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v1.1.3.jar",
|
|
24
|
+
"downloadPathRelativeToCacheDir": "cloud-storage-rules-runtime-v1.1.3.jar"
|
|
19
25
|
},
|
|
20
26
|
"ui": {
|
|
21
27
|
"snapshot": {
|
|
22
28
|
"version": "SNAPSHOT",
|
|
23
29
|
"expectedSize": -1,
|
|
24
30
|
"expectedChecksum": "",
|
|
25
|
-
"expectedChecksumSHA256": ""
|
|
31
|
+
"expectedChecksumSHA256": "",
|
|
32
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-vSNAPSHOT.zip",
|
|
33
|
+
"downloadPathRelativeToCacheDir": "ui-vSNAPSHOT.zip",
|
|
34
|
+
"binaryPathRelativeToCacheDir": "ui-vSNAPSHOT/server/server.mjs"
|
|
26
35
|
},
|
|
27
36
|
"main": {
|
|
28
37
|
"version": "1.15.0",
|
|
29
38
|
"expectedSize": 3538469,
|
|
30
39
|
"expectedChecksum": "2f13d5aea9524564c8b7adaa9cfa2128",
|
|
31
|
-
"expectedChecksumSHA256": "97d8c4c574e3f20c4d690a2ce8373eef76ab024da73279a062dba8517f88cf9a"
|
|
40
|
+
"expectedChecksumSHA256": "97d8c4c574e3f20c4d690a2ce8373eef76ab024da73279a062dba8517f88cf9a",
|
|
41
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.15.0.zip",
|
|
42
|
+
"downloadPathRelativeToCacheDir": "ui-v1.15.0.zip",
|
|
43
|
+
"binaryPathRelativeToCacheDir": "ui-v1.15.0/server/server.mjs"
|
|
32
44
|
}
|
|
33
45
|
},
|
|
34
46
|
"pubsub": {
|
|
35
47
|
"version": "0.8.14",
|
|
36
48
|
"expectedSize": 66786933,
|
|
37
49
|
"expectedChecksum": "a9025b3e53fdeafd2969ccb3ba1e1d38",
|
|
38
|
-
"expectedChecksumSHA256": "4fbeefd8ecb32b10e5ab522e5d8748e0c2b13891c471c0c327c04632dcc75a8d"
|
|
50
|
+
"expectedChecksumSHA256": "4fbeefd8ecb32b10e5ab522e5d8748e0c2b13891c471c0c327c04632dcc75a8d",
|
|
51
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.14.zip",
|
|
52
|
+
"downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.14.zip",
|
|
53
|
+
"binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.14/pubsub-emulator/bin/cloud-pubsub-emulator"
|
|
39
54
|
},
|
|
40
55
|
"dataconnect": {
|
|
41
56
|
"darwin": {
|
|
42
|
-
"version": "2.6.
|
|
43
|
-
"expectedSize":
|
|
44
|
-
"expectedChecksum": "
|
|
45
|
-
"expectedChecksumSHA256": "
|
|
57
|
+
"version": "2.6.2",
|
|
58
|
+
"expectedSize": 27501312,
|
|
59
|
+
"expectedChecksum": "f4adceec52a29bbc8eabf39dc07460a8",
|
|
60
|
+
"expectedChecksumSHA256": "038b1a763d2afd487423b37841323554e13bb6973c69a35f993c315819506ff8",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.6.2",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.2"
|
|
46
63
|
},
|
|
47
64
|
"win32": {
|
|
48
|
-
"version": "2.6.
|
|
49
|
-
"expectedSize":
|
|
50
|
-
"expectedChecksum": "
|
|
51
|
-
"expectedChecksumSHA256": "
|
|
65
|
+
"version": "2.6.2",
|
|
66
|
+
"expectedSize": 27961856,
|
|
67
|
+
"expectedChecksum": "36c75d09d9def62891be1ba84424cc5d",
|
|
68
|
+
"expectedChecksumSHA256": "eb33efdf0374bfe0772f3adff7b2ab7eb3353f5e03e2a781623d10f61a92444e",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.6.2",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.2.exe"
|
|
52
71
|
},
|
|
53
72
|
"linux": {
|
|
54
|
-
"version": "2.6.
|
|
55
|
-
"expectedSize":
|
|
56
|
-
"expectedChecksum": "
|
|
57
|
-
"expectedChecksumSHA256": "
|
|
73
|
+
"version": "2.6.2",
|
|
74
|
+
"expectedSize": 27414680,
|
|
75
|
+
"expectedChecksum": "deb1bad4fbccf4e3bbc437f8e2b34536",
|
|
76
|
+
"expectedChecksumSHA256": "549633c0e3ab26621da197b202e5712163c4be2d1f5d1e6c81bb33f20ccd1d7a",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.6.2",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.2"
|
|
58
79
|
}
|
|
59
80
|
}
|
|
60
81
|
}
|
|
@@ -30,83 +30,39 @@ const dataconnectDetails = process.platform === "darwin"
|
|
|
30
30
|
: EMULATOR_UPDATE_DETAILS.dataconnect.linux;
|
|
31
31
|
exports.DownloadDetails = {
|
|
32
32
|
database: {
|
|
33
|
-
downloadPath: path.join(CACHE_DIR,
|
|
33
|
+
downloadPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.database.downloadPathRelativeToCacheDir),
|
|
34
34
|
version: EMULATOR_UPDATE_DETAILS.database.version,
|
|
35
|
-
opts: {
|
|
36
|
-
cacheDir: CACHE_DIR,
|
|
37
|
-
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/firebase-database-emulator-v${EMULATOR_UPDATE_DETAILS.database.version}.jar`,
|
|
38
|
-
expectedSize: EMULATOR_UPDATE_DETAILS.database.expectedSize,
|
|
39
|
-
expectedChecksum: EMULATOR_UPDATE_DETAILS.database.expectedChecksum,
|
|
40
|
-
namePrefix: "firebase-database-emulator",
|
|
41
|
-
},
|
|
35
|
+
opts: Object.assign(Object.assign({}, EMULATOR_UPDATE_DETAILS.database), { cacheDir: CACHE_DIR, namePrefix: "firebase-database-emulator" }),
|
|
42
36
|
},
|
|
43
37
|
firestore: {
|
|
44
|
-
downloadPath: path.join(CACHE_DIR,
|
|
38
|
+
downloadPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.firestore.downloadPathRelativeToCacheDir),
|
|
45
39
|
version: EMULATOR_UPDATE_DETAILS.firestore.version,
|
|
46
|
-
opts: {
|
|
47
|
-
cacheDir: CACHE_DIR,
|
|
48
|
-
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v${EMULATOR_UPDATE_DETAILS.firestore.version}.jar`,
|
|
49
|
-
expectedSize: EMULATOR_UPDATE_DETAILS.firestore.expectedSize,
|
|
50
|
-
expectedChecksum: EMULATOR_UPDATE_DETAILS.firestore.expectedChecksum,
|
|
51
|
-
namePrefix: "cloud-firestore-emulator",
|
|
52
|
-
},
|
|
40
|
+
opts: Object.assign(Object.assign({}, EMULATOR_UPDATE_DETAILS.firestore), { cacheDir: CACHE_DIR, namePrefix: "cloud-firestore-emulator" }),
|
|
53
41
|
},
|
|
54
42
|
storage: {
|
|
55
|
-
downloadPath: path.join(CACHE_DIR,
|
|
43
|
+
downloadPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.storage.downloadPathRelativeToCacheDir),
|
|
56
44
|
version: EMULATOR_UPDATE_DETAILS.storage.version,
|
|
57
|
-
opts: {
|
|
58
|
-
cacheDir: CACHE_DIR,
|
|
59
|
-
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v${EMULATOR_UPDATE_DETAILS.storage.version}.jar`,
|
|
60
|
-
expectedSize: EMULATOR_UPDATE_DETAILS.storage.expectedSize,
|
|
61
|
-
expectedChecksum: EMULATOR_UPDATE_DETAILS.storage.expectedChecksum,
|
|
62
|
-
namePrefix: "cloud-storage-rules-emulator",
|
|
63
|
-
},
|
|
45
|
+
opts: Object.assign(Object.assign({}, EMULATOR_UPDATE_DETAILS.storage), { cacheDir: CACHE_DIR, namePrefix: "cloud-storage-rules-emulator" }),
|
|
64
46
|
},
|
|
65
47
|
ui: {
|
|
66
48
|
version: emulatorUiDetails.version,
|
|
67
|
-
downloadPath: path.join(CACHE_DIR,
|
|
49
|
+
downloadPath: path.join(CACHE_DIR, emulatorUiDetails.downloadPathRelativeToCacheDir),
|
|
68
50
|
unzipDir: path.join(CACHE_DIR, `ui-v${emulatorUiDetails.version}`),
|
|
69
|
-
binaryPath: path.join(CACHE_DIR,
|
|
70
|
-
opts: {
|
|
71
|
-
cacheDir: CACHE_DIR,
|
|
72
|
-
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v${emulatorUiDetails.version}.zip`,
|
|
73
|
-
expectedSize: emulatorUiDetails.expectedSize,
|
|
74
|
-
expectedChecksum: emulatorUiDetails.expectedChecksum,
|
|
75
|
-
skipCache: experiments.isEnabled("emulatoruisnapshot"),
|
|
76
|
-
skipChecksumAndSize: experiments.isEnabled("emulatoruisnapshot"),
|
|
77
|
-
namePrefix: "ui",
|
|
78
|
-
},
|
|
51
|
+
binaryPath: path.join(CACHE_DIR, emulatorUiDetails.binaryPathRelativeToCacheDir),
|
|
52
|
+
opts: Object.assign(Object.assign({}, emulatorUiDetails), { cacheDir: CACHE_DIR, skipCache: experiments.isEnabled("emulatoruisnapshot"), skipChecksumAndSize: experiments.isEnabled("emulatoruisnapshot"), namePrefix: "ui" }),
|
|
79
53
|
},
|
|
80
54
|
pubsub: {
|
|
81
|
-
downloadPath: path.join(CACHE_DIR,
|
|
55
|
+
downloadPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.pubsub.downloadPathRelativeToCacheDir),
|
|
82
56
|
version: EMULATOR_UPDATE_DETAILS.pubsub.version,
|
|
83
57
|
unzipDir: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`),
|
|
84
|
-
binaryPath: path.join(CACHE_DIR,
|
|
85
|
-
opts: {
|
|
86
|
-
cacheDir: CACHE_DIR,
|
|
87
|
-
remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}.zip`,
|
|
88
|
-
expectedSize: EMULATOR_UPDATE_DETAILS.pubsub.expectedSize,
|
|
89
|
-
expectedChecksum: EMULATOR_UPDATE_DETAILS.pubsub.expectedChecksum,
|
|
90
|
-
namePrefix: "pubsub-emulator",
|
|
91
|
-
},
|
|
58
|
+
binaryPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.pubsub.binaryPathRelativeToCacheDir),
|
|
59
|
+
opts: Object.assign(Object.assign({}, EMULATOR_UPDATE_DETAILS.pubsub), { cacheDir: CACHE_DIR, namePrefix: "pubsub-emulator" }),
|
|
92
60
|
},
|
|
93
61
|
dataconnect: {
|
|
94
|
-
downloadPath: path.join(CACHE_DIR,
|
|
62
|
+
downloadPath: path.join(CACHE_DIR, dataconnectDetails.downloadPathRelativeToCacheDir),
|
|
95
63
|
version: dataconnectDetails.version,
|
|
96
|
-
binaryPath: path.join(CACHE_DIR,
|
|
97
|
-
opts: {
|
|
98
|
-
cacheDir: CACHE_DIR,
|
|
99
|
-
remoteUrl: process.platform === "darwin"
|
|
100
|
-
? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v${dataconnectDetails.version}`
|
|
101
|
-
: process.platform === "win32"
|
|
102
|
-
? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v${dataconnectDetails.version}`
|
|
103
|
-
: `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v${dataconnectDetails.version}`,
|
|
104
|
-
expectedSize: dataconnectDetails.expectedSize,
|
|
105
|
-
expectedChecksum: dataconnectDetails.expectedChecksum,
|
|
106
|
-
skipChecksumAndSize: false,
|
|
107
|
-
namePrefix: "dataconnect-emulator",
|
|
108
|
-
auth: false,
|
|
109
|
-
},
|
|
64
|
+
binaryPath: path.join(CACHE_DIR, dataconnectDetails.downloadPathRelativeToCacheDir),
|
|
65
|
+
opts: Object.assign(Object.assign({}, dataconnectDetails), { cacheDir: CACHE_DIR, skipChecksumAndSize: false, namePrefix: "dataconnect-emulator", auth: false }),
|
|
110
66
|
},
|
|
111
67
|
};
|
|
112
68
|
const EmulatorDetails = {
|