"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.command = void 0; const path = require("path"); const clc = require("colorette"); const requireInteractive_1 = require("../requireInteractive"); const command_1 = require("../command"); const error_1 = require("../error"); const iam_1 = require("../gcp/iam"); const logger_1 = require("../logger"); const prompt_1 = require("../prompt"); const requirePermissions_1 = require("../requirePermissions"); const utils_1 = require("../utils"); const functional_1 = require("../functional"); const configExport = require("../functions/runtimeConfigExport"); const requireConfig_1 = require("../requireConfig"); const projectConfig_1 = require("../functions/projectConfig"); const REQUIRED_PERMISSIONS = [ "runtimeconfig.configs.list", "runtimeconfig.configs.get", "runtimeconfig.variables.list", "runtimeconfig.variables.get", ]; const RESERVED_PROJECT_ALIAS = ["local"]; const MAX_ATTEMPTS = 3; function checkReservedAliases(pInfos) { for (const pInfo of pInfos) { if (pInfo.alias && RESERVED_PROJECT_ALIAS.includes(pInfo.alias)) { (0, utils_1.logWarning)(`Project alias (${clc.bold(pInfo.alias)}) is reserved for internal use. ` + `Saving exported config in .env.${pInfo.projectId} instead.`); delete pInfo.alias; } } } async function checkRequiredPermission(pInfos) { pInfos = pInfos.filter((pInfo) => !pInfo.config); const testPermissions = pInfos.map((pInfo) => (0, iam_1.testIamPermissions)(pInfo.projectId, REQUIRED_PERMISSIONS)); const results = await Promise.all(testPermissions); for (const [pInfo, result] of (0, functional_1.zip)(pInfos, results)) { if (result.passed) { throw new error_1.FirebaseError(`Unexpectedly failed to fetch runtime config for project ${pInfo.projectId}`); } (0, utils_1.logWarning)("You are missing the following permissions to read functions config on project " + `${clc.bold(pInfo.projectId)}:\n\t${result.missing.join("\n\t")}`); const confirm = await (0, prompt_1.promptOnce)({ type: "confirm", name: "skip", default: true, message: `Continue without importing configs from project ${pInfo.projectId}?`, }); if (!confirm) { throw new error_1.FirebaseError("Command aborted!"); } } } async function promptForPrefix(errMsg) { (0, utils_1.logWarning)("The following configs keys could not be exported as environment variables:\n"); (0, utils_1.logWarning)(errMsg); return await (0, prompt_1.promptOnce)({ type: "input", name: "prefix", default: "CONFIG_", message: "Enter a PREFIX to rename invalid environment variable keys:", }, {}); } function fromEntries(itr) { const obj = {}; for (const [k, v] of itr) { obj[k] = v; } return obj; } exports.command = new command_1.Command("functions:config:export") .description("Export environment config as environment variables in dotenv format") .before(requirePermissions_1.requirePermissions, [ "runtimeconfig.configs.list", "runtimeconfig.configs.get", "runtimeconfig.variables.list", "runtimeconfig.variables.get", ]) .before(requireConfig_1.requireConfig) .before(requireInteractive_1.default) .action(async (options) => { const config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0]; const functionsDir = config.source; let pInfos = configExport.getProjectInfos(options); checkReservedAliases(pInfos); (0, utils_1.logBullet)("Importing functions configs from projects [" + pInfos.map(({ projectId }) => `${clc.bold(projectId)}`).join(", ") + "]"); await configExport.hydrateConfigs(pInfos); await checkRequiredPermission(pInfos); pInfos = pInfos.filter((pInfo) => pInfo.config); logger_1.logger.debug(`Loaded function configs: ${JSON.stringify(pInfos)}`); (0, utils_1.logBullet)(`Importing configs from projects: [${pInfos.map((p) => p.projectId).join(", ")}]`); let attempts = 0; let prefix = ""; while (true) { if (attempts >= MAX_ATTEMPTS) { throw new error_1.FirebaseError("Exceeded max attempts to fix invalid config keys."); } const errMsg = configExport.hydrateEnvs(pInfos, prefix); if (errMsg.length === 0) { break; } prefix = await promptForPrefix(errMsg); attempts += 1; } const header = `# Exported firebase functions:config:export command on ${new Date().toLocaleDateString()}`; const dotEnvs = pInfos.map((pInfo) => configExport.toDotenvFormat(pInfo.envs, header)); const filenames = pInfos.map(configExport.generateDotenvFilename); const filesToWrite = fromEntries((0, functional_1.zip)(filenames, dotEnvs)); filesToWrite[".env.local"] = `${header}\n# .env.local file contains environment variables for the Functions Emulator.\n`; filesToWrite[".env"] = `${header}# .env file contains environment variables that applies to all projects.\n`; for (const [filename, content] of Object.entries(filesToWrite)) { await options.config.askWriteProjectFile(path.join(functionsDir, filename), content); } });