firebase-tools 10.1.5 → 10.2.2
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/api.js +1 -0
- package/lib/apiv2.js +3 -0
- package/lib/appdistribution/options-parser-util.js +1 -1
- package/lib/auth.js +62 -25
- package/lib/command.js +1 -1
- package/lib/commands/apps-android-sha-create.js +2 -2
- package/lib/commands/apps-sdkconfig.js +1 -1
- package/lib/commands/auth-import.js +1 -1
- package/lib/commands/database-rules-list.js +2 -2
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/ext-configure.js +1 -0
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +12 -2
- package/lib/commands/ext-install.js +104 -103
- package/lib/commands/ext-uninstall.js +9 -8
- package/lib/commands/ext-update.js +10 -9
- package/lib/commands/functions-config-clone.js +1 -1
- package/lib/commands/functions-config-export.js +1 -1
- package/lib/commands/functions-secrets-access.js +17 -0
- package/lib/commands/functions-secrets-destroy.js +40 -0
- package/lib/commands/functions-secrets-get.js +21 -0
- package/lib/commands/functions-secrets-prune.js +50 -0
- package/lib/commands/functions-secrets-set.js +46 -0
- package/lib/commands/hosting-clone.js +3 -3
- package/lib/commands/index.js +7 -3
- package/lib/commands/login.js +1 -1
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/deploy/extensions/deploymentSummary.js +3 -3
- package/lib/deploy/extensions/params.js +3 -0
- package/lib/deploy/extensions/planner.js +2 -1
- package/lib/deploy/extensions/tasks.js +1 -1
- package/lib/deploy/functions/backend.js +20 -5
- package/lib/deploy/functions/checkIam.js +1 -1
- package/lib/deploy/functions/containerCleaner.js +3 -3
- package/lib/deploy/functions/ensure.js +112 -0
- package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
- package/lib/deploy/functions/functionsDeployHelper.js +2 -2
- package/lib/deploy/functions/prepare.js +15 -20
- package/lib/deploy/functions/pricing.js +1 -1
- package/lib/deploy/functions/prompts.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +3 -3
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/planner.js +11 -8
- package/lib/deploy/functions/release/reporter.js +3 -0
- package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
- package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +17 -11
- package/lib/deploy/functions/runtimes/golang/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/index.js +26 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +40 -7
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/validate.js +58 -3
- package/lib/deploy/hosting/client.js +9 -0
- package/lib/deploy/hosting/convertConfig.js +6 -0
- package/lib/deploy/hosting/deploy.js +2 -2
- package/lib/deploy/hosting/hashcache.js +21 -19
- package/lib/deploy/hosting/index.js +5 -5
- package/lib/deploy/hosting/prepare.js +25 -25
- package/lib/deploy/hosting/release.js +21 -24
- package/lib/deploy/hosting/uploader.js +5 -5
- package/lib/deploy/remoteconfig/functions.js +2 -2
- package/lib/emulator/auth/cloudFunctions.js +1 -1
- package/lib/emulator/auth/operations.js +1 -1
- package/lib/emulator/commandUtils.js +5 -1
- package/lib/emulator/constants.js +3 -0
- package/lib/emulator/controller.js +48 -18
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +30 -13
- package/lib/emulator/emulatorLogger.js +19 -1
- package/lib/emulator/extensions/validation.js +35 -0
- package/lib/emulator/extensionsEmulator.js +140 -0
- package/lib/emulator/functionsEmulator.js +175 -86
- package/lib/emulator/functionsEmulatorRuntime.js +108 -83
- package/lib/emulator/functionsEmulatorShared.js +51 -1
- package/lib/emulator/functionsEmulatorShell.js +1 -2
- package/lib/emulator/functionsEmulatorUtils.js +4 -4
- package/lib/emulator/functionsRuntimeWorker.js +3 -3
- package/lib/emulator/hub.js +4 -3
- package/lib/emulator/loggingEmulator.js +1 -1
- package/lib/emulator/pubsubEmulator.js +1 -1
- package/lib/emulator/registry.js +10 -2
- package/lib/emulator/storage/apis/firebase.js +31 -26
- package/lib/emulator/storage/apis/gcloud.js +7 -12
- package/lib/emulator/storage/files.js +36 -34
- package/lib/emulator/storage/index.js +2 -2
- package/lib/emulator/storage/metadata.js +2 -2
- package/lib/emulator/storage/rules/runtime.js +8 -7
- package/lib/emulator/types.js +3 -0
- package/lib/ensureApiEnabled.js +5 -1
- package/lib/error.js +1 -1
- package/lib/extensions/askUserForParam.js +2 -2
- package/lib/extensions/changelog.js +3 -1
- package/lib/extensions/checkProjectBilling.js +1 -1
- package/lib/extensions/diagnose.js +56 -0
- package/lib/extensions/displayExtensionInfo.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +24 -8
- package/lib/extensions/emulator/specHelper.js +10 -23
- package/lib/extensions/export.js +1 -51
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +23 -10
- package/lib/extensions/listExtensions.js +2 -0
- package/lib/extensions/manifest.js +48 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +4 -4
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/secretsUtils.js +4 -4
- package/lib/functional.js +1 -1
- package/lib/functions/env.js +7 -8
- package/lib/functions/secrets.js +112 -0
- package/lib/gcp/cloudfunctions.js +24 -5
- package/lib/gcp/cloudfunctionsv2.js +18 -5
- package/lib/gcp/cloudtasks.js +1 -1
- package/lib/gcp/docker.js +2 -2
- package/lib/gcp/run.js +2 -2
- package/lib/gcp/secretManager.js +128 -46
- package/lib/gcp/storage.js +1 -0
- package/lib/hosting/api.js +1 -1
- package/lib/hosting/functionsProxy.js +15 -5
- package/lib/hosting/proxy.js +2 -2
- package/lib/init/features/account.js +1 -1
- package/lib/management/database.js +1 -1
- package/lib/previews.js +1 -1
- package/lib/responseToError.js +16 -7
- package/lib/serve/functions.js +2 -2
- package/lib/serve/hosting.js +1 -1
- package/lib/utils.js +7 -2
- package/npm-shrinkwrap.json +904 -412
- package/package.json +3 -3
- package/schema/firebase-config.json +32 -0
- package/templates/init/functions/javascript/package.lint.json +3 -3
- package/templates/init/functions/javascript/package.nolint.json +2 -2
- package/templates/init/functions/typescript/package.lint.json +7 -7
- package/templates/init/functions/typescript/package.nolint.json +3 -3
package/lib/api.js
CHANGED
|
@@ -67,6 +67,7 @@ var _appendQueryData = function (path, data) {
|
|
|
67
67
|
return path;
|
|
68
68
|
};
|
|
69
69
|
var api = {
|
|
70
|
+
authProxyOrigin: utils.envOverride("FIREBASE_AUTHPROXY_URL", "https://auth.firebase.tools"),
|
|
70
71
|
clientId: utils.envOverride("FIREBASE_CLIENT_ID", "563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com"),
|
|
71
72
|
clientSecret: utils.envOverride("FIREBASE_CLIENT_SECRET", "j9iVZfS8kkCEFUPaAeJV0sAi"),
|
|
72
73
|
cloudbillingOrigin: utils.envOverride("FIREBASE_CLOUDBILLING_URL", "https://cloudbilling.googleapis.com"),
|
package/lib/apiv2.js
CHANGED
|
@@ -16,7 +16,7 @@ function getTestersOrGroups(value, file) {
|
|
|
16
16
|
}
|
|
17
17
|
exports.getTestersOrGroups = getTestersOrGroups;
|
|
18
18
|
function getEmails(emails, file) {
|
|
19
|
-
if (emails.length
|
|
19
|
+
if (emails.length === 0) {
|
|
20
20
|
ensureFileExists(file);
|
|
21
21
|
const readFile = fs.readFileSync(file, "utf8");
|
|
22
22
|
return splitter(readFile);
|
package/lib/auth.js
CHANGED
|
@@ -19,6 +19,10 @@ const logger_1 = require("./logger");
|
|
|
19
19
|
const prompt_1 = require("./prompt");
|
|
20
20
|
const scopes = require("./scopes");
|
|
21
21
|
const defaultCredentials_1 = require("./defaultCredentials");
|
|
22
|
+
const uuid_1 = require("uuid");
|
|
23
|
+
const crypto_1 = require("crypto");
|
|
24
|
+
const cli_color_1 = require("cli-color");
|
|
25
|
+
const track_1 = require("./track");
|
|
22
26
|
portfinder.basePort = 9005;
|
|
23
27
|
function getGlobalDefaultAccount() {
|
|
24
28
|
const user = configstore_1.configstore.get("user");
|
|
@@ -182,19 +186,23 @@ function getLoginUrl(callbackUrl, userHint) {
|
|
|
182
186
|
login_hint: userHint,
|
|
183
187
|
}));
|
|
184
188
|
}
|
|
185
|
-
async function getTokensFromAuthorizationCode(code, callbackUrl) {
|
|
189
|
+
async function getTokensFromAuthorizationCode(code, callbackUrl, verifier) {
|
|
186
190
|
var _a, _b;
|
|
187
191
|
let res;
|
|
192
|
+
const params = {
|
|
193
|
+
code: code,
|
|
194
|
+
client_id: api.clientId,
|
|
195
|
+
client_secret: api.clientSecret,
|
|
196
|
+
redirect_uri: callbackUrl,
|
|
197
|
+
grant_type: "authorization_code",
|
|
198
|
+
};
|
|
199
|
+
if (verifier) {
|
|
200
|
+
params["code_verifier"] = verifier;
|
|
201
|
+
}
|
|
188
202
|
try {
|
|
189
203
|
res = await api.request("POST", "/o/oauth2/token", {
|
|
190
204
|
origin: api.authOrigin,
|
|
191
|
-
form:
|
|
192
|
-
code: code,
|
|
193
|
-
client_id: api.clientId,
|
|
194
|
-
client_secret: api.clientSecret,
|
|
195
|
-
redirect_uri: callbackUrl,
|
|
196
|
-
grant_type: "authorization_code",
|
|
197
|
-
},
|
|
205
|
+
form: params,
|
|
198
206
|
});
|
|
199
207
|
}
|
|
200
208
|
catch (err) {
|
|
@@ -248,31 +256,58 @@ async function respondWithFile(req, res, statusCode, filename) {
|
|
|
248
256
|
res.end(response);
|
|
249
257
|
req.socket.destroy();
|
|
250
258
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
259
|
+
function urlsafeBase64(base64string) {
|
|
260
|
+
return base64string.replace(/\+/g, "-").replace(/=+$/, "").replace(/\//g, "_");
|
|
261
|
+
}
|
|
262
|
+
async function loginRemotely(userHint) {
|
|
263
|
+
var _a;
|
|
264
|
+
const authProxyClient = new apiv2.Client({
|
|
265
|
+
urlPrefix: api.authProxyOrigin,
|
|
266
|
+
auth: false,
|
|
267
|
+
});
|
|
268
|
+
const sessionId = (0, uuid_1.v4)();
|
|
269
|
+
const codeVerifier = (0, crypto_1.randomBytes)(32).toString("hex");
|
|
270
|
+
const codeChallenge = urlsafeBase64((0, crypto_1.createHash)("sha256").update(codeVerifier).digest("base64"));
|
|
271
|
+
const attestToken = (_a = (await authProxyClient.post("/attest", {
|
|
272
|
+
session_id: sessionId,
|
|
273
|
+
})).body) === null || _a === void 0 ? void 0 : _a.token;
|
|
274
|
+
const loginUrl = `${api.authProxyOrigin}/login?code_challenge=${codeChallenge}&session=${sessionId}&attest=${attestToken}`;
|
|
275
|
+
logger_1.logger.info();
|
|
276
|
+
logger_1.logger.info("To sign in to the Firebase CLI:");
|
|
254
277
|
logger_1.logger.info();
|
|
255
|
-
logger_1.logger.info("
|
|
256
|
-
logger_1.logger.info(
|
|
278
|
+
logger_1.logger.info("1. Take note of your session ID:");
|
|
279
|
+
logger_1.logger.info();
|
|
280
|
+
logger_1.logger.info(` ${(0, cli_color_1.bold)(sessionId.substring(0, 5).toUpperCase())}`);
|
|
281
|
+
logger_1.logger.info();
|
|
282
|
+
logger_1.logger.info("2. Visit the URL below on any device and follow the instructions to get your code:");
|
|
283
|
+
logger_1.logger.info();
|
|
284
|
+
logger_1.logger.info(` ${loginUrl}`);
|
|
285
|
+
logger_1.logger.info();
|
|
286
|
+
logger_1.logger.info("3. Paste or enter the authorization code below once you have it:");
|
|
257
287
|
logger_1.logger.info();
|
|
258
|
-
open(authUrl);
|
|
259
288
|
const code = await (0, prompt_1.promptOnce)({
|
|
260
289
|
type: "input",
|
|
261
|
-
|
|
262
|
-
message: "Paste authorization code here:",
|
|
290
|
+
message: "Enter authorization code:",
|
|
263
291
|
});
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
292
|
+
try {
|
|
293
|
+
const tokens = await getTokensFromAuthorizationCode(code, `${api.authProxyOrigin}/complete`, codeVerifier);
|
|
294
|
+
void (0, track_1.track)("login", "google_remote");
|
|
295
|
+
return {
|
|
296
|
+
user: jwt.decode(tokens.id_token),
|
|
297
|
+
tokens: tokens,
|
|
298
|
+
scopes: SCOPES,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
throw new error_1.FirebaseError("Unable to authenticate using the provided code. Please try again.");
|
|
303
|
+
}
|
|
270
304
|
}
|
|
271
305
|
async function loginWithLocalhostGoogle(port, userHint) {
|
|
272
306
|
const callbackUrl = getCallbackUrl(port);
|
|
273
307
|
const authUrl = getLoginUrl(callbackUrl, userHint);
|
|
274
308
|
const successTemplate = "../templates/loginSuccess.html";
|
|
275
309
|
const tokens = await loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getTokensFromAuthorizationCode);
|
|
310
|
+
void (0, track_1.track)("login", "google_localhost");
|
|
276
311
|
return {
|
|
277
312
|
user: jwt.decode(tokens.id_token),
|
|
278
313
|
tokens: tokens,
|
|
@@ -283,7 +318,9 @@ async function loginWithLocalhostGitHub(port) {
|
|
|
283
318
|
const callbackUrl = getCallbackUrl(port);
|
|
284
319
|
const authUrl = getGithubLoginUrl(callbackUrl);
|
|
285
320
|
const successTemplate = "../templates/loginSuccessGithub.html";
|
|
286
|
-
|
|
321
|
+
const tokens = await loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getGithubTokensFromAuthorizationCode);
|
|
322
|
+
void (0, track_1.track)("login", "google_localhost");
|
|
323
|
+
return tokens;
|
|
287
324
|
}
|
|
288
325
|
async function loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getTokens) {
|
|
289
326
|
return new Promise((resolve, reject) => {
|
|
@@ -331,10 +368,10 @@ async function loginGoogle(localhost, userHint) {
|
|
|
331
368
|
return await loginWithLocalhostGoogle(port, userHint);
|
|
332
369
|
}
|
|
333
370
|
catch (_a) {
|
|
334
|
-
return await
|
|
371
|
+
return await loginRemotely(userHint);
|
|
335
372
|
}
|
|
336
373
|
}
|
|
337
|
-
return await
|
|
374
|
+
return await loginRemotely(userHint);
|
|
338
375
|
}
|
|
339
376
|
exports.loginGoogle = loginGoogle;
|
|
340
377
|
async function loginGithub() {
|
package/lib/command.js
CHANGED
|
@@ -85,7 +85,7 @@ class Command {
|
|
|
85
85
|
}, null, 2));
|
|
86
86
|
}
|
|
87
87
|
const duration = new Date().getTime() - start;
|
|
88
|
-
track(this.name, "success", duration).then(() => process.exit());
|
|
88
|
+
void track(this.name, "success", duration).then(() => process.exit());
|
|
89
89
|
})
|
|
90
90
|
.catch(async (err) => {
|
|
91
91
|
if ((0, utils_1.getInheritedOption)(options, "json")) {
|
|
@@ -9,9 +9,9 @@ const utils_1 = require("../utils");
|
|
|
9
9
|
function getCertHashType(shaHash) {
|
|
10
10
|
shaHash = shaHash.replace(/:/g, "");
|
|
11
11
|
const shaHashCount = shaHash.length;
|
|
12
|
-
if (shaHashCount
|
|
12
|
+
if (shaHashCount === 40)
|
|
13
13
|
return apps_1.ShaCertificateType.SHA_1.toString();
|
|
14
|
-
if (shaHashCount
|
|
14
|
+
if (shaHashCount === 64)
|
|
15
15
|
return apps_1.ShaCertificateType.SHA_256.toString();
|
|
16
16
|
return apps_1.ShaCertificateType.SHA_CERTIFICATE_TYPE_UNSPECIFIED.toString();
|
|
17
17
|
}
|
|
@@ -70,7 +70,7 @@ module.exports = new command_1.Command("apps:sdkconfig [platform] [appId]")
|
|
|
70
70
|
}
|
|
71
71
|
spinner.succeed();
|
|
72
72
|
const fileInfo = (0, apps_1.getAppConfigFile)(configData, appPlatform);
|
|
73
|
-
if (appPlatform
|
|
73
|
+
if (appPlatform === apps_1.AppPlatform.WEB) {
|
|
74
74
|
fileInfo.sdkConfig = configData;
|
|
75
75
|
}
|
|
76
76
|
if (options.out === undefined) {
|
|
@@ -93,7 +93,7 @@ module.exports = new command_1.Command("auth:import [dataFile]")
|
|
|
93
93
|
if (err) {
|
|
94
94
|
throw new error_1.FirebaseError(`Validation Error: ${err}`);
|
|
95
95
|
}
|
|
96
|
-
currentBatch.push(
|
|
96
|
+
currentBatch.push(value);
|
|
97
97
|
if (currentBatch.length === MAX_BATCH_SIZE) {
|
|
98
98
|
batches.push(currentBatch);
|
|
99
99
|
currentBatch = [];
|
|
@@ -18,10 +18,10 @@ exports.default = new command_1.Command("database:rules:list")
|
|
|
18
18
|
const rulesets = await metadata.listAllRulesets(options.instance);
|
|
19
19
|
for (const ruleset of rulesets) {
|
|
20
20
|
const labels = [];
|
|
21
|
-
if (ruleset.id
|
|
21
|
+
if (ruleset.id === labeled.stable) {
|
|
22
22
|
labels.push("stable");
|
|
23
23
|
}
|
|
24
|
-
if (ruleset.id
|
|
24
|
+
if (ruleset.id === labeled.canary) {
|
|
25
25
|
labels.push("canary");
|
|
26
26
|
}
|
|
27
27
|
logger_1.logger.info(`${ruleset.id} ${ruleset.createdAt} ${labels.join(",")}`);
|
|
@@ -59,9 +59,9 @@ module.exports = new command_1.Command("emulators:start")
|
|
|
59
59
|
emulatorsTable.push(...controller
|
|
60
60
|
.filterEmulatorTargets(options)
|
|
61
61
|
.map((emulator) => {
|
|
62
|
-
const info = registry_1.EmulatorRegistry.getInfo(emulator);
|
|
63
62
|
const emulatorName = constants_1.Constants.description(emulator).replace(/ emulator/i, "");
|
|
64
63
|
const isSupportedByUi = types_1.EMULATORS_SUPPORTED_BY_UI.includes(emulator);
|
|
64
|
+
const info = registry_1.EmulatorRegistry.getInfo(emulator === types_1.Emulators.EXTENSIONS ? types_1.Emulators.FUNCTIONS : emulator);
|
|
65
65
|
if (!info) {
|
|
66
66
|
return [emulatorName, "Failed to initialize (see above)", "", ""];
|
|
67
67
|
}
|
|
@@ -27,6 +27,7 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
27
27
|
"firebaseextensions.instances.get",
|
|
28
28
|
])
|
|
29
29
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
30
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
30
31
|
.action(async (instanceId, options) => {
|
|
31
32
|
const spinner = ora(`Configuring ${clc.bold(instanceId)}. This usually takes 3 to 5 minutes...`);
|
|
32
33
|
try {
|
|
@@ -24,6 +24,55 @@ function readCommonTemplates() {
|
|
|
24
24
|
changelogTemplate: fs.readFileSync(path.join(TEMPLATE_ROOT, "CHANGELOG.md"), "utf8"),
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
+
exports.default = new command_1.Command("ext:dev:init")
|
|
28
|
+
.description("initialize files for writing an extension in the current directory")
|
|
29
|
+
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extDevMinVersion")
|
|
30
|
+
.action(async (options) => {
|
|
31
|
+
const cwd = options.cwd || process.cwd();
|
|
32
|
+
const config = new config_1.Config({}, { projectDir: cwd, cwd: cwd });
|
|
33
|
+
try {
|
|
34
|
+
const lang = await (0, prompt_1.promptOnce)({
|
|
35
|
+
type: "list",
|
|
36
|
+
name: "language",
|
|
37
|
+
message: "In which language do you want to write the Cloud Functions for your extension?",
|
|
38
|
+
default: "javascript",
|
|
39
|
+
choices: [
|
|
40
|
+
{
|
|
41
|
+
name: "JavaScript",
|
|
42
|
+
value: "javascript",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "TypeScript",
|
|
46
|
+
value: "typescript",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
switch (lang) {
|
|
51
|
+
case "javascript": {
|
|
52
|
+
await javascriptSelected(config);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case "typescript": {
|
|
56
|
+
await typescriptSelected(config);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
default: {
|
|
60
|
+
throw new error_1.FirebaseError(`${lang} is not supported.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
await npmDependencies.askInstallDependencies({}, config);
|
|
64
|
+
const welcome = fs.readFileSync(path.join(TEMPLATE_ROOT, lang, "WELCOME.md"), "utf8");
|
|
65
|
+
return logger_1.logger.info("\n" + marked(welcome));
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (!(err instanceof error_1.FirebaseError)) {
|
|
69
|
+
throw new error_1.FirebaseError(`Error occurred when initializing files for new extension: ${err.message}`, {
|
|
70
|
+
original: err,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
27
76
|
async function typescriptSelected(config) {
|
|
28
77
|
const packageLintingTemplate = fs.readFileSync(path.join(TEMPLATE_ROOT, "typescript", "package.lint.json"), "utf8");
|
|
29
78
|
const packageNoLintingTemplate = fs.readFileSync(path.join(TEMPLATE_ROOT, "typescript", "package.nolint.json"), "utf8");
|
|
@@ -84,52 +133,3 @@ async function javascriptSelected(config) {
|
|
|
84
133
|
}
|
|
85
134
|
await config.askWriteProjectFile("functions/.gitignore", gitignoreTemplate);
|
|
86
135
|
}
|
|
87
|
-
exports.default = new command_1.Command("ext:dev:init")
|
|
88
|
-
.description("initialize files for writing an extension in the current directory")
|
|
89
|
-
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extDevMinVersion")
|
|
90
|
-
.action(async (options) => {
|
|
91
|
-
const cwd = options.cwd || process.cwd();
|
|
92
|
-
const config = new config_1.Config({}, { projectDir: cwd, cwd: cwd });
|
|
93
|
-
try {
|
|
94
|
-
const lang = await (0, prompt_1.promptOnce)({
|
|
95
|
-
type: "list",
|
|
96
|
-
name: "language",
|
|
97
|
-
message: "In which language do you want to write the Cloud Functions for your extension?",
|
|
98
|
-
default: "javascript",
|
|
99
|
-
choices: [
|
|
100
|
-
{
|
|
101
|
-
name: "JavaScript",
|
|
102
|
-
value: "javascript",
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
name: "TypeScript",
|
|
106
|
-
value: "typescript",
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
});
|
|
110
|
-
switch (lang) {
|
|
111
|
-
case "javascript": {
|
|
112
|
-
await javascriptSelected(config);
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
case "typescript": {
|
|
116
|
-
await typescriptSelected(config);
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
default: {
|
|
120
|
-
throw new error_1.FirebaseError(`${lang} is not supported.`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
await npmDependencies.askInstallDependencies({}, config);
|
|
124
|
-
const welcome = fs.readFileSync(path.join(TEMPLATE_ROOT, lang, "WELCOME.md"), "utf8");
|
|
125
|
-
return logger_1.logger.info("\n" + marked(welcome));
|
|
126
|
-
}
|
|
127
|
-
catch (err) {
|
|
128
|
-
if (!(err instanceof error_1.FirebaseError)) {
|
|
129
|
-
throw new error_1.FirebaseError(`Error occurred when initializing files for new extension: ${err.message}`, {
|
|
130
|
-
original: err,
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
throw err;
|
|
134
|
-
}
|
|
135
|
-
});
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const checkMinRequiredVersion_1 = require("../checkMinRequiredVersion");
|
|
4
4
|
const command_1 = require("../command");
|
|
5
|
+
const config_1 = require("../config");
|
|
5
6
|
const planner = require("../deploy/extensions/planner");
|
|
7
|
+
const error_1 = require("../error");
|
|
6
8
|
const export_1 = require("../extensions/export");
|
|
7
9
|
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
10
|
+
const manifest_1 = require("../extensions/manifest");
|
|
8
11
|
const functional_1 = require("../functional");
|
|
9
12
|
const getProjectNumber_1 = require("../getProjectNumber");
|
|
10
13
|
const logger_1 = require("../logger");
|
|
@@ -24,7 +27,7 @@ module.exports = new command_1.Command("ext:export")
|
|
|
24
27
|
const subbed = await (0, export_1.setSecretParamsToLatest)(i);
|
|
25
28
|
return (0, export_1.parameterizeProject)(projectId, projectNumber, subbed);
|
|
26
29
|
}));
|
|
27
|
-
if (have.length
|
|
30
|
+
if (have.length === 0) {
|
|
28
31
|
logger_1.logger.info(`No extension instances installed on ${projectId}, so there is nothing to export.`);
|
|
29
32
|
return;
|
|
30
33
|
}
|
|
@@ -40,5 +43,12 @@ module.exports = new command_1.Command("ext:export")
|
|
|
40
43
|
logger_1.logger.info("Exiting. No changes made.");
|
|
41
44
|
return;
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
const existingConfig = config_1.Config.load(options, true);
|
|
47
|
+
if (!existingConfig) {
|
|
48
|
+
throw new error_1.FirebaseError("Not currently in a Firebase directory. Please run `firebase init` to create a Firebase directory.");
|
|
49
|
+
}
|
|
50
|
+
await (0, manifest_1.writeToManifest)(withRef, existingConfig, {
|
|
51
|
+
nonInteractive: options.nonInteractive,
|
|
52
|
+
force: options.force,
|
|
53
|
+
});
|
|
44
54
|
});
|
|
@@ -30,6 +30,110 @@ const previews_1 = require("../previews");
|
|
|
30
30
|
marked.setOptions({
|
|
31
31
|
renderer: new TerminalRenderer(),
|
|
32
32
|
});
|
|
33
|
+
exports.default = new command_1.Command("ext:install [extensionName]")
|
|
34
|
+
.description("install an official extension if [extensionName] or [extensionName@version] is provided; " +
|
|
35
|
+
(previews_1.previews.extdev
|
|
36
|
+
? "install a local extension if [localPathOrUrl] or [url#root] is provided; install a published extension (not authored by Firebase) if [publisherId/extensionId] is provided "
|
|
37
|
+
: "") +
|
|
38
|
+
"or run with `-i` to see all available extensions.")
|
|
39
|
+
.withForce()
|
|
40
|
+
.option("--params <paramsFile>", "name of params variables file with .env format.")
|
|
41
|
+
.before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.create"])
|
|
42
|
+
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
43
|
+
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
44
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
45
|
+
.action(async (extensionName, options) => {
|
|
46
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
47
|
+
const paramsEnvPath = options.params;
|
|
48
|
+
let learnMore = false;
|
|
49
|
+
if (!extensionName) {
|
|
50
|
+
if (options.interactive) {
|
|
51
|
+
learnMore = true;
|
|
52
|
+
extensionName = await (0, extensionsHelper_1.promptForOfficialExtension)("Which official extension do you wish to install?\n" +
|
|
53
|
+
" Select an extension, then press Enter to learn more.");
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}'. ` +
|
|
57
|
+
`Run ${clc.bold("firebase ext:install -i")} to select from the list of all available published extensions.`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let source;
|
|
61
|
+
let extVersion;
|
|
62
|
+
if ((0, extensionsHelper_1.isLocalOrURLPath)(extensionName)) {
|
|
63
|
+
(0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
|
|
64
|
+
source = await infoInstallBySource(projectId, extensionName);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
(0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
|
|
68
|
+
extVersion = await infoInstallByReference(extensionName, options.interactive);
|
|
69
|
+
}
|
|
70
|
+
if (!(await (0, extensionsHelper_1.confirm)({
|
|
71
|
+
nonInteractive: options.nonInteractive,
|
|
72
|
+
force: options.force,
|
|
73
|
+
default: true,
|
|
74
|
+
}))) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!source && !extVersion) {
|
|
78
|
+
throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
|
|
79
|
+
}
|
|
80
|
+
const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
|
|
81
|
+
if (!spec) {
|
|
82
|
+
throw new error_1.FirebaseError(`Could not find the extension.yaml for extension '${clc.bold(extensionName)}'. Please make sure this is a valid extension and try again.`);
|
|
83
|
+
}
|
|
84
|
+
if (learnMore) {
|
|
85
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `You selected: ${clc.bold(spec.displayName)}.\n` +
|
|
86
|
+
`${spec.description}\n` +
|
|
87
|
+
`View details: https://firebase.google.com/products/extensions/${spec.name}\n`);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return installExtension({
|
|
91
|
+
paramsEnvPath,
|
|
92
|
+
projectId,
|
|
93
|
+
extensionName,
|
|
94
|
+
source,
|
|
95
|
+
extVersion,
|
|
96
|
+
nonInteractive: options.nonInteractive,
|
|
97
|
+
force: options.force,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
if (!(err instanceof error_1.FirebaseError)) {
|
|
102
|
+
throw new error_1.FirebaseError(`Error occurred installing the extension: ${err.message}`, {
|
|
103
|
+
original: err,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
async function infoInstallBySource(projectId, extensionName) {
|
|
110
|
+
let source;
|
|
111
|
+
try {
|
|
112
|
+
source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, extensionName);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}', ` +
|
|
116
|
+
`and encountered the following error when trying to create an instance of extension '${clc.bold(extensionName)}':\n ${err.message}`);
|
|
117
|
+
}
|
|
118
|
+
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
|
|
119
|
+
return source;
|
|
120
|
+
}
|
|
121
|
+
async function infoInstallByReference(extensionName, interactive) {
|
|
122
|
+
if (extensionName.split("/").length < 2) {
|
|
123
|
+
const [extensionID, version] = extensionName.split("@");
|
|
124
|
+
extensionName = `firebase/${extensionID}@${version || "latest"}`;
|
|
125
|
+
}
|
|
126
|
+
const ref = refs.parse(extensionName);
|
|
127
|
+
const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
|
|
128
|
+
if (!ref.version) {
|
|
129
|
+
(0, track_1.track)("Extension Install", "Install by Extension Version Ref", interactive ? 1 : 0);
|
|
130
|
+
extensionName = `${extensionName}@latest`;
|
|
131
|
+
}
|
|
132
|
+
const extVersion = await extensionsApi.getExtensionVersion(extensionName);
|
|
133
|
+
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, ref.publisherId, extVersion.spec, true);
|
|
134
|
+
await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, extVersion);
|
|
135
|
+
return extVersion;
|
|
136
|
+
}
|
|
33
137
|
async function installExtension(options) {
|
|
34
138
|
const { projectId, extensionName, source, extVersion, paramsEnvPath, nonInteractive, force } = options;
|
|
35
139
|
const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
|
|
@@ -162,106 +266,3 @@ async function installExtension(options) {
|
|
|
162
266
|
});
|
|
163
267
|
}
|
|
164
268
|
}
|
|
165
|
-
async function infoInstallBySource(projectId, extensionName) {
|
|
166
|
-
let source;
|
|
167
|
-
try {
|
|
168
|
-
source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, extensionName);
|
|
169
|
-
}
|
|
170
|
-
catch (err) {
|
|
171
|
-
throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}', ` +
|
|
172
|
-
`and encountered the following error when trying to create an instance of extension '${clc.bold(extensionName)}':\n ${err.message}`);
|
|
173
|
-
}
|
|
174
|
-
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
|
|
175
|
-
return source;
|
|
176
|
-
}
|
|
177
|
-
async function infoInstallByReference(extensionName, interactive) {
|
|
178
|
-
if (extensionName.split("/").length < 2) {
|
|
179
|
-
const [extensionID, version] = extensionName.split("@");
|
|
180
|
-
extensionName = `firebase/${extensionID}@${version || "latest"}`;
|
|
181
|
-
}
|
|
182
|
-
const ref = refs.parse(extensionName);
|
|
183
|
-
const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
|
|
184
|
-
if (!ref.version) {
|
|
185
|
-
(0, track_1.track)("Extension Install", "Install by Extension Version Ref", interactive ? 1 : 0);
|
|
186
|
-
extensionName = `${extensionName}@latest`;
|
|
187
|
-
}
|
|
188
|
-
const extVersion = await extensionsApi.getExtensionVersion(extensionName);
|
|
189
|
-
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, ref.publisherId, extVersion.spec, true);
|
|
190
|
-
await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, extVersion);
|
|
191
|
-
return extVersion;
|
|
192
|
-
}
|
|
193
|
-
exports.default = new command_1.Command("ext:install [extensionName]")
|
|
194
|
-
.description("install an official extension if [extensionName] or [extensionName@version] is provided; " +
|
|
195
|
-
(previews_1.previews.extdev
|
|
196
|
-
? "install a local extension if [localPathOrUrl] or [url#root] is provided; install a published extension (not authored by Firebase) if [publisherId/extensionId] is provided "
|
|
197
|
-
: "") +
|
|
198
|
-
"or run with `-i` to see all available extensions.")
|
|
199
|
-
.withForce()
|
|
200
|
-
.option("--params <paramsFile>", "name of params variables file with .env format.")
|
|
201
|
-
.before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.create"])
|
|
202
|
-
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
203
|
-
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
204
|
-
.action(async (extensionName, options) => {
|
|
205
|
-
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
206
|
-
const paramsEnvPath = options.params;
|
|
207
|
-
let learnMore = false;
|
|
208
|
-
if (!extensionName) {
|
|
209
|
-
if (options.interactive) {
|
|
210
|
-
learnMore = true;
|
|
211
|
-
extensionName = await (0, extensionsHelper_1.promptForOfficialExtension)("Which official extension do you wish to install?\n" +
|
|
212
|
-
" Select an extension, then press Enter to learn more.");
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}'. ` +
|
|
216
|
-
`Run ${clc.bold("firebase ext:install -i")} to select from the list of all available published extensions.`);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
let source;
|
|
220
|
-
let extVersion;
|
|
221
|
-
if ((0, extensionsHelper_1.isLocalOrURLPath)(extensionName)) {
|
|
222
|
-
(0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
|
|
223
|
-
source = await infoInstallBySource(projectId, extensionName);
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
(0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
|
|
227
|
-
extVersion = await infoInstallByReference(extensionName, options.interactive);
|
|
228
|
-
}
|
|
229
|
-
if (!(await (0, extensionsHelper_1.confirm)({
|
|
230
|
-
nonInteractive: options.nonInteractive,
|
|
231
|
-
force: options.force,
|
|
232
|
-
default: true,
|
|
233
|
-
}))) {
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
if (!source && !extVersion) {
|
|
237
|
-
throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
|
|
238
|
-
}
|
|
239
|
-
const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
|
|
240
|
-
if (!spec) {
|
|
241
|
-
throw new error_1.FirebaseError(`Could not find the extension.yaml for extension '${clc.bold(extensionName)}'. Please make sure this is a valid extension and try again.`);
|
|
242
|
-
}
|
|
243
|
-
if (learnMore) {
|
|
244
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `You selected: ${clc.bold(spec.displayName)}.\n` +
|
|
245
|
-
`${spec.description}\n` +
|
|
246
|
-
`View details: https://firebase.google.com/products/extensions/${spec.name}\n`);
|
|
247
|
-
}
|
|
248
|
-
try {
|
|
249
|
-
return installExtension({
|
|
250
|
-
paramsEnvPath,
|
|
251
|
-
projectId,
|
|
252
|
-
extensionName,
|
|
253
|
-
source,
|
|
254
|
-
extVersion,
|
|
255
|
-
nonInteractive: options.nonInteractive,
|
|
256
|
-
force: options.force,
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
catch (err) {
|
|
260
|
-
if (!(err instanceof error_1.FirebaseError)) {
|
|
261
|
-
throw new error_1.FirebaseError(`Error occurred installing the extension: ${err.message}`, {
|
|
262
|
-
original: err,
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
throw err;
|
|
266
|
-
}
|
|
267
|
-
});
|
|
@@ -19,20 +19,13 @@ const logger_1 = require("../logger");
|
|
|
19
19
|
marked.setOptions({
|
|
20
20
|
renderer: new TerminalRenderer(),
|
|
21
21
|
});
|
|
22
|
-
function consoleUninstallOnly(projectId, instanceId) {
|
|
23
|
-
const instanceURL = `https://console.firebase.google.com/project/${projectId}/extensions/instances/${instanceId}`;
|
|
24
|
-
const consoleUninstall = "This extension has additional uninstall checks that are not currently supported by the CLI, and can only be uninstalled through the Firebase Console. " +
|
|
25
|
-
`Please visit **[${instanceURL}](${instanceURL})** to uninstall this extension.`;
|
|
26
|
-
logger_1.logger.info("\n");
|
|
27
|
-
utils.logLabeledWarning(extensionsHelper_1.logPrefix, marked(consoleUninstall));
|
|
28
|
-
return Promise.resolve();
|
|
29
|
-
}
|
|
30
22
|
exports.default = new command_1.Command("ext:uninstall <extensionInstanceId>")
|
|
31
23
|
.description("uninstall an extension that is installed in your Firebase project by instance ID")
|
|
32
24
|
.withForce()
|
|
33
25
|
.before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.delete"])
|
|
34
26
|
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
35
27
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
28
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
36
29
|
.action(async (instanceId, options) => {
|
|
37
30
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
38
31
|
let instance;
|
|
@@ -101,3 +94,11 @@ exports.default = new command_1.Command("ext:uninstall <extensionInstanceId>")
|
|
|
101
94
|
}
|
|
102
95
|
utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `uninstalled ${clc.bold(instanceId)}`);
|
|
103
96
|
});
|
|
97
|
+
function consoleUninstallOnly(projectId, instanceId) {
|
|
98
|
+
const instanceURL = `https://console.firebase.google.com/project/${projectId}/extensions/instances/${instanceId}`;
|
|
99
|
+
const consoleUninstall = "This extension has additional uninstall checks that are not currently supported by the CLI, and can only be uninstalled through the Firebase Console. " +
|
|
100
|
+
`Please visit **[${instanceURL}](${instanceURL})** to uninstall this extension.`;
|
|
101
|
+
logger_1.logger.info("\n");
|
|
102
|
+
utils.logLabeledWarning(extensionsHelper_1.logPrefix, marked(consoleUninstall));
|
|
103
|
+
return Promise.resolve();
|
|
104
|
+
}
|