No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

functions-config-export.js 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.command = void 0;
  4. const path = require("path");
  5. const clc = require("colorette");
  6. const requireInteractive_1 = require("../requireInteractive");
  7. const command_1 = require("../command");
  8. const error_1 = require("../error");
  9. const iam_1 = require("../gcp/iam");
  10. const logger_1 = require("../logger");
  11. const prompt_1 = require("../prompt");
  12. const requirePermissions_1 = require("../requirePermissions");
  13. const utils_1 = require("../utils");
  14. const functional_1 = require("../functional");
  15. const configExport = require("../functions/runtimeConfigExport");
  16. const requireConfig_1 = require("../requireConfig");
  17. const projectConfig_1 = require("../functions/projectConfig");
  18. const REQUIRED_PERMISSIONS = [
  19. "runtimeconfig.configs.list",
  20. "runtimeconfig.configs.get",
  21. "runtimeconfig.variables.list",
  22. "runtimeconfig.variables.get",
  23. ];
  24. const RESERVED_PROJECT_ALIAS = ["local"];
  25. const MAX_ATTEMPTS = 3;
  26. function checkReservedAliases(pInfos) {
  27. for (const pInfo of pInfos) {
  28. if (pInfo.alias && RESERVED_PROJECT_ALIAS.includes(pInfo.alias)) {
  29. (0, utils_1.logWarning)(`Project alias (${clc.bold(pInfo.alias)}) is reserved for internal use. ` +
  30. `Saving exported config in .env.${pInfo.projectId} instead.`);
  31. delete pInfo.alias;
  32. }
  33. }
  34. }
  35. async function checkRequiredPermission(pInfos) {
  36. pInfos = pInfos.filter((pInfo) => !pInfo.config);
  37. const testPermissions = pInfos.map((pInfo) => (0, iam_1.testIamPermissions)(pInfo.projectId, REQUIRED_PERMISSIONS));
  38. const results = await Promise.all(testPermissions);
  39. for (const [pInfo, result] of (0, functional_1.zip)(pInfos, results)) {
  40. if (result.passed) {
  41. throw new error_1.FirebaseError(`Unexpectedly failed to fetch runtime config for project ${pInfo.projectId}`);
  42. }
  43. (0, utils_1.logWarning)("You are missing the following permissions to read functions config on project " +
  44. `${clc.bold(pInfo.projectId)}:\n\t${result.missing.join("\n\t")}`);
  45. const confirm = await (0, prompt_1.promptOnce)({
  46. type: "confirm",
  47. name: "skip",
  48. default: true,
  49. message: `Continue without importing configs from project ${pInfo.projectId}?`,
  50. });
  51. if (!confirm) {
  52. throw new error_1.FirebaseError("Command aborted!");
  53. }
  54. }
  55. }
  56. async function promptForPrefix(errMsg) {
  57. (0, utils_1.logWarning)("The following configs keys could not be exported as environment variables:\n");
  58. (0, utils_1.logWarning)(errMsg);
  59. return await (0, prompt_1.promptOnce)({
  60. type: "input",
  61. name: "prefix",
  62. default: "CONFIG_",
  63. message: "Enter a PREFIX to rename invalid environment variable keys:",
  64. }, {});
  65. }
  66. function fromEntries(itr) {
  67. const obj = {};
  68. for (const [k, v] of itr) {
  69. obj[k] = v;
  70. }
  71. return obj;
  72. }
  73. exports.command = new command_1.Command("functions:config:export")
  74. .description("Export environment config as environment variables in dotenv format")
  75. .before(requirePermissions_1.requirePermissions, [
  76. "runtimeconfig.configs.list",
  77. "runtimeconfig.configs.get",
  78. "runtimeconfig.variables.list",
  79. "runtimeconfig.variables.get",
  80. ])
  81. .before(requireConfig_1.requireConfig)
  82. .before(requireInteractive_1.default)
  83. .action(async (options) => {
  84. const config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
  85. const functionsDir = config.source;
  86. let pInfos = configExport.getProjectInfos(options);
  87. checkReservedAliases(pInfos);
  88. (0, utils_1.logBullet)("Importing functions configs from projects [" +
  89. pInfos.map(({ projectId }) => `${clc.bold(projectId)}`).join(", ") +
  90. "]");
  91. await configExport.hydrateConfigs(pInfos);
  92. await checkRequiredPermission(pInfos);
  93. pInfos = pInfos.filter((pInfo) => pInfo.config);
  94. logger_1.logger.debug(`Loaded function configs: ${JSON.stringify(pInfos)}`);
  95. (0, utils_1.logBullet)(`Importing configs from projects: [${pInfos.map((p) => p.projectId).join(", ")}]`);
  96. let attempts = 0;
  97. let prefix = "";
  98. while (true) {
  99. if (attempts >= MAX_ATTEMPTS) {
  100. throw new error_1.FirebaseError("Exceeded max attempts to fix invalid config keys.");
  101. }
  102. const errMsg = configExport.hydrateEnvs(pInfos, prefix);
  103. if (errMsg.length === 0) {
  104. break;
  105. }
  106. prefix = await promptForPrefix(errMsg);
  107. attempts += 1;
  108. }
  109. const header = `# Exported firebase functions:config:export command on ${new Date().toLocaleDateString()}`;
  110. const dotEnvs = pInfos.map((pInfo) => configExport.toDotenvFormat(pInfo.envs, header));
  111. const filenames = pInfos.map(configExport.generateDotenvFilename);
  112. const filesToWrite = fromEntries((0, functional_1.zip)(filenames, dotEnvs));
  113. filesToWrite[".env.local"] = `${header}\n# .env.local file contains environment variables for the Functions Emulator.\n`;
  114. filesToWrite[".env"] = `${header}# .env file contains environment variables that applies to all projects.\n`;
  115. for (const [filename, content] of Object.entries(filesToWrite)) {
  116. await options.config.askWriteProjectFile(path.join(functionsDir, filename), content);
  117. }
  118. });