firebase-tools 10.2.0 → 10.3.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/apiv2.js +3 -0
- package/lib/appdistribution/options-parser-util.js +1 -1
- package/lib/auth.js +3 -3
- 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 +58 -4
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +7 -2
- package/lib/commands/ext-install.js +163 -104
- package/lib/commands/ext-uninstall.js +17 -8
- package/lib/commands/ext-update.js +64 -11
- package/lib/commands/functions-config-clone.js +1 -1
- package/lib/commands/functions-config-export.js +1 -1
- package/lib/commands/hosting-clone.js +3 -3
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/config.js +6 -3
- package/lib/deploy/extensions/deploymentSummary.js +3 -3
- package/lib/deploy/extensions/planner.js +7 -6
- package/lib/deploy/extensions/tasks.js +1 -1
- package/lib/deploy/functions/backend.js +21 -5
- package/lib/deploy/functions/checkIam.js +5 -5
- package/lib/deploy/functions/containerCleaner.js +3 -3
- package/lib/deploy/functions/ensure.js +3 -3
- package/lib/deploy/functions/functionsDeployHelper.js +2 -2
- package/lib/deploy/functions/prepare.js +5 -3
- package/lib/deploy/functions/pricing.js +1 -1
- package/lib/deploy/functions/prompts.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +7 -7
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/planner.js +43 -26
- 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 +22 -12
- package/lib/deploy/functions/runtimes/golang/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/index.js +53 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +52 -15
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
- package/lib/deploy/functions/services/index.js +9 -1
- package/lib/deploy/functions/services/storage.js +10 -4
- package/lib/deploy/functions/triggerRegionHelper.js +1 -1
- package/lib/deploy/functions/validate.js +3 -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 +4 -0
- package/lib/emulator/controller.js +54 -24
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +30 -13
- package/lib/emulator/emulatorLogger.js +12 -1
- package/lib/emulator/extensions/validation.js +70 -0
- package/lib/emulator/extensionsEmulator.js +175 -0
- package/lib/emulator/functionsEmulator.js +106 -44
- package/lib/emulator/functionsEmulatorRuntime.js +44 -36
- package/lib/emulator/functionsEmulatorShared.js +17 -10
- package/lib/emulator/functionsEmulatorShell.js +1 -1
- package/lib/emulator/functionsEmulatorUtils.js +4 -4
- package/lib/emulator/functionsRuntimeWorker.js +2 -2
- 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 +314 -332
- package/lib/emulator/storage/apis/gcloud.js +241 -121
- package/lib/emulator/storage/crc.js +5 -1
- package/lib/emulator/storage/errors.js +9 -0
- package/lib/emulator/storage/files.js +159 -300
- package/lib/emulator/storage/index.js +27 -73
- package/lib/emulator/storage/metadata.js +65 -51
- package/lib/emulator/storage/multipart.js +62 -0
- package/lib/emulator/storage/persistence.js +78 -0
- package/lib/emulator/storage/rules/config.js +33 -0
- package/lib/emulator/storage/rules/manager.js +81 -0
- package/lib/emulator/storage/rules/runtime.js +8 -7
- package/lib/emulator/storage/rules/utils.js +48 -0
- package/lib/emulator/storage/server.js +2 -2
- package/lib/emulator/storage/upload.js +106 -0
- package/lib/emulator/types.js +3 -0
- package/lib/ensureApiEnabled.js +5 -1
- package/lib/error.js +1 -1
- package/lib/extensions/askUserForParam.js +1 -1
- package/lib/extensions/changelog.js +3 -1
- package/lib/extensions/checkProjectBilling.js +1 -1
- package/lib/extensions/displayExtensionInfo.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +56 -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 +32 -19
- package/lib/extensions/listExtensions.js +2 -0
- package/lib/extensions/manifest.js +144 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +9 -8
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/secretsUtils.js +3 -3
- package/lib/functional.js +1 -1
- package/lib/functions/env.js +6 -7
- package/lib/functions/events/v2.js +11 -0
- package/lib/gcp/cloudfunctions.js +42 -11
- package/lib/gcp/cloudfunctionsv2.js +48 -17
- package/lib/gcp/cloudtasks.js +1 -1
- package/lib/gcp/docker.js +2 -2
- package/lib/gcp/resourceManager.js +4 -4
- package/lib/gcp/run.js +2 -2
- 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 -1
- package/lib/serve/hosting.js +1 -1
- package/lib/utils.js +15 -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/deploy/extensions/params.js +0 -39
- package/lib/deploy/functions/eventTypes.js +0 -10
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
|
@@ -291,7 +291,7 @@ async function loginRemotely(userHint) {
|
|
|
291
291
|
});
|
|
292
292
|
try {
|
|
293
293
|
const tokens = await getTokensFromAuthorizationCode(code, `${api.authProxyOrigin}/complete`, codeVerifier);
|
|
294
|
-
(0, track_1.track)("login", "google_remote");
|
|
294
|
+
void (0, track_1.track)("login", "google_remote");
|
|
295
295
|
return {
|
|
296
296
|
user: jwt.decode(tokens.id_token),
|
|
297
297
|
tokens: tokens,
|
|
@@ -307,7 +307,7 @@ async function loginWithLocalhostGoogle(port, userHint) {
|
|
|
307
307
|
const authUrl = getLoginUrl(callbackUrl, userHint);
|
|
308
308
|
const successTemplate = "../templates/loginSuccess.html";
|
|
309
309
|
const tokens = await loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getTokensFromAuthorizationCode);
|
|
310
|
-
(0, track_1.track)("login", "google_localhost");
|
|
310
|
+
void (0, track_1.track)("login", "google_localhost");
|
|
311
311
|
return {
|
|
312
312
|
user: jwt.decode(tokens.id_token),
|
|
313
313
|
tokens: tokens,
|
|
@@ -319,7 +319,7 @@ async function loginWithLocalhostGitHub(port) {
|
|
|
319
319
|
const authUrl = getGithubLoginUrl(callbackUrl);
|
|
320
320
|
const successTemplate = "../templates/loginSuccessGithub.html";
|
|
321
321
|
const tokens = await loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getGithubTokensFromAuthorizationCode);
|
|
322
|
-
(0, track_1.track)("login", "google_localhost");
|
|
322
|
+
void (0, track_1.track)("login", "google_localhost");
|
|
323
323
|
return tokens;
|
|
324
324
|
}
|
|
325
325
|
async function loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getTokens) {
|
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
|
}
|
|
@@ -15,6 +15,9 @@ const paramHelper = require("../extensions/paramHelper");
|
|
|
15
15
|
const requirePermissions_1 = require("../requirePermissions");
|
|
16
16
|
const utils = require("../utils");
|
|
17
17
|
const logger_1 = require("../logger");
|
|
18
|
+
const refs = require("../extensions/refs");
|
|
19
|
+
const manifest = require("../extensions/manifest");
|
|
20
|
+
const functional_1 = require("../functional");
|
|
18
21
|
marked.setOptions({
|
|
19
22
|
renderer: new TerminalRenderer(),
|
|
20
23
|
});
|
|
@@ -22,6 +25,7 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
22
25
|
.description("configure an existing extension instance")
|
|
23
26
|
.withForce()
|
|
24
27
|
.option("--params <paramsFile>", "path of params file with .env format.")
|
|
28
|
+
.option("--local", "save to firebase.json rather than directly install to a Firebase project")
|
|
25
29
|
.before(requirePermissions_1.requirePermissions, [
|
|
26
30
|
"firebaseextensions.instances.update",
|
|
27
31
|
"firebaseextensions.instances.get",
|
|
@@ -29,9 +33,46 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
29
33
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
30
34
|
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
31
35
|
.action(async (instanceId, options) => {
|
|
36
|
+
var _a;
|
|
37
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
38
|
+
if (options.local) {
|
|
39
|
+
if (options.nonInteractive) {
|
|
40
|
+
throw new error_1.FirebaseError(`Command not supported in non-interactive mode, edit ./extensions/${instanceId}.env directly instead`);
|
|
41
|
+
}
|
|
42
|
+
const config = manifest.loadConfig(options);
|
|
43
|
+
const targetRef = manifest.getInstanceRef(instanceId, config);
|
|
44
|
+
const extensionVersion = await extensionsApi.getExtensionVersion(refs.toExtensionVersionRef(targetRef));
|
|
45
|
+
const oldParamValues = manifest.readInstanceParam({
|
|
46
|
+
instanceId,
|
|
47
|
+
projectDir: config.projectDir,
|
|
48
|
+
});
|
|
49
|
+
const [immutableParams, tbdParams] = (0, functional_1.partition)(extensionVersion.spec.params, (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
|
|
50
|
+
infoImmutableParams(immutableParams, oldParamValues);
|
|
51
|
+
paramHelper.setNewDefaults(tbdParams, oldParamValues);
|
|
52
|
+
const mutableParamsValues = await paramHelper.getParams({
|
|
53
|
+
projectId,
|
|
54
|
+
paramSpecs: tbdParams,
|
|
55
|
+
nonInteractive: false,
|
|
56
|
+
paramsEnvPath: ((_a = options.params) !== null && _a !== void 0 ? _a : ""),
|
|
57
|
+
instanceId,
|
|
58
|
+
reconfiguring: true,
|
|
59
|
+
});
|
|
60
|
+
const newParamValues = Object.assign(Object.assign({}, oldParamValues), mutableParamsValues);
|
|
61
|
+
await manifest.writeToManifest([
|
|
62
|
+
{
|
|
63
|
+
instanceId,
|
|
64
|
+
ref: targetRef,
|
|
65
|
+
params: newParamValues,
|
|
66
|
+
},
|
|
67
|
+
], config, {
|
|
68
|
+
nonInteractive: false,
|
|
69
|
+
force: true,
|
|
70
|
+
});
|
|
71
|
+
manifest.showPreviewWarning();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
32
74
|
const spinner = ora(`Configuring ${clc.bold(instanceId)}. This usually takes 3 to 5 minutes...`);
|
|
33
75
|
try {
|
|
34
|
-
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
35
76
|
let existingInstance;
|
|
36
77
|
try {
|
|
37
78
|
existingInstance = await extensionsApi.getInstance(projectId, instanceId);
|
|
@@ -45,9 +86,7 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
45
86
|
throw err;
|
|
46
87
|
}
|
|
47
88
|
const paramSpecWithNewDefaults = paramHelper.getParamsWithCurrentValuesAsDefaults(existingInstance);
|
|
48
|
-
const immutableParams = _.remove(paramSpecWithNewDefaults, (param) =>
|
|
49
|
-
return param.immutable || param.param === "LOCATION";
|
|
50
|
-
});
|
|
89
|
+
const immutableParams = _.remove(paramSpecWithNewDefaults, (param) => param.immutable);
|
|
51
90
|
const params = await paramHelper.getParams({
|
|
52
91
|
projectId,
|
|
53
92
|
paramSpecs: paramSpecWithNewDefaults,
|
|
@@ -74,6 +113,7 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
74
113
|
spinner.stop();
|
|
75
114
|
utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `successfully configured ${clc.bold(instanceId)}.`);
|
|
76
115
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`You can view your reconfigured instance in the Firebase console: ${utils.consoleUrl(projectId, `/extensions/instances/${instanceId}?tab=config`)}`));
|
|
116
|
+
manifest.showDeprecationWarning();
|
|
77
117
|
return res;
|
|
78
118
|
}
|
|
79
119
|
catch (err) {
|
|
@@ -88,3 +128,17 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
88
128
|
throw err;
|
|
89
129
|
}
|
|
90
130
|
});
|
|
131
|
+
function infoImmutableParams(immutableParams, paramValues) {
|
|
132
|
+
if (!immutableParams.length) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const plural = immutableParams.length > 1;
|
|
136
|
+
utils.logLabeledWarning(extensionsHelper_1.logPrefix, marked(`The following param${plural ? "s are" : " is"} immutable and won't be changed:`));
|
|
137
|
+
for (const { param } of immutableParams) {
|
|
138
|
+
logger_1.logger.info(`param: ${param}, value: ${paramValues[param]}`);
|
|
139
|
+
}
|
|
140
|
+
logger_1.logger.info((plural
|
|
141
|
+
? "To set different values for these params"
|
|
142
|
+
: "To set a different value for this param") +
|
|
143
|
+
", uninstall the extension, then install a new instance of this extension.");
|
|
144
|
+
}
|
|
@@ -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
|
-
});
|
|
@@ -5,6 +5,7 @@ const command_1 = require("../command");
|
|
|
5
5
|
const planner = require("../deploy/extensions/planner");
|
|
6
6
|
const export_1 = require("../extensions/export");
|
|
7
7
|
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
8
|
+
const manifest = require("../extensions/manifest");
|
|
8
9
|
const functional_1 = require("../functional");
|
|
9
10
|
const getProjectNumber_1 = require("../getProjectNumber");
|
|
10
11
|
const logger_1 = require("../logger");
|
|
@@ -24,7 +25,7 @@ module.exports = new command_1.Command("ext:export")
|
|
|
24
25
|
const subbed = await (0, export_1.setSecretParamsToLatest)(i);
|
|
25
26
|
return (0, export_1.parameterizeProject)(projectId, projectNumber, subbed);
|
|
26
27
|
}));
|
|
27
|
-
if (have.length
|
|
28
|
+
if (have.length === 0) {
|
|
28
29
|
logger_1.logger.info(`No extension instances installed on ${projectId}, so there is nothing to export.`);
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
@@ -40,5 +41,9 @@ module.exports = new command_1.Command("ext:export")
|
|
|
40
41
|
logger_1.logger.info("Exiting. No changes made.");
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
const existingConfig = manifest.loadConfig(options);
|
|
45
|
+
await manifest.writeToManifest(withRef, existingConfig, {
|
|
46
|
+
nonInteractive: options.nonInteractive,
|
|
47
|
+
force: options.force,
|
|
48
|
+
}, true);
|
|
44
49
|
});
|
|
@@ -27,9 +27,171 @@ const utils = require("../utils");
|
|
|
27
27
|
const track_1 = require("../track");
|
|
28
28
|
const logger_1 = require("../logger");
|
|
29
29
|
const previews_1 = require("../previews");
|
|
30
|
+
const manifest = require("../extensions/manifest");
|
|
30
31
|
marked.setOptions({
|
|
31
32
|
renderer: new TerminalRenderer(),
|
|
32
33
|
});
|
|
34
|
+
exports.default = new command_1.Command("ext:install [extensionName]")
|
|
35
|
+
.description("install an official extension if [extensionName] or [extensionName@version] is provided; " +
|
|
36
|
+
(previews_1.previews.extdev
|
|
37
|
+
? "install a local extension if [localPathOrUrl] or [url#root] is provided; install a published extension (not authored by Firebase) if [publisherId/extensionId] is provided "
|
|
38
|
+
: "") +
|
|
39
|
+
"or run with `-i` to see all available extensions.")
|
|
40
|
+
.withForce()
|
|
41
|
+
.option("--params <paramsFile>", "name of params variables file with .env format.")
|
|
42
|
+
.option("--local", "save to firebase.json rather than directly install to a Firebase project")
|
|
43
|
+
.before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.create"])
|
|
44
|
+
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
45
|
+
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
46
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
47
|
+
.action(async (extensionName, options) => {
|
|
48
|
+
var _a;
|
|
49
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
50
|
+
const paramsEnvPath = ((_a = options.params) !== null && _a !== void 0 ? _a : "");
|
|
51
|
+
let learnMore = false;
|
|
52
|
+
if (!extensionName) {
|
|
53
|
+
if (options.interactive) {
|
|
54
|
+
learnMore = true;
|
|
55
|
+
extensionName = await (0, extensionsHelper_1.promptForOfficialExtension)("Which official extension do you wish to install?\n" +
|
|
56
|
+
" Select an extension, then press Enter to learn more.");
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}'. ` +
|
|
60
|
+
`Run ${clc.bold("firebase ext:install -i")} to select from the list of all available published extensions.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let source;
|
|
64
|
+
let extVersion;
|
|
65
|
+
if ((0, extensionsHelper_1.isUrlPath)(extensionName)) {
|
|
66
|
+
void (0, track_1.track)("Extension Install", "Install by url path", options.interactive ? 1 : 0);
|
|
67
|
+
}
|
|
68
|
+
if ((0, extensionsHelper_1.isLocalOrURLPath)(extensionName)) {
|
|
69
|
+
void (0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
|
|
70
|
+
if (options.local) {
|
|
71
|
+
throw new error_1.FirebaseError("Installing a local source locally is not supported yet, please use ext:dev:emulator commands");
|
|
72
|
+
}
|
|
73
|
+
source = await infoInstallBySource(projectId, extensionName);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
void (0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
|
|
77
|
+
extVersion = await infoInstallByReference(extensionName, options.interactive);
|
|
78
|
+
}
|
|
79
|
+
if (!(await (0, extensionsHelper_1.confirm)({
|
|
80
|
+
nonInteractive: options.nonInteractive,
|
|
81
|
+
force: options.force,
|
|
82
|
+
default: true,
|
|
83
|
+
}))) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!source && !extVersion) {
|
|
87
|
+
throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
|
|
88
|
+
}
|
|
89
|
+
const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
|
|
90
|
+
if (!spec) {
|
|
91
|
+
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.`);
|
|
92
|
+
}
|
|
93
|
+
if (learnMore) {
|
|
94
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `You selected: ${clc.bold(spec.displayName)}.\n` +
|
|
95
|
+
`${spec.description}\n` +
|
|
96
|
+
`View details: https://firebase.google.com/products/extensions/${spec.name}\n`);
|
|
97
|
+
}
|
|
98
|
+
if (options.local) {
|
|
99
|
+
try {
|
|
100
|
+
return installToManifest({
|
|
101
|
+
paramsEnvPath,
|
|
102
|
+
projectId,
|
|
103
|
+
extensionName,
|
|
104
|
+
source,
|
|
105
|
+
extVersion,
|
|
106
|
+
nonInteractive: options.nonInteractive,
|
|
107
|
+
force: options.force,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (!(err instanceof error_1.FirebaseError)) {
|
|
112
|
+
throw new error_1.FirebaseError(`Error occurred saving the extension to manifest: ${err.message}`, {
|
|
113
|
+
original: err,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
return installExtension({
|
|
121
|
+
paramsEnvPath,
|
|
122
|
+
projectId,
|
|
123
|
+
extensionName,
|
|
124
|
+
source,
|
|
125
|
+
extVersion,
|
|
126
|
+
nonInteractive: options.nonInteractive,
|
|
127
|
+
force: options.force,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (!(err instanceof error_1.FirebaseError)) {
|
|
132
|
+
throw new error_1.FirebaseError(`Error occurred installing the extension: ${err.message}`, {
|
|
133
|
+
original: err,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
async function infoInstallBySource(projectId, extensionName) {
|
|
140
|
+
let source;
|
|
141
|
+
try {
|
|
142
|
+
source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, extensionName);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}', ` +
|
|
146
|
+
`and encountered the following error when trying to create an instance of extension '${clc.bold(extensionName)}':\n ${err.message}`);
|
|
147
|
+
}
|
|
148
|
+
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
|
|
149
|
+
return source;
|
|
150
|
+
}
|
|
151
|
+
async function infoInstallByReference(extensionName, interactive) {
|
|
152
|
+
if (extensionName.split("/").length < 2) {
|
|
153
|
+
const [extensionID, version] = extensionName.split("@");
|
|
154
|
+
extensionName = `firebase/${extensionID}@${version || "latest"}`;
|
|
155
|
+
}
|
|
156
|
+
const ref = refs.parse(extensionName);
|
|
157
|
+
const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
|
|
158
|
+
if (!ref.version) {
|
|
159
|
+
void (0, track_1.track)("Extension Install", "Install by Extension Version Ref", interactive ? 1 : 0);
|
|
160
|
+
extensionName = `${extensionName}@latest`;
|
|
161
|
+
}
|
|
162
|
+
const extVersion = await extensionsApi.getExtensionVersion(extensionName);
|
|
163
|
+
(0, displayExtensionInfo_1.displayExtInfo)(extensionName, ref.publisherId, extVersion.spec, true);
|
|
164
|
+
await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, extVersion);
|
|
165
|
+
return extVersion;
|
|
166
|
+
}
|
|
167
|
+
async function installToManifest(options) {
|
|
168
|
+
const { projectId, extensionName, extVersion, paramsEnvPath, nonInteractive, force } = options;
|
|
169
|
+
const spec = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec;
|
|
170
|
+
if (!spec) {
|
|
171
|
+
throw new error_1.FirebaseError(`Could not find the extension.yaml for ${extensionName}. Please make sure this is a valid extension and try again.`);
|
|
172
|
+
}
|
|
173
|
+
const config = manifest.loadConfig(options);
|
|
174
|
+
let instanceId = spec.name;
|
|
175
|
+
while (manifest.instanceExists(instanceId, config)) {
|
|
176
|
+
instanceId = await (0, extensionsHelper_1.promptForValidInstanceId)(`${spec.name}-${(0, utils_1.getRandomString)(4)}`);
|
|
177
|
+
}
|
|
178
|
+
const params = await paramHelper.getParams({
|
|
179
|
+
projectId,
|
|
180
|
+
paramSpecs: spec.params,
|
|
181
|
+
nonInteractive,
|
|
182
|
+
paramsEnvPath,
|
|
183
|
+
instanceId,
|
|
184
|
+
});
|
|
185
|
+
const ref = refs.parse(extVersion.ref);
|
|
186
|
+
await manifest.writeToManifest([
|
|
187
|
+
{
|
|
188
|
+
instanceId,
|
|
189
|
+
ref,
|
|
190
|
+
params,
|
|
191
|
+
},
|
|
192
|
+
], config, { nonInteractive, force: force !== null && force !== void 0 ? force : false });
|
|
193
|
+
manifest.showPreviewWarning();
|
|
194
|
+
}
|
|
33
195
|
async function installExtension(options) {
|
|
34
196
|
const { projectId, extensionName, source, extVersion, paramsEnvPath, nonInteractive, force } = options;
|
|
35
197
|
const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
|
|
@@ -149,6 +311,7 @@ async function installExtension(options) {
|
|
|
149
311
|
`which may include some required post-installation tasks: ${utils.consoleUrl(projectId, `/extensions/instances/${instanceId}?tab=usage`)}`));
|
|
150
312
|
logger_1.logger.info(marked("You can run `firebase ext` to view available Firebase Extensions commands, " +
|
|
151
313
|
"including those to update, reconfigure, or delete your installed extension."));
|
|
314
|
+
manifest.showDeprecationWarning();
|
|
152
315
|
}
|
|
153
316
|
catch (err) {
|
|
154
317
|
if (spinner.isSpinning) {
|
|
@@ -162,107 +325,3 @@ async function installExtension(options) {
|
|
|
162
325
|
});
|
|
163
326
|
}
|
|
164
327
|
}
|
|
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
|
-
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
205
|
-
.action(async (extensionName, options) => {
|
|
206
|
-
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
207
|
-
const paramsEnvPath = options.params;
|
|
208
|
-
let learnMore = false;
|
|
209
|
-
if (!extensionName) {
|
|
210
|
-
if (options.interactive) {
|
|
211
|
-
learnMore = true;
|
|
212
|
-
extensionName = await (0, extensionsHelper_1.promptForOfficialExtension)("Which official extension do you wish to install?\n" +
|
|
213
|
-
" Select an extension, then press Enter to learn more.");
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}'. ` +
|
|
217
|
-
`Run ${clc.bold("firebase ext:install -i")} to select from the list of all available published extensions.`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
let source;
|
|
221
|
-
let extVersion;
|
|
222
|
-
if ((0, extensionsHelper_1.isLocalOrURLPath)(extensionName)) {
|
|
223
|
-
(0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
|
|
224
|
-
source = await infoInstallBySource(projectId, extensionName);
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
(0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
|
|
228
|
-
extVersion = await infoInstallByReference(extensionName, options.interactive);
|
|
229
|
-
}
|
|
230
|
-
if (!(await (0, extensionsHelper_1.confirm)({
|
|
231
|
-
nonInteractive: options.nonInteractive,
|
|
232
|
-
force: options.force,
|
|
233
|
-
default: true,
|
|
234
|
-
}))) {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (!source && !extVersion) {
|
|
238
|
-
throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
|
|
239
|
-
}
|
|
240
|
-
const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
|
|
241
|
-
if (!spec) {
|
|
242
|
-
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.`);
|
|
243
|
-
}
|
|
244
|
-
if (learnMore) {
|
|
245
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `You selected: ${clc.bold(spec.displayName)}.\n` +
|
|
246
|
-
`${spec.description}\n` +
|
|
247
|
-
`View details: https://firebase.google.com/products/extensions/${spec.name}\n`);
|
|
248
|
-
}
|
|
249
|
-
try {
|
|
250
|
-
return installExtension({
|
|
251
|
-
paramsEnvPath,
|
|
252
|
-
projectId,
|
|
253
|
-
extensionName,
|
|
254
|
-
source,
|
|
255
|
-
extVersion,
|
|
256
|
-
nonInteractive: options.nonInteractive,
|
|
257
|
-
force: options.force,
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
catch (err) {
|
|
261
|
-
if (!(err instanceof error_1.FirebaseError)) {
|
|
262
|
-
throw new error_1.FirebaseError(`Error occurred installing the extension: ${err.message}`, {
|
|
263
|
-
original: err,
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
throw err;
|
|
267
|
-
}
|
|
268
|
-
});
|