firebase-tools 13.30.0 → 13.31.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/secrets/dialogs.js +2 -2
- package/lib/commands/appdistribution-groups-list.js +1 -1
- package/lib/commands/appdistribution-testers-list.js +4 -4
- package/lib/commands/apphosting-backends-list.js +1 -1
- package/lib/commands/apphosting-secrets-describe.js +1 -1
- package/lib/commands/apps-android-sha-list.js +1 -1
- package/lib/commands/apps-create.js +1 -104
- package/lib/commands/apps-init.js +94 -0
- package/lib/commands/apps-list.js +1 -1
- package/lib/commands/apps-sdkconfig.js +3 -3
- package/lib/commands/database-instances-list.js +1 -1
- package/lib/commands/dataconnect-services-list.js +1 -1
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/experiments-list.js +1 -1
- package/lib/commands/ext-dev-list.js +1 -1
- package/lib/commands/ext-dev-usage.js +1 -1
- package/lib/commands/functions-list.js +1 -1
- package/lib/commands/hosting-channel-list.js +1 -1
- package/lib/commands/hosting-sites-get.js +1 -1
- package/lib/commands/hosting-sites-list.js +1 -1
- package/lib/commands/index.js +1 -0
- package/lib/commands/projects-list.js +1 -1
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/commands/remoteconfig-versions-list.js +1 -1
- package/lib/crashlytics/buildToolsJarHelper.js +1 -1
- package/lib/dataconnect/build.js +6 -0
- package/lib/dataconnect/client.js +1 -1
- package/lib/dataconnect/dataplaneClient.js +1 -1
- package/lib/dataconnect/fileUtils.js +25 -2
- package/lib/dataconnect/graphqlError.js +5 -3
- package/lib/emulator/commandUtils.js +1 -1
- package/lib/emulator/dataconnectEmulator.js +4 -1
- package/lib/emulator/dataconnectToolkitController.js +5 -0
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/emulator/extensionsEmulator.js +2 -2
- package/lib/experiments.js +6 -0
- package/lib/extensions/change-log.js +1 -25
- package/lib/extensions/listExtensions.js +1 -1
- package/lib/firestore/pretty-print.js +1 -1
- package/lib/frameworks/angular/index.js +11 -6
- package/lib/functions/secrets.js +1 -1
- package/lib/gcp/cloudsql/interactive.js +1 -1
- package/lib/init/features/dataconnect/sdk.js +28 -3
- package/lib/init/features/genkit/index.js +9 -7
- package/lib/management/apps.js +314 -7
- package/lib/profileReport.js +1 -1
- package/lib/prompt.js +10 -2
- package/package.json +14 -5
- package/templates/genkit/firebase.0.9.0.template +1 -1
- package/templates/genkit/firebase.1.0.0.template +1 -1
- package/templates/setup/web.js +0 -5
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DataConnectToolkitController = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
4
5
|
const error_1 = require("../error");
|
|
5
6
|
const portUtils = require("./portUtils");
|
|
6
7
|
const utils_1 = require("../utils");
|
|
7
8
|
const dataconnectEmulator_1 = require("./dataconnectEmulator");
|
|
9
|
+
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
8
10
|
const name = "Data Connect Toolkit";
|
|
9
11
|
class DataConnectToolkitController {
|
|
10
12
|
static async start(args) {
|
|
@@ -29,6 +31,9 @@ class DataConnectToolkitController {
|
|
|
29
31
|
throw new error_1.FirebaseError(`Data Connect Toolkit failed to stop with error: ${e}`);
|
|
30
32
|
}
|
|
31
33
|
}
|
|
34
|
+
static getVersion() {
|
|
35
|
+
return (0, downloadableEmulators_1.getDownloadDetails)(types_1.Emulators.DATACONNECT).version;
|
|
36
|
+
}
|
|
32
37
|
static getInfo() {
|
|
33
38
|
return this.instance.getInfo();
|
|
34
39
|
}
|
|
@@ -48,20 +48,20 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
48
48
|
},
|
|
49
49
|
dataconnect: process.platform === "darwin"
|
|
50
50
|
? {
|
|
51
|
-
version: "1.
|
|
52
|
-
expectedSize:
|
|
53
|
-
expectedChecksum: "
|
|
51
|
+
version: "1.8.0",
|
|
52
|
+
expectedSize: 25469696,
|
|
53
|
+
expectedChecksum: "d20a4240b4290a262caff3d0d3a85a4f",
|
|
54
54
|
}
|
|
55
55
|
: process.platform === "win32"
|
|
56
56
|
? {
|
|
57
|
-
version: "1.
|
|
58
|
-
expectedSize:
|
|
59
|
-
expectedChecksum: "
|
|
57
|
+
version: "1.8.0",
|
|
58
|
+
expectedSize: 25903616,
|
|
59
|
+
expectedChecksum: "dda0350259449493ecc5cc24187ce752",
|
|
60
60
|
}
|
|
61
61
|
: {
|
|
62
|
-
version: "1.
|
|
63
|
-
expectedSize:
|
|
64
|
-
expectedChecksum: "
|
|
62
|
+
version: "1.8.0",
|
|
63
|
+
expectedSize: 25383064,
|
|
64
|
+
expectedChecksum: "54a19e336f4118854c4c2d4eeeccb506",
|
|
65
65
|
},
|
|
66
66
|
};
|
|
67
67
|
exports.DownloadDetails = {
|
|
@@ -6,7 +6,7 @@ const spawn = require("cross-spawn");
|
|
|
6
6
|
const fs = require("fs-extra");
|
|
7
7
|
const os = require("os");
|
|
8
8
|
const path = require("path");
|
|
9
|
-
const Table = require("cli-
|
|
9
|
+
const Table = require("cli-table3");
|
|
10
10
|
const planner = require("../deploy/extensions/planner");
|
|
11
11
|
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
12
12
|
const error_1 = require("../error");
|
|
@@ -221,7 +221,7 @@ class ExtensionsEmulator {
|
|
|
221
221
|
const enablementUri = await (0, shortenUrl_1.shortenUrl)((0, ensureApiEnabled_1.enableApiURI)(this.args.projectId, apiToWarn.apiName));
|
|
222
222
|
table.push([
|
|
223
223
|
apiToWarn.apiName,
|
|
224
|
-
apiToWarn.instanceIds,
|
|
224
|
+
apiToWarn.instanceIds.join(", "),
|
|
225
225
|
apiToWarn.enabled ? "Yes" : "No",
|
|
226
226
|
apiToWarn.enabled ? "" : clc.bold(clc.underline(enablementUri)),
|
|
227
227
|
]);
|
package/lib/experiments.js
CHANGED
|
@@ -106,6 +106,12 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
106
106
|
default: true,
|
|
107
107
|
public: false,
|
|
108
108
|
},
|
|
109
|
+
appsinit: {
|
|
110
|
+
shortDescription: "Adds experimental `apps:init` command.",
|
|
111
|
+
fullDescription: "Adds experimental `apps:init` command. When run from an app directory, this command detects the app's platform and configures required files.",
|
|
112
|
+
default: false,
|
|
113
|
+
public: true,
|
|
114
|
+
},
|
|
109
115
|
fdcconnectorevolution: {
|
|
110
116
|
shortDescription: "Enable Data Connect connector evolution warnings.",
|
|
111
117
|
fullDescription: "Enable Data Connect connector evolution warnings.",
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseChangelog = exports.getLocalChangelog = exports.breakingChangesInUpdate = exports.
|
|
4
|
-
const clc = require("colorette");
|
|
3
|
+
exports.parseChangelog = exports.getLocalChangelog = exports.breakingChangesInUpdate = exports.getReleaseNotesForUpdate = void 0;
|
|
5
4
|
const marked_1 = require("marked");
|
|
6
5
|
const path = require("path");
|
|
7
6
|
const semver = require("semver");
|
|
8
7
|
const marked_terminal_1 = require("marked-terminal");
|
|
9
|
-
const Table = require("cli-table");
|
|
10
8
|
const extensionsApi_1 = require("./extensionsApi");
|
|
11
9
|
const localHelper_1 = require("./localHelper");
|
|
12
|
-
const logger_1 = require("../logger");
|
|
13
10
|
const refs = require("./refs");
|
|
14
|
-
const utils_1 = require("../utils");
|
|
15
11
|
marked_1.marked.use((0, marked_terminal_1.markedTerminal)());
|
|
16
12
|
const EXTENSIONS_CHANGELOG = "CHANGELOG.md";
|
|
17
13
|
const VERSION_LINE_REGEX = /##.+?(\d+\.\d+\.\d+(?:-((\d+|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(\d+|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?).*/;
|
|
@@ -31,26 +27,6 @@ async function getReleaseNotesForUpdate(args) {
|
|
|
31
27
|
return releaseNotes;
|
|
32
28
|
}
|
|
33
29
|
exports.getReleaseNotesForUpdate = getReleaseNotesForUpdate;
|
|
34
|
-
function displayReleaseNotes(releaseNotes, fromVersion) {
|
|
35
|
-
const versions = [fromVersion].concat(Object.keys(releaseNotes));
|
|
36
|
-
const breakingVersions = breakingChangesInUpdate(versions);
|
|
37
|
-
const table = new Table({ head: ["Version", "What's New"], style: { head: ["yellow", "bold"] } });
|
|
38
|
-
for (const [version, note] of Object.entries(releaseNotes)) {
|
|
39
|
-
if (breakingVersions.includes(version)) {
|
|
40
|
-
table.push([clc.yellow(clc.bold(version)), (0, marked_1.marked)(note)]);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
table.push([version, (0, marked_1.marked)(note)]);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
logger_1.logger.info(clc.bold("What's new with this update:"));
|
|
47
|
-
if (breakingVersions.length) {
|
|
48
|
-
(0, utils_1.logLabeledWarning)("warning", "This is a major version update, which means it may contain breaking changes." +
|
|
49
|
-
" Read the release notes carefully before continuing with this update.");
|
|
50
|
-
}
|
|
51
|
-
logger_1.logger.info(table.toString());
|
|
52
|
-
}
|
|
53
|
-
exports.displayReleaseNotes = displayReleaseNotes;
|
|
54
30
|
function breakingChangesInUpdate(versionsInUpdate) {
|
|
55
31
|
const breakingVersions = [];
|
|
56
32
|
const semvers = versionsInUpdate.map((v) => semver.parse(v)).sort(semver.compare);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.listExtensions = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
|
-
const Table = require("cli-
|
|
5
|
+
const Table = require("cli-table3");
|
|
6
6
|
const extensionsApi_1 = require("./extensionsApi");
|
|
7
7
|
const logger_1 = require("../logger");
|
|
8
8
|
const utils_1 = require("../utils");
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PrettyPrint = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
|
-
const Table = require("cli-
|
|
5
|
+
const Table = require("cli-table3");
|
|
6
6
|
const sort = require("./api-sort");
|
|
7
7
|
const types = require("./api-types");
|
|
8
8
|
const logger_1 = require("../logger");
|
|
@@ -15,7 +15,7 @@ exports.support = "preview";
|
|
|
15
15
|
exports.type = 3;
|
|
16
16
|
exports.docsUrl = "https://firebase.google.com/docs/hosting/frameworks/angular";
|
|
17
17
|
const DEFAULT_BUILD_SCRIPT = ["ng build"];
|
|
18
|
-
exports.supportedRange = "16 -
|
|
18
|
+
exports.supportedRange = "16 - 19";
|
|
19
19
|
async function discover(dir) {
|
|
20
20
|
if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "package.json"))))
|
|
21
21
|
return;
|
|
@@ -182,17 +182,22 @@ exports.handle = function(req,res) {
|
|
|
182
182
|
}
|
|
183
183
|
else if (serverOutputPath) {
|
|
184
184
|
bootstrapScript = `
|
|
185
|
-
const app = new Promise((resolve) => {
|
|
185
|
+
const app = new Promise((resolve, reject) => {
|
|
186
186
|
setTimeout(() => {
|
|
187
187
|
const port = process.env.PORT;
|
|
188
188
|
const socket = 'express.sock';
|
|
189
189
|
process.env.PORT = socket;
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
${(serverEntry === null || serverEntry === void 0 ? void 0 : serverEntry.endsWith(".mjs"))
|
|
192
192
|
? `import(\`./${serverOutputPath}/${serverEntry}\`)`
|
|
193
|
-
: `Promise.resolve(require('./${serverOutputPath}/${serverEntry}'))`}.then(({ app }) => {
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
: `Promise.resolve(require('./${serverOutputPath}/${serverEntry}'))`}.then(({ default: defHandler, reqHandler, app }) => {
|
|
194
|
+
const handler = app?.() ?? reqHandler ?? defHandler;
|
|
195
|
+
if (!handler) {
|
|
196
|
+
reject(\`The file at "./${serverOutputPath}/${serverEntry}" did not export a valid request handler. Expected exports: 'app', 'default', or 'reqHandler'.\`);
|
|
197
|
+
} else {
|
|
198
|
+
process.env.PORT = port;
|
|
199
|
+
resolve(handler);
|
|
200
|
+
}
|
|
196
201
|
});
|
|
197
202
|
}, 0);
|
|
198
203
|
});
|
package/lib/functions/secrets.js
CHANGED
|
@@ -16,7 +16,7 @@ const functional_1 = require("../functional");
|
|
|
16
16
|
const secretManager_2 = require("../gcp/secretManager");
|
|
17
17
|
const secretManager_3 = require("../gcp/secretManager");
|
|
18
18
|
const projectUtils_1 = require("../projectUtils");
|
|
19
|
-
const Table = require("cli-
|
|
19
|
+
const Table = require("cli-table3");
|
|
20
20
|
const gcfV1PollerOptions = {
|
|
21
21
|
apiOrigin: (0, api_1.functionsOrigin)(),
|
|
22
22
|
apiVersion: "v1",
|
|
@@ -5,7 +5,7 @@ const ora = require("ora");
|
|
|
5
5
|
const clc = require("colorette");
|
|
6
6
|
const logger_1 = require("../../logger");
|
|
7
7
|
const prompt_1 = require("../../prompt");
|
|
8
|
-
const Table = require("cli-
|
|
8
|
+
const Table = require("cli-table3");
|
|
9
9
|
const destructiveSqlKeywords = ["DROP", "DELETE"];
|
|
10
10
|
function checkIsDestructiveSql(query) {
|
|
11
11
|
const upperCaseQuery = query.toUpperCase();
|
|
@@ -75,13 +75,30 @@ async function askQuestions(setup, config) {
|
|
|
75
75
|
choices: connectorChoices,
|
|
76
76
|
});
|
|
77
77
|
const connectorYaml = JSON.parse(JSON.stringify(connectorInfo.connectorYaml));
|
|
78
|
-
const newConnectorYaml = generateSdkYaml(targetPlatform, connectorYaml, connectorInfo.directory, appDir);
|
|
78
|
+
const newConnectorYaml = await generateSdkYaml(targetPlatform, connectorYaml, connectorInfo.directory, appDir);
|
|
79
|
+
if (targetPlatform === types_1.Platform.WEB) {
|
|
80
|
+
const unusedFrameworks = fileUtils_1.SUPPORTED_FRAMEWORKS.filter((framework) => { var _a; return (_a = newConnectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk[framework]; });
|
|
81
|
+
if (unusedFrameworks.length > 0) {
|
|
82
|
+
const additionalFrameworks = await (0, prompt_1.prompt)(setup, [
|
|
83
|
+
{
|
|
84
|
+
type: "checkbox",
|
|
85
|
+
name: "features",
|
|
86
|
+
message: "Which framework would you like to generate SDKs for? " +
|
|
87
|
+
"Press Space to select features, then Enter to confirm your choices.",
|
|
88
|
+
choices: unusedFrameworks,
|
|
89
|
+
},
|
|
90
|
+
]);
|
|
91
|
+
for (const framework of additionalFrameworks.features) {
|
|
92
|
+
newConnectorYaml.generate.javascriptSdk[framework] = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
79
96
|
const connectorYamlContents = yaml.stringify(newConnectorYaml);
|
|
80
97
|
connectorInfo.connectorYaml = newConnectorYaml;
|
|
81
98
|
const displayIOSWarning = targetPlatform === types_1.Platform.IOS;
|
|
82
99
|
return { connectorYamlContents, connectorInfo, displayIOSWarning };
|
|
83
100
|
}
|
|
84
|
-
function generateSdkYaml(targetPlatform, connectorYaml, connectorDir, appDir) {
|
|
101
|
+
async function generateSdkYaml(targetPlatform, connectorYaml, connectorDir, appDir) {
|
|
85
102
|
if (!connectorYaml.generate) {
|
|
86
103
|
connectorYaml.generate = {};
|
|
87
104
|
}
|
|
@@ -94,11 +111,19 @@ function generateSdkYaml(targetPlatform, connectorYaml, connectorDir, appDir) {
|
|
|
94
111
|
}
|
|
95
112
|
if (targetPlatform === types_1.Platform.WEB) {
|
|
96
113
|
const pkg = `${connectorYaml.connectorId}-connector`;
|
|
114
|
+
const packageJsonDir = path.relative(connectorDir, appDir);
|
|
97
115
|
const javascriptSdk = {
|
|
98
116
|
outputDir: path.relative(connectorDir, path.join(appDir, `dataconnect-generated/js/${pkg}`)),
|
|
99
117
|
package: `@firebasegen/${pkg}`,
|
|
100
|
-
packageJsonDir
|
|
118
|
+
packageJsonDir,
|
|
101
119
|
};
|
|
120
|
+
const packageJson = await (0, fileUtils_1.resolvePackageJson)(appDir);
|
|
121
|
+
if (packageJson) {
|
|
122
|
+
const frameworksUsed = (0, fileUtils_1.getFrameworksFromPackageJson)(packageJson);
|
|
123
|
+
for (const framework of frameworksUsed) {
|
|
124
|
+
javascriptSdk[framework] = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
102
127
|
connectorYaml.generate.javascriptSdk = javascriptSdk;
|
|
103
128
|
}
|
|
104
129
|
if (targetPlatform === types_1.Platform.FLUTTER) {
|
|
@@ -65,6 +65,12 @@ async function getGenkitVersion() {
|
|
|
65
65
|
}
|
|
66
66
|
return { genkitVersion, templateVersion, useInit, stopInstall };
|
|
67
67
|
}
|
|
68
|
+
function showStartMessage(config, command) {
|
|
69
|
+
logger_1.logger.info("\nLogin to Google Cloud using:");
|
|
70
|
+
logger_1.logger.info(clc.bold(clc.green(` gcloud auth application-default login --project ${config.options.project}\n`)));
|
|
71
|
+
logger_1.logger.info("Then start the Genkit developer experience by running:");
|
|
72
|
+
logger_1.logger.info(clc.bold(clc.green(` ${command}`)));
|
|
73
|
+
}
|
|
68
74
|
async function doSetup(setup, config, options) {
|
|
69
75
|
var _a;
|
|
70
76
|
const genkitInfo = await getGenkitVersion();
|
|
@@ -113,23 +119,19 @@ async function doSetup(setup, config, options) {
|
|
|
113
119
|
else {
|
|
114
120
|
await (0, spawn_1.wrapSpawn)("npm", ["install", "-g", `genkit-cli@${genkitInfo.genkitVersion}`], projectDir);
|
|
115
121
|
await genkitSetup(options, genkitInfo, projectDir);
|
|
116
|
-
|
|
117
|
-
logger_1.logger.info(` cd ${setup.functions.source} && npm run genkit:start`);
|
|
122
|
+
showStartMessage(config, `cd ${setup.functions.source} && npm run genkit:start`);
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
else {
|
|
121
126
|
if (genkitInfo.useInit) {
|
|
122
127
|
await (0, spawn_1.wrapSpawn)("npm", ["install", `genkit@${genkitInfo.genkitVersion}`, "--save-dev"], projectDir);
|
|
123
128
|
await (0, spawn_1.wrapSpawn)("npx", ["genkit", "init", "-p", "firebase"], projectDir);
|
|
124
|
-
|
|
125
|
-
logger_1.logger.info(` cd ${setup.functions.source} && npx genkit start`);
|
|
129
|
+
showStartMessage(config, `cd ${setup.functions.source} && npx genkit start`);
|
|
126
130
|
}
|
|
127
131
|
else {
|
|
128
132
|
await (0, spawn_1.wrapSpawn)("npm", ["install", `genkit-cli@${genkitInfo.genkitVersion}`, "--save-dev"], projectDir);
|
|
129
133
|
await genkitSetup(options, genkitInfo, projectDir);
|
|
130
|
-
|
|
131
|
-
logger_1.logger.info();
|
|
132
|
-
logger_1.logger.info(clc.bold(clc.green(` cd ${setup.functions.source} && npm run genkit:start`)));
|
|
134
|
+
showStartMessage(config, `cd ${setup.functions.source} && npm run genkit:start`);
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
}
|
package/lib/management/apps.js
CHANGED
|
@@ -1,16 +1,248 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.deleteAppAndroidSha = exports.createAppAndroidSha = exports.listAppAndroidSha = exports.getAppConfig = exports.getAppConfigFile = exports.listFirebaseApps = exports.createWebApp = exports.createAndroidApp = exports.createIosApp = exports.getAppPlatform = exports.ShaCertificateType = exports.AppPlatform = exports.APP_LIST_PAGE_SIZE = void 0;
|
|
3
|
+
exports.findIntelligentPathForAndroid = exports.findIntelligentPathForIOS = exports.deleteAppAndroidSha = exports.createAppAndroidSha = exports.listAppAndroidSha = exports.getAppConfig = exports.writeConfigToFile = exports.getAppConfigFile = exports.listFirebaseApps = exports.createWebApp = exports.createAndroidApp = exports.createIosApp = exports.getAppPlatform = exports.ShaCertificateType = exports.AppPlatform = exports.getSdkConfig = exports.checkForApps = exports.getSdkOutputPath = exports.sdkInit = exports.getPlatform = exports.APP_LIST_PAGE_SIZE = void 0;
|
|
4
|
+
const fs = require("fs-extra");
|
|
5
|
+
const ora = require("ora");
|
|
6
|
+
const path = require("path");
|
|
4
7
|
const apiv2_1 = require("../apiv2");
|
|
5
8
|
const api_1 = require("../api");
|
|
6
9
|
const error_1 = require("../error");
|
|
7
10
|
const logger_1 = require("../logger");
|
|
8
11
|
const operation_poller_1 = require("../operation-poller");
|
|
9
|
-
const
|
|
12
|
+
const types_1 = require("../dataconnect/types");
|
|
13
|
+
const projectUtils_1 = require("../projectUtils");
|
|
14
|
+
const prompt_1 = require("../prompt");
|
|
15
|
+
const projects_1 = require("./projects");
|
|
16
|
+
const fileUtils_1 = require("../dataconnect/fileUtils");
|
|
17
|
+
const utils_1 = require("../utils");
|
|
10
18
|
const TIMEOUT_MILLIS = 30000;
|
|
11
19
|
exports.APP_LIST_PAGE_SIZE = 100;
|
|
12
20
|
const CREATE_APP_API_REQUEST_TIMEOUT_MILLIS = 15000;
|
|
13
|
-
const
|
|
21
|
+
const DISPLAY_NAME_QUESTION = {
|
|
22
|
+
type: "input",
|
|
23
|
+
name: "displayName",
|
|
24
|
+
default: "",
|
|
25
|
+
message: "What would you like to call your app?",
|
|
26
|
+
};
|
|
27
|
+
async function getPlatform(appDir, config) {
|
|
28
|
+
let targetPlatform = await (0, fileUtils_1.getPlatformFromFolder)(appDir);
|
|
29
|
+
if (targetPlatform === types_1.Platform.NONE) {
|
|
30
|
+
appDir = await (0, prompt_1.promptForDirectory)({
|
|
31
|
+
config,
|
|
32
|
+
relativeTo: appDir,
|
|
33
|
+
message: "We couldn't determine what kind of app you're using. Where is your app directory?",
|
|
34
|
+
});
|
|
35
|
+
targetPlatform = await (0, fileUtils_1.getPlatformFromFolder)(appDir);
|
|
36
|
+
}
|
|
37
|
+
if (targetPlatform === types_1.Platform.NONE || targetPlatform === types_1.Platform.MULTIPLE) {
|
|
38
|
+
if (targetPlatform === types_1.Platform.NONE) {
|
|
39
|
+
(0, utils_1.logBullet)(`Couldn't automatically detect app your in directory ${appDir}.`);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
(0, utils_1.logSuccess)(`Detected multiple app platforms in directory ${appDir}`);
|
|
43
|
+
}
|
|
44
|
+
const platforms = [
|
|
45
|
+
{ name: "iOS (Swift)", value: types_1.Platform.IOS },
|
|
46
|
+
{ name: "Web (JavaScript)", value: types_1.Platform.WEB },
|
|
47
|
+
{ name: "Android (Kotlin)", value: types_1.Platform.ANDROID },
|
|
48
|
+
];
|
|
49
|
+
targetPlatform = await (0, prompt_1.promptOnce)({
|
|
50
|
+
message: "Which platform do you want to set up an SDK for? Note: We currently do not support automatically setting up C++ or Unity projects.",
|
|
51
|
+
type: "list",
|
|
52
|
+
choices: platforms,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else if (targetPlatform === types_1.Platform.FLUTTER) {
|
|
56
|
+
(0, utils_1.logWarning)(`Detected ${targetPlatform} app in directory ${appDir}`);
|
|
57
|
+
throw new error_1.FirebaseError(`Flutter is not supported by apps:configure.
|
|
58
|
+
Please follow the link below to set up firebase for your Flutter app:
|
|
59
|
+
https://firebase.google.com/docs/flutter/setup
|
|
60
|
+
`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
(0, utils_1.logSuccess)(`Detected ${targetPlatform} app in directory ${appDir}`);
|
|
64
|
+
}
|
|
65
|
+
return targetPlatform === types_1.Platform.MULTIPLE
|
|
66
|
+
? AppPlatform.PLATFORM_UNSPECIFIED
|
|
67
|
+
: targetPlatform;
|
|
68
|
+
}
|
|
69
|
+
exports.getPlatform = getPlatform;
|
|
70
|
+
async function initiateIosAppCreation(options) {
|
|
71
|
+
if (!options.nonInteractive) {
|
|
72
|
+
await (0, prompt_1.prompt)(options, [
|
|
73
|
+
DISPLAY_NAME_QUESTION,
|
|
74
|
+
{
|
|
75
|
+
type: "input",
|
|
76
|
+
default: "",
|
|
77
|
+
name: "bundleId",
|
|
78
|
+
message: "Please specify your iOS app bundle ID:",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: "input",
|
|
82
|
+
default: "",
|
|
83
|
+
name: "appStoreId",
|
|
84
|
+
message: "Please specify your iOS app App Store ID:",
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
if (!options.bundleId) {
|
|
89
|
+
throw new error_1.FirebaseError("Bundle ID for iOS app cannot be empty");
|
|
90
|
+
}
|
|
91
|
+
const spinner = ora("Creating your iOS app").start();
|
|
92
|
+
try {
|
|
93
|
+
const appData = await createIosApp(options.project, {
|
|
94
|
+
displayName: options.displayName,
|
|
95
|
+
bundleId: options.bundleId,
|
|
96
|
+
appStoreId: options.appStoreId,
|
|
97
|
+
});
|
|
98
|
+
spinner.succeed();
|
|
99
|
+
return appData;
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
spinner.fail();
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function initiateAndroidAppCreation(options) {
|
|
107
|
+
if (!options.nonInteractive) {
|
|
108
|
+
await (0, prompt_1.prompt)(options, [
|
|
109
|
+
DISPLAY_NAME_QUESTION,
|
|
110
|
+
{
|
|
111
|
+
type: "input",
|
|
112
|
+
default: "",
|
|
113
|
+
name: "packageName",
|
|
114
|
+
message: "Please specify your Android app package name:",
|
|
115
|
+
},
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
if (!options.packageName) {
|
|
119
|
+
throw new error_1.FirebaseError("Package name for Android app cannot be empty");
|
|
120
|
+
}
|
|
121
|
+
const spinner = ora("Creating your Android app").start();
|
|
122
|
+
try {
|
|
123
|
+
const appData = await createAndroidApp(options.project, {
|
|
124
|
+
displayName: options.displayName,
|
|
125
|
+
packageName: options.packageName,
|
|
126
|
+
});
|
|
127
|
+
spinner.succeed();
|
|
128
|
+
return appData;
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
spinner.fail();
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function initiateWebAppCreation(options) {
|
|
136
|
+
if (!options.nonInteractive) {
|
|
137
|
+
await (0, prompt_1.prompt)(options, [DISPLAY_NAME_QUESTION]);
|
|
138
|
+
}
|
|
139
|
+
if (!options.displayName) {
|
|
140
|
+
throw new error_1.FirebaseError("Display name for Web app cannot be empty");
|
|
141
|
+
}
|
|
142
|
+
const spinner = ora("Creating your Web app").start();
|
|
143
|
+
try {
|
|
144
|
+
const appData = await createWebApp(options.project, { displayName: options.displayName });
|
|
145
|
+
spinner.succeed();
|
|
146
|
+
return appData;
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
spinner.fail();
|
|
150
|
+
throw err;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function sdkInit(appPlatform, options) {
|
|
154
|
+
let appData;
|
|
155
|
+
switch (appPlatform) {
|
|
156
|
+
case AppPlatform.IOS:
|
|
157
|
+
appData = await initiateIosAppCreation(options);
|
|
158
|
+
break;
|
|
159
|
+
case AppPlatform.ANDROID:
|
|
160
|
+
appData = await initiateAndroidAppCreation(options);
|
|
161
|
+
break;
|
|
162
|
+
case AppPlatform.WEB:
|
|
163
|
+
appData = await initiateWebAppCreation(options);
|
|
164
|
+
break;
|
|
165
|
+
default:
|
|
166
|
+
throw new error_1.FirebaseError("Unexpected error. This should not happen");
|
|
167
|
+
}
|
|
168
|
+
return appData;
|
|
169
|
+
}
|
|
170
|
+
exports.sdkInit = sdkInit;
|
|
171
|
+
async function getSdkOutputPath(appDir, platform, config) {
|
|
172
|
+
switch (platform) {
|
|
173
|
+
case AppPlatform.ANDROID:
|
|
174
|
+
const androidPath = await findIntelligentPathForAndroid(appDir, config);
|
|
175
|
+
return path.join(androidPath, "google-services.json");
|
|
176
|
+
case AppPlatform.WEB:
|
|
177
|
+
return path.join(appDir, "firebase-js-config.json");
|
|
178
|
+
case AppPlatform.IOS:
|
|
179
|
+
const iosPath = await findIntelligentPathForIOS(appDir, config);
|
|
180
|
+
return path.join(iosPath, "GoogleService-Info.plist");
|
|
181
|
+
}
|
|
182
|
+
throw new error_1.FirebaseError("Platform " + platform.toString() + " is not supported yet.");
|
|
183
|
+
}
|
|
184
|
+
exports.getSdkOutputPath = getSdkOutputPath;
|
|
185
|
+
function checkForApps(apps, appPlatform) {
|
|
186
|
+
if (!apps.length) {
|
|
187
|
+
throw new error_1.FirebaseError(`There are no ${appPlatform === AppPlatform.ANY ? "" : appPlatform + " "}apps ` +
|
|
188
|
+
"associated with this Firebase project");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.checkForApps = checkForApps;
|
|
192
|
+
async function selectAppInteractively(apps, appPlatform) {
|
|
193
|
+
checkForApps(apps, appPlatform);
|
|
194
|
+
const choices = apps.map((app) => {
|
|
195
|
+
return {
|
|
196
|
+
name: `${app.displayName || app.bundleId || app.packageName}` +
|
|
197
|
+
` - ${app.appId} (${app.platform})`,
|
|
198
|
+
value: app,
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
return await (0, prompt_1.promptOnce)({
|
|
202
|
+
type: "list",
|
|
203
|
+
message: `Select the ${appPlatform === AppPlatform.ANY ? "" : appPlatform + " "}` +
|
|
204
|
+
"app to get the configuration data:",
|
|
205
|
+
choices,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async function getSdkConfig(options, appPlatform, appId) {
|
|
209
|
+
if (!appId) {
|
|
210
|
+
let projectId = (0, projectUtils_1.needProjectId)(options);
|
|
211
|
+
if (options.nonInteractive && !projectId) {
|
|
212
|
+
throw new error_1.FirebaseError("Must supply app and project ids in non-interactive mode.");
|
|
213
|
+
}
|
|
214
|
+
else if (!projectId) {
|
|
215
|
+
const result = await (0, projects_1.getOrPromptProject)(options);
|
|
216
|
+
projectId = result.projectId;
|
|
217
|
+
}
|
|
218
|
+
const apps = await listFirebaseApps(projectId, appPlatform);
|
|
219
|
+
checkForApps(apps, appPlatform);
|
|
220
|
+
if (apps.length === 1) {
|
|
221
|
+
appId = apps[0].appId;
|
|
222
|
+
appPlatform = apps[0].platform;
|
|
223
|
+
}
|
|
224
|
+
else if (options.nonInteractive) {
|
|
225
|
+
throw new error_1.FirebaseError(`Project ${projectId} has multiple apps, must specify an app id.`);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const appMetadata = await selectAppInteractively(apps, appPlatform);
|
|
229
|
+
appId = appMetadata.appId;
|
|
230
|
+
appPlatform = appMetadata.platform;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
let configData;
|
|
234
|
+
const spinner = ora(`Downloading configuration data for your Firebase ${appPlatform} app`).start();
|
|
235
|
+
try {
|
|
236
|
+
configData = await getAppConfig(appId, appPlatform);
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
spinner.fail();
|
|
240
|
+
throw err;
|
|
241
|
+
}
|
|
242
|
+
spinner.succeed();
|
|
243
|
+
return configData;
|
|
244
|
+
}
|
|
245
|
+
exports.getSdkConfig = getSdkConfig;
|
|
14
246
|
var AppPlatform;
|
|
15
247
|
(function (AppPlatform) {
|
|
16
248
|
AppPlatform["PLATFORM_UNSPECIFIED"] = "PLATFORM_UNSPECIFIED";
|
|
@@ -182,13 +414,12 @@ function getAppConfigResourceString(appId, platform) {
|
|
|
182
414
|
}
|
|
183
415
|
function parseConfigFromResponse(responseBody, platform) {
|
|
184
416
|
if (platform === AppPlatform.WEB) {
|
|
185
|
-
const JS_TEMPLATE = (0, templates_1.readTemplateSync)("setup/web.js");
|
|
186
417
|
return {
|
|
187
|
-
fileName:
|
|
188
|
-
fileContents:
|
|
418
|
+
fileName: "firebase-js-config.json",
|
|
419
|
+
fileContents: JSON.stringify(responseBody, null, 2),
|
|
189
420
|
};
|
|
190
421
|
}
|
|
191
|
-
else if (
|
|
422
|
+
else if ("configFilename" in responseBody) {
|
|
192
423
|
return {
|
|
193
424
|
fileName: responseBody.configFilename,
|
|
194
425
|
fileContents: Buffer.from(responseBody.configFileContents, "base64").toString("utf8"),
|
|
@@ -200,6 +431,24 @@ function getAppConfigFile(config, platform) {
|
|
|
200
431
|
return parseConfigFromResponse(config, platform);
|
|
201
432
|
}
|
|
202
433
|
exports.getAppConfigFile = getAppConfigFile;
|
|
434
|
+
async function writeConfigToFile(filename, nonInteractive, fileContents) {
|
|
435
|
+
if (fs.existsSync(filename)) {
|
|
436
|
+
if (nonInteractive) {
|
|
437
|
+
throw new error_1.FirebaseError(`${filename} already exists`);
|
|
438
|
+
}
|
|
439
|
+
const overwrite = await (0, prompt_1.promptOnce)({
|
|
440
|
+
type: "confirm",
|
|
441
|
+
default: false,
|
|
442
|
+
message: `${filename} already exists. Do you want to overwrite?`,
|
|
443
|
+
});
|
|
444
|
+
if (!overwrite) {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
await fs.writeFile(filename, fileContents);
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
exports.writeConfigToFile = writeConfigToFile;
|
|
203
452
|
async function getAppConfig(appId, platform) {
|
|
204
453
|
try {
|
|
205
454
|
const response = await apiClient.request({
|
|
@@ -278,3 +527,61 @@ async function deleteAppAndroidSha(projectId, appId, shaId) {
|
|
|
278
527
|
}
|
|
279
528
|
}
|
|
280
529
|
exports.deleteAppAndroidSha = deleteAppAndroidSha;
|
|
530
|
+
async function findIntelligentPathForIOS(appDir, options) {
|
|
531
|
+
const currentFiles = await fs.readdir(appDir, { withFileTypes: true });
|
|
532
|
+
for (let i = 0; i < currentFiles.length; i++) {
|
|
533
|
+
const dirent = currentFiles[i];
|
|
534
|
+
const xcodeStr = ".xcodeproj";
|
|
535
|
+
const file = dirent.name;
|
|
536
|
+
if (file.endsWith(xcodeStr)) {
|
|
537
|
+
return path.join(appDir, file.substring(0, file.length - xcodeStr.length));
|
|
538
|
+
}
|
|
539
|
+
else if (file === "Info.plist" ||
|
|
540
|
+
file === "Assets.xcassets" ||
|
|
541
|
+
(dirent.isDirectory() && file === "Preview Content")) {
|
|
542
|
+
return appDir;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
let outputPath = null;
|
|
546
|
+
if (!options.nonInteractive) {
|
|
547
|
+
outputPath = await (0, prompt_1.promptForDirectory)({
|
|
548
|
+
config: options.config,
|
|
549
|
+
message: `We weren't able to automatically determine the output directory. Where would you like to output your config file?`,
|
|
550
|
+
relativeTo: appDir,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
if (!outputPath) {
|
|
554
|
+
throw new Error("We weren't able to automatically determine the output directory.");
|
|
555
|
+
}
|
|
556
|
+
return outputPath;
|
|
557
|
+
}
|
|
558
|
+
exports.findIntelligentPathForIOS = findIntelligentPathForIOS;
|
|
559
|
+
async function findIntelligentPathForAndroid(appDir, options) {
|
|
560
|
+
const paths = appDir.split("/");
|
|
561
|
+
if (paths[0] === "app") {
|
|
562
|
+
return appDir;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
const currentFiles = await fs.readdir(appDir, { withFileTypes: true });
|
|
566
|
+
const dirs = [];
|
|
567
|
+
for (const fileOrDir of currentFiles) {
|
|
568
|
+
if (fileOrDir.isDirectory()) {
|
|
569
|
+
if (fileOrDir.name === "src") {
|
|
570
|
+
return appDir;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
let module = path.join(appDir, "app");
|
|
575
|
+
if (dirs.length === 1) {
|
|
576
|
+
return module;
|
|
577
|
+
}
|
|
578
|
+
if (!options.nonInteractive) {
|
|
579
|
+
module = await (0, prompt_1.promptForDirectory)({
|
|
580
|
+
config: options.config,
|
|
581
|
+
message: `We weren't able to automatically determine the output directory. Where would you like to output your config file?`,
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
return module;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
exports.findIntelligentPathForAndroid = findIntelligentPathForAndroid;
|