123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.JAVA_DEPRECATION_WARNING = exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = exports.checkJavaMajorVersion = exports.emulatorExec = exports.getListenOverview = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
- const clc = require("colorette");
- const childProcess = require("child_process");
- const controller = require("../emulator/controller");
- const config_1 = require("../config");
- const utils = require("../utils");
- const logger_1 = require("../logger");
- const path = require("path");
- const constants_1 = require("./constants");
- const requireAuth_1 = require("../requireAuth");
- const requireConfig_1 = require("../requireConfig");
- const types_1 = require("./types");
- const error_1 = require("../error");
- const registry_1 = require("./registry");
- const projectUtils_1 = require("../projectUtils");
- const prompt_1 = require("../prompt");
- const fsutils = require("../fsutils");
- const Table = require("cli-table");
- const track_1 = require("../track");
- const env_1 = require("./env");
- exports.FLAG_ONLY = "--only <emulators>";
- exports.DESC_ONLY = "only specific emulators. " +
- "This is a comma separated list of emulator names. " +
- "Valid options are: " +
- JSON.stringify(types_1.ALL_SERVICE_EMULATORS);
- exports.FLAG_INSPECT_FUNCTIONS = "--inspect-functions [port]";
- exports.DESC_INSPECT_FUNCTIONS = "emulate Cloud Functions in debug mode with the node inspector on the given port (9229 if not specified)";
- exports.FLAG_IMPORT = "--import [dir]";
- exports.DESC_IMPORT = "import emulator data from a previous export (see emulators:export)";
- exports.FLAG_EXPORT_ON_EXIT_NAME = "--export-on-exit";
- exports.FLAG_EXPORT_ON_EXIT = `${exports.FLAG_EXPORT_ON_EXIT_NAME} [dir]`;
- exports.DESC_EXPORT_ON_EXIT = "automatically export emulator data (emulators:export) " +
- "when the emulators make a clean exit (SIGINT), " +
- `when no dir is provided the location of ${exports.FLAG_IMPORT} is used`;
- exports.EXPORT_ON_EXIT_USAGE_ERROR = `"${exports.FLAG_EXPORT_ON_EXIT_NAME}" must be used with "${exports.FLAG_IMPORT}"` +
- ` or provide a dir directly to "${exports.FLAG_EXPORT_ON_EXIT}"`;
- exports.EXPORT_ON_EXIT_CWD_DANGER = `"${exports.FLAG_EXPORT_ON_EXIT_NAME}" must not point to the current directory or parents. Please choose a new/dedicated directory for exports.`;
- exports.FLAG_UI = "--ui";
- exports.DESC_UI = "run the Emulator UI";
- exports.FLAG_TEST_CONFIG = "--test-config <firebase.json file>";
- exports.DESC_TEST_CONFIG = "A firebase.json style file. Used to configure the Firestore and Realtime Database emulators.";
- exports.FLAG_TEST_PARAMS = "--test-params <params.env file>";
- exports.DESC_TEST_PARAMS = "A .env file containing test param values for your emulated extension.";
- const DEFAULT_CONFIG = new config_1.Config({
- eventarc: {},
- database: {},
- firestore: {},
- functions: {},
- hosting: {},
- emulators: { auth: {}, pubsub: {} },
- }, {});
- function printNoticeIfEmulated(options, emulator) {
- if (emulator !== types_1.Emulators.DATABASE && emulator !== types_1.Emulators.FIRESTORE) {
- return;
- }
- const emuName = constants_1.Constants.description(emulator);
- const envKey = emulator === types_1.Emulators.DATABASE
- ? constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST
- : constants_1.Constants.FIRESTORE_EMULATOR_HOST;
- const envVal = process.env[envKey];
- if (envVal) {
- utils.logBullet(`You have set ${clc.bold(`${envKey}=${envVal}`)}, this command will execute against the ${emuName} running at that address.`);
- }
- }
- exports.printNoticeIfEmulated = printNoticeIfEmulated;
- function warnEmulatorNotSupported(options, emulator) {
- if (emulator !== types_1.Emulators.DATABASE && emulator !== types_1.Emulators.FIRESTORE) {
- return;
- }
- const emuName = constants_1.Constants.description(emulator);
- const envKey = emulator === types_1.Emulators.DATABASE
- ? constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST
- : constants_1.Constants.FIRESTORE_EMULATOR_HOST;
- const envVal = process.env[envKey];
- if (envVal) {
- utils.logWarning(`You have set ${clc.bold(`${envKey}=${envVal}`)}, however this command does not support running against the ${emuName} so this action will affect production.`);
- const opts = {
- confirm: undefined,
- };
- return (0, prompt_1.promptOnce)({
- type: "confirm",
- default: false,
- message: "Do you want to continue?",
- }).then(() => {
- if (!opts.confirm) {
- return utils.reject("Command aborted.", { exit: 1 });
- }
- });
- }
- }
- exports.warnEmulatorNotSupported = warnEmulatorNotSupported;
- async function beforeEmulatorCommand(options) {
- const optionsWithDefaultConfig = Object.assign(Object.assign({}, options), { config: DEFAULT_CONFIG });
- const optionsWithConfig = options.config ? options : optionsWithDefaultConfig;
- const canStartWithoutConfig = options.only &&
- !controller.shouldStart(optionsWithConfig, types_1.Emulators.FUNCTIONS) &&
- !controller.shouldStart(optionsWithConfig, types_1.Emulators.HOSTING);
- try {
- await (0, requireAuth_1.requireAuth)(options);
- }
- catch (e) {
- logger_1.logger.debug(e);
- utils.logLabeledWarning("emulators", `You are not currently authenticated so some features may not work correctly. Please run ${clc.bold("firebase login")} to authenticate the CLI.`);
- }
- if (canStartWithoutConfig && !options.config) {
- utils.logWarning("Could not find config (firebase.json) so using defaults.");
- options.config = DEFAULT_CONFIG;
- }
- else {
- await (0, requireConfig_1.requireConfig)(options);
- }
- }
- exports.beforeEmulatorCommand = beforeEmulatorCommand;
- function parseInspectionPort(options) {
- let port = options.inspectFunctions;
- if (port === true) {
- port = "9229";
- }
- const parsed = Number(port);
- if (isNaN(parsed) || parsed < 1024 || parsed > 65535) {
- throw new error_1.FirebaseError(`"${port}" is not a valid port for debugging, please pass an integer between 1024 and 65535.`);
- }
- return parsed;
- }
- exports.parseInspectionPort = parseInspectionPort;
- function setExportOnExitOptions(options) {
- if (options.exportOnExit || typeof options.exportOnExit === "string") {
- if (options.import) {
- options.exportOnExit =
- typeof options.exportOnExit === "string" ? options.exportOnExit : options.import;
- const importPath = path.resolve(options.import);
- if (!fsutils.dirExistsSync(importPath) && options.import === options.exportOnExit) {
- options.exportOnExit = options.import;
- delete options.import;
- }
- }
- if (options.exportOnExit === true || !options.exportOnExit) {
- throw new error_1.FirebaseError(exports.EXPORT_ON_EXIT_USAGE_ERROR);
- }
- if (path.resolve(".").startsWith(path.resolve(options.exportOnExit))) {
- throw new error_1.FirebaseError(exports.EXPORT_ON_EXIT_CWD_DANGER);
- }
- }
- return;
- }
- exports.setExportOnExitOptions = setExportOnExitOptions;
- function processKillSignal(signal, res, rej, options) {
- let lastSignal = new Date().getTime();
- let signalCount = 0;
- return async () => {
- var _a;
- try {
- const now = new Date().getTime();
- const diff = now - lastSignal;
- if (diff < 100) {
- logger_1.logger.debug(`Ignoring signal ${signal} due to short delay of ${diff}ms`);
- return;
- }
- signalCount = signalCount + 1;
- lastSignal = now;
- const signalDisplay = signal === "SIGINT" ? `SIGINT (Ctrl-C)` : signal;
- logger_1.logger.debug(`Received signal ${signalDisplay} ${signalCount}`);
- logger_1.logger.info(" ");
- if (signalCount === 1) {
- utils.logLabeledBullet("emulators", `Received ${signalDisplay} for the first time. Starting a clean shutdown.`);
- utils.logLabeledBullet("emulators", `Please wait for a clean shutdown or send the ${signalDisplay} signal again to stop right now.`);
- await controller.onExit(options);
- await controller.cleanShutdown();
- }
- else {
- logger_1.logger.debug(`Skipping clean onExit() and cleanShutdown()`);
- const runningEmulatorsInfosWithPid = registry_1.EmulatorRegistry.listRunningWithInfo().filter((i) => Boolean(i.pid));
- utils.logLabeledWarning("emulators", `Received ${signalDisplay} ${signalCount} times. You have forced the Emulator Suite to exit without waiting for ${runningEmulatorsInfosWithPid.length} subprocess${runningEmulatorsInfosWithPid.length > 1 ? "es" : ""} to finish. These processes ${clc.bold("may")} still be running on your machine: `);
- const pids = [];
- const emulatorsTable = new Table({
- head: ["Emulator", "Host:Port", "PID"],
- style: {
- head: ["yellow"],
- },
- });
- for (const emulatorInfo of runningEmulatorsInfosWithPid) {
- pids.push(emulatorInfo.pid);
- emulatorsTable.push([
- constants_1.Constants.description(emulatorInfo.name),
- (_a = getListenOverview(emulatorInfo.name)) !== null && _a !== void 0 ? _a : "unknown",
- emulatorInfo.pid,
- ]);
- }
- logger_1.logger.info(`\n${emulatorsTable}\n\nTo force them to exit run:\n`);
- if (process.platform === "win32") {
- logger_1.logger.info(clc.bold(`TASKKILL ${pids.map((pid) => "/PID " + pid).join(" ")} /T\n`));
- }
- else {
- logger_1.logger.info(clc.bold(`kill ${pids.join(" ")}\n`));
- }
- }
- res();
- }
- catch (e) {
- logger_1.logger.debug(e);
- rej();
- }
- };
- }
- function shutdownWhenKilled(options) {
- return new Promise((res, rej) => {
- ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"].forEach((signal) => {
- process.on(signal, processKillSignal(signal, res, rej, options));
- });
- }).catch((e) => {
- logger_1.logger.debug(e);
- utils.logLabeledWarning("emulators", "emulators failed to shut down cleanly, see firebase-debug.log for details.");
- throw e;
- });
- }
- exports.shutdownWhenKilled = shutdownWhenKilled;
- async function runScript(script, extraEnv) {
- utils.logBullet(`Running script: ${clc.bold(script)}`);
- const env = Object.assign(Object.assign({}, process.env), extraEnv);
- const emulatorInfos = registry_1.EmulatorRegistry.listRunningWithInfo();
- (0, env_1.setEnvVarsForEmulators)(env, emulatorInfos);
- const proc = childProcess.spawn(script, {
- stdio: ["inherit", "inherit", "inherit"],
- shell: true,
- windowsHide: true,
- env,
- });
- logger_1.logger.debug(`Running ${script} with environment ${JSON.stringify(env)}`);
- return new Promise((resolve, reject) => {
- proc.on("error", (err) => {
- utils.logWarning(`There was an error running the script: ${JSON.stringify(err)}`);
- reject();
- });
- const exitDelayMs = 500;
- proc.once("exit", (code, signal) => {
- if (signal) {
- utils.logWarning(`Script exited with signal: ${signal}`);
- setTimeout(reject, exitDelayMs);
- return;
- }
- const exitCode = code || 0;
- if (code === 0) {
- utils.logSuccess(`Script exited successfully (code 0)`);
- }
- else {
- utils.logWarning(`Script exited unsuccessfully (code ${code})`);
- }
- setTimeout(() => {
- resolve(exitCode);
- }, exitDelayMs);
- });
- });
- }
- function getListenOverview(emulator) {
- var _a;
- const info = (_a = registry_1.EmulatorRegistry.get(emulator)) === null || _a === void 0 ? void 0 : _a.getInfo();
- if (!info) {
- return undefined;
- }
- if (info.host.includes(":")) {
- return `[${info.host}]:${info.port}`;
- }
- else {
- return `${info.host}:${info.port}`;
- }
- }
- exports.getListenOverview = getListenOverview;
- async function emulatorExec(script, options) {
- const projectId = (0, projectUtils_1.getProjectId)(options);
- const extraEnv = {};
- if (projectId) {
- extraEnv.GCLOUD_PROJECT = projectId;
- }
- const session = (0, track_1.emulatorSession)();
- if (session && session.debugMode) {
- extraEnv[constants_1.Constants.FIREBASE_GA_SESSION] = JSON.stringify(session);
- }
- let exitCode = 0;
- let deprecationNotices;
- try {
- const showUI = !!options.ui;
- ({ deprecationNotices } = await controller.startAll(options, showUI));
- exitCode = await runScript(script, extraEnv);
- await controller.onExit(options);
- }
- finally {
- await controller.cleanShutdown();
- }
- for (const notice of deprecationNotices) {
- utils.logLabeledWarning("emulators", notice, "warn");
- }
- if (exitCode !== 0) {
- throw new error_1.FirebaseError(`Script "${clc.bold(script)}" exited with code ${exitCode}`, {
- exit: exitCode,
- });
- }
- }
- exports.emulatorExec = emulatorExec;
- const JAVA_VERSION_REGEX = /version "([1-9][0-9]*)/;
- const JAVA_HINT = "Please make sure Java is installed and on your system PATH.";
- async function checkJavaMajorVersion() {
- return new Promise((resolve, reject) => {
- var _a, _b;
- let child;
- try {
- child = childProcess.spawn("java", ["-Duser.language=en", "-Dfile.encoding=UTF-8", "-version"], {
- stdio: ["inherit", "pipe", "pipe"],
- });
- }
- catch (err) {
- return reject(new error_1.FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { original: err }));
- }
- let output = "";
- let error = "";
- (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (data) => {
- const str = data.toString("utf8");
- logger_1.logger.debug(str);
- output += str;
- });
- (_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on("data", (data) => {
- const str = data.toString("utf8");
- logger_1.logger.debug(str);
- error += str;
- });
- child.once("error", (err) => {
- reject(new error_1.FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { original: err }));
- });
- child.once("exit", (code, signal) => {
- if (signal) {
- reject(new error_1.FirebaseError(`Process \`java -version\` was killed by signal ${signal}.`));
- }
- else if (code && code !== 0) {
- reject(new error_1.FirebaseError(`Process \`java -version\` has exited with code ${code}. ${JAVA_HINT}\n` +
- `-----Original stdout-----\n${output}` +
- `-----Original stderr-----\n${error}`));
- }
- else {
- resolve(`${output}\n${error}`);
- }
- });
- }).then((output) => {
- let versionInt = -1;
- const match = output.match(JAVA_VERSION_REGEX);
- if (match) {
- const version = match[1];
- versionInt = parseInt(version, 10);
- if (!versionInt) {
- utils.logLabeledWarning("emulators", `Failed to parse Java version. Got "${match[0]}".`, "warn");
- }
- else {
- logger_1.logger.debug(`Parsed Java major version: ${versionInt}`);
- }
- }
- else {
- logger_1.logger.debug("java -version outputs:", output);
- logger_1.logger.warn(`Failed to parse Java version.`);
- }
- const session = (0, track_1.emulatorSession)();
- if (session) {
- session.javaMajorVersion = versionInt;
- }
- return versionInt;
- });
- }
- exports.checkJavaMajorVersion = checkJavaMajorVersion;
- exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = 11;
- exports.JAVA_DEPRECATION_WARNING = "firebase-tools no longer supports Java version before 11. " +
- "Please upgrade to Java version 11 or above to continue using the emulators.";
|