forked from dienianindya/gsi_ess_mobile
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.
246 lines
8.9 KiB
246 lines
8.9 KiB
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.checkForDuplicateKeys = exports.writeUserEnvs = exports.hasUserEnvs = exports.parseStrict = exports.validateKey = exports.KeyValidationError = exports.parse = void 0;
|
|
const clc = require("colorette");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const error_1 = require("../error");
|
|
const logger_1 = require("../logger");
|
|
const utils_1 = require("../utils");
|
|
const FUNCTIONS_EMULATOR_DOTENV = ".env.local";
|
|
const RESERVED_PREFIXES = ["X_GOOGLE_", "FIREBASE_", "EXT_"];
|
|
const RESERVED_KEYS = [
|
|
"FIREBASE_CONFIG",
|
|
"CLOUD_RUNTIME_CONFIG",
|
|
"EVENTARC_CLOUD_EVENT_SOURCE",
|
|
"ENTRY_POINT",
|
|
"GCP_PROJECT",
|
|
"GCLOUD_PROJECT",
|
|
"GOOGLE_CLOUD_PROJECT",
|
|
"FUNCTION_TRIGGER_TYPE",
|
|
"FUNCTION_NAME",
|
|
"FUNCTION_MEMORY_MB",
|
|
"FUNCTION_TIMEOUT_SEC",
|
|
"FUNCTION_IDENTITY",
|
|
"FUNCTION_REGION",
|
|
"FUNCTION_TARGET",
|
|
"FUNCTION_SIGNATURE_TYPE",
|
|
"K_SERVICE",
|
|
"K_REVISION",
|
|
"PORT",
|
|
"K_CONFIGURATION",
|
|
];
|
|
const LINE_RE = new RegExp("^" +
|
|
"\\s*" +
|
|
"([\\w./]+)" +
|
|
"\\s*=[\\f\\t\\v]*" +
|
|
"(" +
|
|
"\\s*'(?:\\\\'|[^'])*'|" +
|
|
'\\s*"(?:\\\\"|[^"])*"|' +
|
|
"[^#\\r\\n]*" +
|
|
")?" +
|
|
"\\s*" +
|
|
"(?:#[^\\n]*)?" +
|
|
"$", "gms");
|
|
const ESCAPE_SEQUENCES_TO_CHARACTERS = {
|
|
"\\n": "\n",
|
|
"\\r": "\r",
|
|
"\\t": "\t",
|
|
"\\v": "\v",
|
|
"\\\\": "\\",
|
|
"\\'": "'",
|
|
'\\"': '"',
|
|
};
|
|
const ALL_ESCAPE_SEQUENCES_RE = /\\[nrtv\\'"]/g;
|
|
const CHARACTERS_TO_ESCAPE_SEQUENCES = {
|
|
"\n": "\\n",
|
|
"\r": "\\r",
|
|
"\t": "\\t",
|
|
"\v": "\\v",
|
|
"\\": "\\\\",
|
|
"'": "\\'",
|
|
'"': '\\"',
|
|
};
|
|
const ALL_ESCAPABLE_CHARACTERS_RE = /[\n\r\t\v\\'"]/g;
|
|
function parse(data) {
|
|
const envs = {};
|
|
const errors = [];
|
|
data = data.replace(/\r\n?/, "\n");
|
|
let match;
|
|
while ((match = LINE_RE.exec(data))) {
|
|
let [, k, v] = match;
|
|
v = (v || "").trim();
|
|
let quotesMatch;
|
|
if ((quotesMatch = /^(["'])(.*)\1$/ms.exec(v)) != null) {
|
|
v = quotesMatch[2];
|
|
if (quotesMatch[1] === '"') {
|
|
v = v.replace(ALL_ESCAPE_SEQUENCES_RE, (match) => ESCAPE_SEQUENCES_TO_CHARACTERS[match]);
|
|
}
|
|
}
|
|
envs[k] = v;
|
|
}
|
|
const nonmatches = data.replace(LINE_RE, "");
|
|
for (let line of nonmatches.split(/[\r\n]+/)) {
|
|
line = line.trim();
|
|
if (line.startsWith("#")) {
|
|
continue;
|
|
}
|
|
if (line.length)
|
|
errors.push(line);
|
|
}
|
|
return { envs, errors };
|
|
}
|
|
exports.parse = parse;
|
|
class KeyValidationError extends Error {
|
|
constructor(key, message) {
|
|
super(`Failed to validate key ${key}: ${message}`);
|
|
this.key = key;
|
|
this.message = message;
|
|
}
|
|
}
|
|
exports.KeyValidationError = KeyValidationError;
|
|
function validateKey(key) {
|
|
if (RESERVED_KEYS.includes(key)) {
|
|
throw new KeyValidationError(key, `Key ${key} is reserved for internal use.`);
|
|
}
|
|
if (!/^[A-Z_][A-Z0-9_]*$/.test(key)) {
|
|
throw new KeyValidationError(key, `Key ${key} must start with an uppercase ASCII letter or underscore` +
|
|
", and then consist of uppercase ASCII letters, digits, and underscores.");
|
|
}
|
|
if (RESERVED_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
|
throw new KeyValidationError(key, `Key ${key} starts with a reserved prefix (${RESERVED_PREFIXES.join(" ")})`);
|
|
}
|
|
}
|
|
exports.validateKey = validateKey;
|
|
function parseStrict(data) {
|
|
const { envs, errors } = parse(data);
|
|
if (errors.length) {
|
|
throw new error_1.FirebaseError(`Invalid dotenv file, error on lines: ${errors.join(",")}`);
|
|
}
|
|
const validationErrors = [];
|
|
for (const key of Object.keys(envs)) {
|
|
try {
|
|
validateKey(key);
|
|
}
|
|
catch (err) {
|
|
logger_1.logger.debug(`Failed to validate key ${key}: ${err}`);
|
|
if (err instanceof KeyValidationError) {
|
|
validationErrors.push(err);
|
|
}
|
|
else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
if (validationErrors.length > 0) {
|
|
throw new error_1.FirebaseError("Validation failed", { children: validationErrors });
|
|
}
|
|
return envs;
|
|
}
|
|
exports.parseStrict = parseStrict;
|
|
function findEnvfiles(functionsSource, projectId, projectAlias, isEmulator) {
|
|
const files = [".env"];
|
|
files.push(`.env.${projectId}`);
|
|
if (projectAlias) {
|
|
files.push(`.env.${projectAlias}`);
|
|
}
|
|
if (isEmulator) {
|
|
files.push(FUNCTIONS_EMULATOR_DOTENV);
|
|
}
|
|
return files
|
|
.map((f) => path.join(functionsSource, f))
|
|
.filter(fs.existsSync)
|
|
.map((p) => path.basename(p));
|
|
}
|
|
function hasUserEnvs({ functionsSource, projectId, projectAlias, isEmulator, }) {
|
|
return findEnvfiles(functionsSource, projectId, projectAlias, isEmulator).length > 0;
|
|
}
|
|
exports.hasUserEnvs = hasUserEnvs;
|
|
function writeUserEnvs(toWrite, envOpts) {
|
|
if (Object.keys(toWrite).length === 0) {
|
|
return;
|
|
}
|
|
const { functionsSource, projectId, projectAlias, isEmulator } = envOpts;
|
|
const allEnvFiles = findEnvfiles(functionsSource, projectId, projectAlias, isEmulator);
|
|
const targetEnvFile = envOpts.isEmulator
|
|
? FUNCTIONS_EMULATOR_DOTENV
|
|
: `.env.${envOpts.projectId}`;
|
|
const targetEnvFileExists = allEnvFiles.includes(targetEnvFile);
|
|
if (!targetEnvFileExists) {
|
|
fs.writeFileSync(path.join(envOpts.functionsSource, targetEnvFile), "", { flag: "wx" });
|
|
(0, utils_1.logBullet)(clc.yellow(clc.bold("functions: ")) +
|
|
`Created new local file ${targetEnvFile} to store param values. We suggest explicitly adding or excluding this file from version control.`);
|
|
}
|
|
const fullEnvs = loadUserEnvs(envOpts);
|
|
const prodEnvs = isEmulator
|
|
? loadUserEnvs(Object.assign(Object.assign({}, envOpts), { isEmulator: false }))
|
|
: loadUserEnvs(envOpts);
|
|
checkForDuplicateKeys(isEmulator || false, Object.keys(toWrite), fullEnvs, prodEnvs);
|
|
for (const k of Object.keys(toWrite)) {
|
|
validateKey(k);
|
|
}
|
|
(0, utils_1.logBullet)(clc.cyan(clc.bold("functions: ")) + `Writing new parameter values to disk: ${targetEnvFile}`);
|
|
let lines = "";
|
|
for (const k of Object.keys(toWrite)) {
|
|
lines += formatUserEnvForWrite(k, toWrite[k]);
|
|
}
|
|
fs.appendFileSync(path.join(functionsSource, targetEnvFile), lines);
|
|
}
|
|
exports.writeUserEnvs = writeUserEnvs;
|
|
function checkForDuplicateKeys(isEmulator, keys, fullEnv, envsWithoutLocal) {
|
|
for (const key of keys) {
|
|
const definedInEnv = fullEnv.hasOwnProperty(key);
|
|
if (definedInEnv) {
|
|
if (envsWithoutLocal && isEmulator && envsWithoutLocal.hasOwnProperty(key)) {
|
|
(0, utils_1.logWarning)(clc.cyan(clc.yellow("functions: ")) +
|
|
`Writing parameter ${key} to emulator-specific config .env.local. This will overwrite your existing definition only when emulating.`);
|
|
continue;
|
|
}
|
|
throw new error_1.FirebaseError(`Attempted to write param-defined key ${key} to .env files, but it was already defined.`);
|
|
}
|
|
}
|
|
}
|
|
exports.checkForDuplicateKeys = checkForDuplicateKeys;
|
|
function formatUserEnvForWrite(key, value) {
|
|
const escapedValue = value.replace(ALL_ESCAPABLE_CHARACTERS_RE, (match) => CHARACTERS_TO_ESCAPE_SEQUENCES[match]);
|
|
if (escapedValue !== value) {
|
|
return `${key}="${escapedValue}"\n`;
|
|
}
|
|
return `${key}=${escapedValue}\n`;
|
|
}
|
|
function loadUserEnvs({ functionsSource, projectId, projectAlias, isEmulator, }) {
|
|
var _a;
|
|
const envFiles = findEnvfiles(functionsSource, projectId, projectAlias, isEmulator);
|
|
if (envFiles.length === 0) {
|
|
return {};
|
|
}
|
|
if (projectAlias) {
|
|
if (envFiles.includes(`.env.${projectId}`) && envFiles.includes(`.env.${projectAlias}`)) {
|
|
throw new error_1.FirebaseError(`Can't have both dotenv files with projectId (env.${projectId}) ` +
|
|
`and projectAlias (.env.${projectAlias}) as extensions.`);
|
|
}
|
|
}
|
|
let envs = {};
|
|
for (const f of envFiles) {
|
|
try {
|
|
const data = fs.readFileSync(path.join(functionsSource, f), "utf8");
|
|
envs = Object.assign(Object.assign({}, envs), parseStrict(data));
|
|
}
|
|
catch (err) {
|
|
throw new error_1.FirebaseError(`Failed to load environment variables from ${f}.`, {
|
|
exit: 2,
|
|
children: ((_a = err.children) === null || _a === void 0 ? void 0 : _a.length) > 0 ? err.children : [err],
|
|
});
|
|
}
|
|
}
|
|
(0, utils_1.logBullet)(clc.cyan(clc.bold("functions: ")) + `Loaded environment variables from ${envFiles.join(", ")}.`);
|
|
return envs;
|
|
}
|
|
exports.loadUserEnvs = loadUserEnvs;
|
|
function loadFirebaseEnvs(firebaseConfig, projectId) {
|
|
return {
|
|
FIREBASE_CONFIG: JSON.stringify(firebaseConfig),
|
|
GCLOUD_PROJECT: projectId,
|
|
};
|
|
}
|
|
exports.loadFirebaseEnvs = loadFirebaseEnvs;
|