123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.start = exports.downloadIfNecessary = exports.stop = exports.getPID = exports.get = exports.getDownloadDetails = exports.requiresJava = exports.handleEmulatorProcessError = exports._getCommand = exports.getLogFileName = exports.DownloadDetails = void 0;
- const types_1 = require("./types");
- const constants_1 = require("./constants");
- const error_1 = require("../error");
- const childProcess = require("child_process");
- const utils = require("../utils");
- const emulatorLogger_1 = require("./emulatorLogger");
- const clc = require("colorette");
- const fs = require("fs-extra");
- const path = require("path");
- const os = require("os");
- const registry_1 = require("./registry");
- const download_1 = require("../emulator/download");
- const experiments = require("../experiments");
- const EMULATOR_INSTANCE_KILL_TIMEOUT = 4000;
- const CACHE_DIR = process.env.FIREBASE_EMULATORS_PATH || path.join(os.homedir(), ".cache", "firebase", "emulators");
- const EMULATOR_UPDATE_DETAILS = {
- database: {
- version: "4.11.0",
- expectedSize: 34318940,
- expectedChecksum: "311609538bd65666eb724ef47c2e6466",
- },
- firestore: {
- version: "1.15.1",
- expectedSize: 61475851,
- expectedChecksum: "4f41d24a3c0f3b55ea22804a424cc0ee",
- },
- storage: {
- version: "1.1.2",
- expectedSize: 47028740,
- expectedChecksum: "983b4415b1e72b109864f1b8e7ea7546",
- },
- ui: experiments.isEnabled("emulatoruisnapshot")
- ? { version: "SNAPSHOT", expectedSize: -1, expectedChecksum: "" }
- : {
- version: "1.11.2",
- expectedSize: 3062873,
- expectedChecksum: "fe7f668437d0e3c3b92677aaaade78bf",
- },
- pubsub: {
- version: "0.7.1",
- expectedSize: 65137179,
- expectedChecksum: "b59a6e705031a54a69e5e1dced7ca9bf",
- },
- };
- exports.DownloadDetails = {
- database: {
- downloadPath: path.join(CACHE_DIR, `firebase-database-emulator-v${EMULATOR_UPDATE_DETAILS.database.version}.jar`),
- version: EMULATOR_UPDATE_DETAILS.database.version,
- opts: {
- cacheDir: CACHE_DIR,
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/firebase-database-emulator-v${EMULATOR_UPDATE_DETAILS.database.version}.jar`,
- expectedSize: EMULATOR_UPDATE_DETAILS.database.expectedSize,
- expectedChecksum: EMULATOR_UPDATE_DETAILS.database.expectedChecksum,
- namePrefix: "firebase-database-emulator",
- },
- },
- firestore: {
- downloadPath: path.join(CACHE_DIR, `cloud-firestore-emulator-v${EMULATOR_UPDATE_DETAILS.firestore.version}.jar`),
- version: EMULATOR_UPDATE_DETAILS.firestore.version,
- opts: {
- cacheDir: CACHE_DIR,
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v${EMULATOR_UPDATE_DETAILS.firestore.version}.jar`,
- expectedSize: EMULATOR_UPDATE_DETAILS.firestore.expectedSize,
- expectedChecksum: EMULATOR_UPDATE_DETAILS.firestore.expectedChecksum,
- namePrefix: "cloud-firestore-emulator",
- },
- },
- storage: {
- downloadPath: path.join(CACHE_DIR, `cloud-storage-rules-runtime-v${EMULATOR_UPDATE_DETAILS.storage.version}.jar`),
- version: EMULATOR_UPDATE_DETAILS.storage.version,
- opts: {
- cacheDir: CACHE_DIR,
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v${EMULATOR_UPDATE_DETAILS.storage.version}.jar`,
- expectedSize: EMULATOR_UPDATE_DETAILS.storage.expectedSize,
- expectedChecksum: EMULATOR_UPDATE_DETAILS.storage.expectedChecksum,
- namePrefix: "cloud-storage-rules-emulator",
- },
- },
- ui: {
- version: EMULATOR_UPDATE_DETAILS.ui.version,
- downloadPath: path.join(CACHE_DIR, `ui-v${EMULATOR_UPDATE_DETAILS.ui.version}.zip`),
- unzipDir: path.join(CACHE_DIR, `ui-v${EMULATOR_UPDATE_DETAILS.ui.version}`),
- binaryPath: path.join(CACHE_DIR, `ui-v${EMULATOR_UPDATE_DETAILS.ui.version}`, "server", "server.js"),
- opts: {
- cacheDir: CACHE_DIR,
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v${EMULATOR_UPDATE_DETAILS.ui.version}.zip`,
- expectedSize: EMULATOR_UPDATE_DETAILS.ui.expectedSize,
- expectedChecksum: EMULATOR_UPDATE_DETAILS.ui.expectedChecksum,
- skipCache: experiments.isEnabled("emulatoruisnapshot"),
- skipChecksumAndSize: experiments.isEnabled("emulatoruisnapshot"),
- namePrefix: "ui",
- },
- },
- pubsub: {
- downloadPath: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}.zip`),
- version: EMULATOR_UPDATE_DETAILS.pubsub.version,
- unzipDir: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`),
- binaryPath: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`, `pubsub-emulator/bin/cloud-pubsub-emulator${process.platform === "win32" ? ".bat" : ""}`),
- opts: {
- cacheDir: CACHE_DIR,
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}.zip`,
- expectedSize: EMULATOR_UPDATE_DETAILS.pubsub.expectedSize,
- expectedChecksum: EMULATOR_UPDATE_DETAILS.pubsub.expectedChecksum,
- namePrefix: "pubsub-emulator",
- },
- },
- };
- const EmulatorDetails = {
- database: {
- name: types_1.Emulators.DATABASE,
- instance: null,
- stdout: null,
- },
- firestore: {
- name: types_1.Emulators.FIRESTORE,
- instance: null,
- stdout: null,
- },
- storage: {
- name: types_1.Emulators.STORAGE,
- instance: null,
- stdout: null,
- },
- pubsub: {
- name: types_1.Emulators.PUBSUB,
- instance: null,
- stdout: null,
- },
- ui: {
- name: types_1.Emulators.UI,
- instance: null,
- stdout: null,
- },
- };
- const Commands = {
- database: {
- binary: "java",
- args: ["-Duser.language=en", "-jar", getExecPath(types_1.Emulators.DATABASE)],
- optionalArgs: [
- "port",
- "host",
- "functions_emulator_port",
- "functions_emulator_host",
- "single_project_mode",
- ],
- joinArgs: false,
- },
- firestore: {
- binary: "java",
- args: [
- "-Dgoogle.cloud_firestore.debug_log_level=FINE",
- "-Duser.language=en",
- "-jar",
- getExecPath(types_1.Emulators.FIRESTORE),
- ],
- optionalArgs: [
- "port",
- "webchannel_port",
- "host",
- "rules",
- "websocket_port",
- "functions_emulator",
- "seed_from_export",
- "project_id",
- "single_project_mode",
- ],
- joinArgs: false,
- },
- storage: {
- binary: "java",
- args: [
- "-Duser.language=en",
- "-jar",
- getExecPath(types_1.Emulators.STORAGE),
- "serve",
- ],
- optionalArgs: [],
- joinArgs: false,
- },
- pubsub: {
- binary: getExecPath(types_1.Emulators.PUBSUB),
- args: [],
- optionalArgs: ["port", "host"],
- joinArgs: true,
- },
- ui: {
- binary: "node",
- args: [getExecPath(types_1.Emulators.UI)],
- optionalArgs: [],
- joinArgs: false,
- },
- };
- function getExecPath(name) {
- const details = getDownloadDetails(name);
- return details.binaryPath || details.downloadPath;
- }
- function getLogFileName(name) {
- return `${name}-debug.log`;
- }
- exports.getLogFileName = getLogFileName;
- function _getCommand(emulator, args) {
- const baseCmd = Commands[emulator];
- const defaultPort = constants_1.Constants.getDefaultPort(emulator);
- if (!args.port) {
- args.port = defaultPort;
- }
- const cmdLineArgs = baseCmd.args.slice();
- if (baseCmd.binary === "java" &&
- utils.isRunningInWSL() &&
- (!args.host || !args.host.includes(":"))) {
- cmdLineArgs.unshift("-Djava.net.preferIPv4Stack=true");
- }
- const logger = emulatorLogger_1.EmulatorLogger.forEmulator(emulator);
- Object.keys(args).forEach((key) => {
- if (!baseCmd.optionalArgs.includes(key)) {
- logger.log("DEBUG", `Ignoring unsupported arg: ${key}`);
- return;
- }
- const argKey = "--" + key;
- const argVal = args[key];
- if (argVal === undefined) {
- logger.log("DEBUG", `Ignoring empty arg for key: ${key}`);
- return;
- }
- if (baseCmd.joinArgs) {
- cmdLineArgs.push(`${argKey}=${argVal}`);
- }
- else {
- cmdLineArgs.push(argKey, argVal);
- }
- });
- return {
- binary: baseCmd.binary,
- args: cmdLineArgs,
- optionalArgs: baseCmd.optionalArgs,
- joinArgs: baseCmd.joinArgs,
- };
- }
- exports._getCommand = _getCommand;
- async function _fatal(emulator, errorMsg) {
- try {
- const logger = emulatorLogger_1.EmulatorLogger.forEmulator(emulator);
- logger.logLabeled("WARN", emulator, `Fatal error occurred: \n ${errorMsg}, \n stopping all running emulators`);
- await registry_1.EmulatorRegistry.stopAll();
- }
- finally {
- process.exit(1);
- }
- }
- async function handleEmulatorProcessError(emulator, err) {
- const description = constants_1.Constants.description(emulator);
- if (err.path === "java" && err.code === "ENOENT") {
- await _fatal(emulator, `${description} has exited because java is not installed, you can install it from https://openjdk.java.net/install/`);
- }
- else {
- await _fatal(emulator, `${description} has exited: ${err}`);
- }
- }
- exports.handleEmulatorProcessError = handleEmulatorProcessError;
- function requiresJava(emulator) {
- if (emulator in Commands) {
- return Commands[emulator].binary === "java";
- }
- return false;
- }
- exports.requiresJava = requiresJava;
- async function _runBinary(emulator, command, extraEnv) {
- return new Promise((resolve) => {
- var _a, _b;
- const logger = emulatorLogger_1.EmulatorLogger.forEmulator(emulator.name);
- emulator.stdout = fs.createWriteStream(getLogFileName(emulator.name));
- try {
- emulator.instance = childProcess.spawn(command.binary, command.args, {
- env: Object.assign(Object.assign({}, process.env), extraEnv),
- detached: true,
- stdio: ["inherit", "pipe", "pipe"],
- });
- }
- catch (e) {
- if (e.code === "EACCES") {
- logger.logLabeled("WARN", emulator.name, `Could not spawn child process for emulator, check that java is installed and on your $PATH.`);
- }
- _fatal(emulator.name, e);
- }
- const description = constants_1.Constants.description(emulator.name);
- if (emulator.instance == null) {
- logger.logLabeled("WARN", emulator.name, `Could not spawn child process for ${description}.`);
- return;
- }
- logger.logLabeled("BULLET", emulator.name, `${description} logging to ${clc.bold(getLogFileName(emulator.name))}`);
- (_a = emulator.instance.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (data) => {
- logger.log("DEBUG", data.toString());
- emulator.stdout.write(data);
- });
- (_b = emulator.instance.stderr) === null || _b === void 0 ? void 0 : _b.on("data", (data) => {
- logger.log("DEBUG", data.toString());
- emulator.stdout.write(data);
- if (data.toString().includes("java.lang.UnsupportedClassVersionError")) {
- logger.logLabeled("WARN", emulator.name, "Unsupported java version, make sure java --version reports 1.8 or higher.");
- }
- });
- emulator.instance.on("error", (err) => {
- handleEmulatorProcessError(emulator.name, err);
- });
- emulator.instance.once("exit", async (code, signal) => {
- if (signal) {
- utils.logWarning(`${description} has exited upon receiving signal: ${signal}`);
- }
- else if (code && code !== 0 && code !== 130) {
- await _fatal(emulator.name, `${description} has exited with code: ${code}`);
- }
- });
- resolve();
- });
- }
- function getDownloadDetails(emulator) {
- return exports.DownloadDetails[emulator];
- }
- exports.getDownloadDetails = getDownloadDetails;
- function get(emulator) {
- return EmulatorDetails[emulator];
- }
- exports.get = get;
- function getPID(emulator) {
- const emulatorInstance = get(emulator).instance;
- return emulatorInstance && emulatorInstance.pid ? emulatorInstance.pid : 0;
- }
- exports.getPID = getPID;
- async function stop(targetName) {
- const emulator = get(targetName);
- return new Promise((resolve, reject) => {
- const logger = emulatorLogger_1.EmulatorLogger.forEmulator(emulator.name);
- if (emulator.instance) {
- const killTimeout = setTimeout(() => {
- const pid = emulator.instance ? emulator.instance.pid : -1;
- const errorMsg = constants_1.Constants.description(emulator.name) + ": Unable to terminate process (PID=" + pid + ")";
- logger.log("DEBUG", errorMsg);
- reject(new error_1.FirebaseError(emulator.name + ": " + errorMsg));
- }, EMULATOR_INSTANCE_KILL_TIMEOUT);
- emulator.instance.once("exit", () => {
- clearTimeout(killTimeout);
- resolve();
- });
- emulator.instance.kill("SIGINT");
- }
- else {
- resolve();
- }
- });
- }
- exports.stop = stop;
- async function downloadIfNecessary(targetName) {
- const hasEmulator = fs.existsSync(getExecPath(targetName));
- if (hasEmulator) {
- return;
- }
- await (0, download_1.downloadEmulator)(targetName);
- }
- exports.downloadIfNecessary = downloadIfNecessary;
- async function start(targetName, args, extraEnv = {}) {
- const downloadDetails = exports.DownloadDetails[targetName];
- const emulator = get(targetName);
- const hasEmulator = fs.existsSync(getExecPath(targetName));
- const logger = emulatorLogger_1.EmulatorLogger.forEmulator(targetName);
- if (!hasEmulator || downloadDetails.opts.skipCache) {
- if (args.auto_download) {
- if (process.env.CI) {
- utils.logWarning(`It appears you are running in a CI environment. You can avoid downloading the ${constants_1.Constants.description(targetName)} repeatedly by caching the ${downloadDetails.opts.cacheDir} directory.`);
- }
- await (0, download_1.downloadEmulator)(targetName);
- }
- else {
- utils.logWarning("Setup required, please run: firebase setup:emulators:" + targetName);
- throw new error_1.FirebaseError("emulator not found");
- }
- }
- const command = _getCommand(targetName, args);
- logger.log("DEBUG", `Starting ${constants_1.Constants.description(targetName)} with command ${JSON.stringify(command)}`);
- return _runBinary(emulator, command, extraEnv);
- }
- exports.start = start;
|