Skip to content

Commit aba2118

Browse files
authored
Split confirmWriteProjectFile out of askWriteProjectFile (firebase#8648)
* split confirmWriteProjectFile out of askWriteProjectFile
1 parent 0233555 commit aba2118

File tree

8 files changed

+90
-101
lines changed

8 files changed

+90
-101
lines changed

src/commands/init.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,9 @@ export async function initAction(feature: string, options: Options): Promise<voi
230230
await init(setup, config, options);
231231

232232
logger.info();
233-
utils.logBullet("Writing configuration info to " + clc.bold("firebase.json") + "...");
234233
config.writeProjectFile("firebase.json", setup.config);
235-
utils.logBullet("Writing project information to " + clc.bold(".firebaserc") + "...");
236234
config.writeProjectFile(".firebaserc", setup.rcfile);
237235
if (!fsutils.fileExistsSync(config.path(".gitignore"))) {
238-
utils.logBullet("Writing gitignore file to " + clc.bold(".gitignore") + "...");
239236
config.writeProjectFile(".gitignore", GITIGNORE_TEMPLATE);
240237
}
241238
logger.info();

src/config.ts

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,21 @@ export class Config {
216216
}
217217
}
218218

219-
writeProjectFile(p: string, content: any) {
220-
if (typeof content !== "string") {
221-
content = JSON.stringify(content, null, 2) + "\n";
219+
writeProjectFile(p: string, content: any): void {
220+
const path = this.path(p);
221+
fs.ensureFileSync(path);
222+
fs.writeFileSync(path, stringifyContent(content), "utf8");
223+
switch (p) {
224+
case "firebase.json":
225+
utils.logSuccess("Wrote configuration info to " + clc.bold("firebase.json"));
226+
break;
227+
case ".firebaserc":
228+
utils.logSuccess("Wrote project information to " + clc.bold(".firebaserc"));
229+
break;
230+
default:
231+
utils.logSuccess("Wrote " + clc.bold(p));
232+
break;
222233
}
223-
224-
fs.ensureFileSync(this.path(p));
225-
fs.writeFileSync(this.path(p), content, "utf8");
226234
}
227235

228236
projectFileExists(p: string): boolean {
@@ -233,36 +241,45 @@ export class Config {
233241
fs.removeSync(this.path(p));
234242
}
235243

236-
async askWriteProjectFile(
244+
async confirmWriteProjectFile(
237245
path: string,
238246
content: any,
239-
force?: boolean,
240247
confirmByDefault?: boolean,
241-
): Promise<void> {
248+
): Promise<boolean> {
242249
const writeTo = this.path(path);
243-
let next = true;
244-
if (typeof content !== "string") {
245-
content = JSON.stringify(content, null, 2) + "\n";
250+
if (!fsutils.fileExistsSync(writeTo)) {
251+
return true;
246252
}
247-
let existingContent: string | undefined;
248-
if (fsutils.fileExistsSync(writeTo)) {
249-
existingContent = fsutils.readFile(writeTo);
253+
const existingContent = fsutils.readFile(writeTo);
254+
const newContent = stringifyContent(content);
255+
if (existingContent === newContent) {
256+
utils.logBullet(clc.bold(path) + " is unchanged");
257+
return false;
250258
}
251-
if (existingContent && existingContent !== content && !force) {
252-
next = await confirm({
253-
message: "File " + clc.underline(path) + " already exists. Overwrite?",
254-
default: !!confirmByDefault,
255-
});
259+
const shouldWrite = await confirm({
260+
message: "File " + clc.underline(path) + " already exists. Overwrite?",
261+
default: !!confirmByDefault,
262+
});
263+
if (!shouldWrite) {
264+
utils.logBullet("Skipping write of " + clc.bold(path));
265+
return false;
256266
}
267+
return true;
268+
}
257269

258-
if (existingContent === content) {
259-
utils.logBullet(clc.bold(path) + " is unchanged");
260-
} else if (next) {
261-
this.writeProjectFile(path, content);
262-
utils.logSuccess("Wrote " + clc.bold(path));
263-
} else {
264-
utils.logBullet("Skipping write of " + clc.bold(path));
270+
async askWriteProjectFile(
271+
path: string,
272+
content: any,
273+
force?: boolean,
274+
confirmByDefault?: boolean,
275+
): Promise<void> {
276+
if (!force) {
277+
const shouldWrite = await this.confirmWriteProjectFile(path, content, confirmByDefault);
278+
if (!shouldWrite) {
279+
return;
280+
}
265281
}
282+
this.writeProjectFile(path, content);
266283
}
267284

268285
public static load(options: any, allowMissing?: boolean): Config | null {
@@ -304,3 +321,10 @@ export class Config {
304321
});
305322
}
306323
}
324+
325+
function stringifyContent(content: any): string {
326+
if (typeof content === "string") {
327+
return content;
328+
}
329+
return JSON.stringify(content, null, 2) + "\n";
330+
}

src/deploy/apphosting/prepare.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export default async function (context: Context, options: Options): Promise<void
106106
options.config.writeProjectFile(configPath, options.config.src);
107107
logLabeledBullet(
108108
"apphosting",
109-
`Your deployment preferences have been saved to firebase.json. On future invocations of "firebase deploy", your local source will be deployed to ${cfg.backendId}. You can edit this setting in your firebase.json at any time.`,
109+
`On future invocations of "firebase deploy", your local source will be deployed to ${cfg.backendId}. You can edit this setting in your firebase.json at any time.`,
110110
);
111111
if (!confirmDeploy) {
112112
logLabeledWarning("apphosting", `Skipping deployment of backend ${cfg.backendId}`);

src/extensions/manifest.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as clc from "colorette";
21
import * as path from "path";
32
import * as fs from "fs-extra";
43

@@ -9,7 +8,6 @@ import { logger } from "../logger";
98
import { confirm, select } from "../prompt";
109
import { readEnvFile } from "./paramHelper";
1110
import { FirebaseError } from "../error";
12-
import * as utils from "../utils";
1311
import { isLocalPath } from "./extensionsHelper";
1412
import { ParamType } from "./types";
1513

@@ -217,7 +215,6 @@ export function writeExtensionsToFirebaseJson(specs: ManifestInstanceSpec[], con
217215
}
218216
config.set("extensions", extensions);
219217
config.writeProjectFile("firebase.json", config.src);
220-
utils.logSuccess("Wrote extensions to " + clc.bold("firebase.json") + "...");
221218
}
222219

223220
async function writeEnvFiles(

src/init/features/apphosting.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ export async function doSetup(setup: Setup, config: Config): Promise<void> {
117117
});
118118

119119
upsertAppHostingConfig(backendConfig, config);
120-
utils.logBullet("Writing configuration info to firebase.json...");
121120
config.writeProjectFile("firebase.json", config.src);
122121

123122
utils.logBullet("Writing default settings to " + clc.bold("apphosting.yaml") + "...");

src/init/features/database.ts

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as clc from "colorette";
22
import { confirm, input, select } from "../../prompt";
33
import { logger } from "../../logger";
44
import * as utils from "../../utils";
5-
import * as fsutils from "../../fsutils";
65
import { Config } from "../../config";
76
import {
87
createInstance,
@@ -51,18 +50,10 @@ async function getDBRules(instanceDetails: DatabaseInstance): Promise<string> {
5150
return await response.response.text();
5251
}
5352

54-
function writeDBRules(
55-
rules: string,
56-
logMessagePrefix: string,
57-
filename: string,
58-
config: Config,
59-
): void {
53+
function writeDBRules(rules: string, filename: string, config: Config): void {
6054
config.writeProjectFile(filename, rules);
61-
utils.logSuccess(`${logMessagePrefix} have been written to ${clc.bold(filename)}.`);
6255
logger.info(
63-
`Future modifications to ${clc.bold(
64-
filename,
65-
)} will update Realtime Database Security Rules when you run`,
56+
`Future modifications to ${clc.bold(filename)} will update Realtime Database Security Rules when you run`,
6657
);
6758
logger.info(clc.bold("firebase deploy") + ".");
6859
}
@@ -142,12 +133,7 @@ async function initializeDatabaseInstance(projectId: string): Promise<DatabaseIn
142133
* @param setup information helpful for database setup
143134
* @param config legacy config parameter. not used for database setup.
144135
*/
145-
export async function askQuestions(setup: Setup): Promise<void> {
146-
let instanceDetails: DatabaseInstance | null = null;
147-
if (setup.projectId) {
148-
instanceDetails = await initializeDatabaseInstance(setup.projectId);
149-
}
150-
136+
export async function askQuestions(setup: Setup, config: Config): Promise<void> {
151137
logger.info();
152138
logger.info(
153139
"Firebase Realtime Database Security Rules allow you to define how your data should be",
@@ -167,20 +153,16 @@ export async function askQuestions(setup: Setup): Promise<void> {
167153
rules: DEFAULT_RULES,
168154
writeRules: true,
169155
};
170-
if (fsutils.fileExistsSync(rulesFilename)) {
171-
const rulesDescription = instanceDetails
172-
? `the Realtime Database Security Rules for ${clc.bold(instanceDetails.name)} from the Firebase console`
173-
: "default rules";
174-
const msg = `File ${clc.bold(
175-
rulesFilename,
176-
)} already exists. Do you want to overwrite it with ${rulesDescription}?`;
177-
178-
info.writeRules = await confirm(msg);
179-
}
180-
if (info.writeRules && instanceDetails) {
181-
info.rules = await getDBRules(instanceDetails);
156+
if (setup.projectId) {
157+
const instanceDetails = await initializeDatabaseInstance(setup.projectId);
158+
if (instanceDetails) {
159+
info.rules = await getDBRules(instanceDetails);
160+
utils.logBullet(
161+
`Downloaded the existing Realtime Database Security Rules of database ${clc.bold(instanceDetails.name)} from the Firebase console`,
162+
);
163+
}
182164
}
183-
165+
info.writeRules = await config.confirmWriteProjectFile(rulesFilename, info.rules);
184166
// Populate featureInfo for the actuate step later.
185167
setup.featureInfo = setup.featureInfo || {};
186168
setup.featureInfo.database = info;
@@ -198,9 +180,9 @@ export async function actuate(setup: Setup, config: Config): Promise<void> {
198180

199181
if (info.writeRules) {
200182
if (info.rules === DEFAULT_RULES) {
201-
writeDBRules(info.rules, `Default rules for ${setup.projectId}`, info.rulesFilename, config);
183+
writeDBRules(info.rules, info.rulesFilename, config);
202184
} else {
203-
writeDBRules(info.rules, `Database Rules for ${setup.projectId}`, info.rulesFilename, config);
185+
writeDBRules(info.rules, info.rulesFilename, config);
204186
}
205187
} else {
206188
logger.info("Skipping overwrite of Realtime Database Security Rules.");

src/init/features/firestore/indexes.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import * as clc from "colorette";
22

33
import { FirebaseError } from "../../../error";
44
import * as api from "../../../firestore/api";
5-
import * as fsutils from "../../../fsutils";
6-
import { confirm, input } from "../../../prompt";
5+
import { input } from "../../../prompt";
6+
import * as utils from "../../../utils";
77
import { logger } from "../../../logger";
88
import { readTemplateSync } from "../../../templates";
99
import { RequiredInfo } from ".";
@@ -30,24 +30,22 @@ export async function initIndexes(setup: Setup, config: Config, info: RequiredIn
3030
default: DEFAULT_INDEXES_FILE,
3131
}));
3232

33-
if (fsutils.fileExistsSync(info.indexesFilename)) {
34-
const msg =
35-
"File " +
36-
clc.bold(info.indexesFilename) +
37-
" already exists." +
38-
" Do you want to overwrite it with the Firestore Indexes from the Firebase Console?";
39-
if (!(await confirm(msg))) {
40-
info.writeIndexes = false;
41-
return;
33+
info.indexes = INDEXES_TEMPLATE;
34+
if (setup.projectId) {
35+
const downloadIndexes = await getIndexesFromConsole(setup.projectId, info.databaseId);
36+
if (downloadIndexes) {
37+
info.indexes = downloadIndexes;
38+
utils.logBullet(`Downloaded the existing Firestore indexes from the Firebase console`);
4239
}
4340
}
4441

45-
if (setup.projectId) {
46-
info.indexes = await getIndexesFromConsole(setup.projectId, info.databaseId);
47-
}
42+
info.writeRules = await config.confirmWriteProjectFile(info.indexesFilename, info.indexes);
4843
}
4944

50-
async function getIndexesFromConsole(projectId: string, databaseId: string): Promise<string> {
45+
async function getIndexesFromConsole(
46+
projectId: string,
47+
databaseId: string,
48+
): Promise<string | null> {
5149
const indexesPromise = indexes.listIndexes(projectId, databaseId);
5250
const fieldOverridesPromise = indexes.listFieldOverrides(projectId, databaseId);
5351

@@ -56,7 +54,7 @@ async function getIndexesFromConsole(projectId: string, databaseId: string): Pro
5654
return JSON.stringify(indexes.makeIndexSpec(res[0], res[1]), null, 2);
5755
} catch (e: any) {
5856
if (e.message.indexOf("is not a Cloud Firestore enabled project") >= 0) {
59-
return INDEXES_TEMPLATE;
57+
return null;
6058
}
6159

6260
throw new FirebaseError("Error fetching Firestore indexes", {

src/init/features/firestore/rules.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import * as clc from "colorette";
22

33
import * as gcp from "../../../gcp";
4-
import * as fsutils from "../../../fsutils";
5-
import { confirm, input } from "../../../prompt";
4+
import { input } from "../../../prompt";
65
import { logger } from "../../../logger";
76
import * as utils from "../../../utils";
87
import { readTemplateSync } from "../../../templates";
@@ -34,31 +33,24 @@ export async function initRules(setup: Setup, config: Config, info: RequiredInfo
3433
default: DEFAULT_RULES_FILE,
3534
}));
3635

37-
if (fsutils.fileExistsSync(info.rulesFilename)) {
38-
const msg =
39-
"File " +
40-
clc.bold(info.rulesFilename) +
41-
" already exists." +
42-
" Do you want to overwrite it with the Firestore Rules from the Firebase Console?";
43-
if (!(await confirm(msg))) {
44-
info.writeRules = false;
45-
return;
36+
info.rules = getDefaultRules();
37+
if (setup.projectId) {
38+
const downloadedRules = await getRulesFromConsole(setup.projectId);
39+
if (downloadedRules) {
40+
info.rules = downloadedRules;
41+
utils.logBullet(`Downloaded the existing Firestore Security Rules from the Firebase console`);
4642
}
4743
}
4844

49-
if (setup.projectId) {
50-
info.rules = await getRulesFromConsole(setup.projectId);
51-
}
45+
info.writeRules = await config.confirmWriteProjectFile(info.rulesFilename, info.rules);
5246
}
5347

54-
async function getRulesFromConsole(projectId: string): Promise<string> {
48+
async function getRulesFromConsole(projectId: string): Promise<string | null> {
5549
const name = await gcp.rules.getLatestRulesetName(projectId, "cloud.firestore");
5650
if (!name) {
57-
logger.debug("No rulesets found, using default.");
58-
return getDefaultRules();
51+
return null;
5952
}
6053

61-
logger.debug("Found ruleset: " + name);
6254
const rules = await gcp.rules.getRulesetContent(name);
6355
if (rules.length <= 0) {
6456
return utils.reject("Ruleset has no files", { exit: 1 });

0 commit comments

Comments
 (0)