Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deprecation notice for Java<11. #4347

Merged
merged 3 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Marks Java 10 and below as deprecated. Support will be dropped in Firebase CLI v11. Please upgrade to Java version 11 or above to continue using the emulators. (#4347)
8 changes: 7 additions & 1 deletion src/commands/emulators-start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { EmulatorRegistry } from "../emulator/registry";
import { Emulators, EMULATORS_SUPPORTED_BY_UI } from "../emulator/types";
import * as clc from "cli-color";
import { Constants } from "../emulator/constants";
import { logLabeledWarning } from "../utils";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const Table = require("cli-table");
Expand All @@ -26,8 +27,9 @@ module.exports = new Command("emulators:start")
.action(async (options: any) => {
const killSignalPromise = commandUtils.shutdownWhenKilled(options);

let deprecationNotices;
try {
await controller.startAll(options);
({ deprecationNotices } = await controller.startAll(options));
} catch (e: any) {
await controller.cleanShutdown();
throw e;
Expand Down Expand Up @@ -111,6 +113,10 @@ Issues? Report them at ${stylizeLink(
// Add this line above once connect page is implemented
// It is now safe to connect your app. Instructions: http://${uiInfo?.host}:${uiInfo?.port}/connect

for (const notice of deprecationNotices) {
logLabeledWarning("emulators", notice, "warn");
}

// Hang until explicitly killed
await killSignalPromise;
});
7 changes: 6 additions & 1 deletion src/commands/ext-dev-emulators-start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ module.exports = new Command("ext:dev:emulators:start")
.action(async (options: any) => {
const killSignalPromise = commandUtils.shutdownWhenKilled(options);
const emulatorOptions = await optionsHelper.buildOptions(options);

let deprecationNotices;
try {
commandUtils.beforeEmulatorCommand(emulatorOptions);
await controller.startAll(emulatorOptions);
({ deprecationNotices } = await controller.startAll(emulatorOptions));
} catch (e: any) {
await controller.cleanShutdown();
if (!(e instanceof FirebaseError)) {
Expand All @@ -30,6 +32,9 @@ module.exports = new Command("ext:dev:emulators:start")
}

utils.logSuccess("All emulators ready, it is now safe to connect.");
for (const notice of deprecationNotices) {
utils.logLabeledWarning("emulators", notice, "warn");
}

// Hang until explicitly killed
await killSignalPromise;
Expand Down
103 changes: 102 additions & 1 deletion src/emulator/commandUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,18 +416,119 @@ export async function emulatorExec(script: string, options: any) {
extraEnv.GCLOUD_PROJECT = projectId;
}
let exitCode = 0;
let deprecationNotices;
try {
const showUI = !!options.ui;
await controller.startAll(options, showUI);
({ deprecationNotices } = await controller.startAll(options, showUI));
exitCode = await runScript(script, extraEnv);
await onExit(options);
} finally {
await controller.cleanShutdown();
}

for (const notice of deprecationNotices) {
utils.logLabeledWarning("emulators", notice, "warn");
}

if (exitCode !== 0) {
throw new FirebaseError(`Script "${clc.bold(script)}" exited with code ${exitCode}`, {
exit: exitCode,
});
}
}

// Regex to extract Java major version. Only works with Java >= 9.
// See: http://openjdk.java.net/jeps/223
const JAVA_VERSION_REGEX = /version "([1-9][0-9]*)/;
const MIN_SUPPORTED_JAVA_MAJOR_VERSION = 11;
const JAVA_HINT = "Please make sure Java is installed and on your system PATH.";

/**
* Return whether Java major verion is supported. Throws if Java not available.
*
* @returns true if Java >= 11, false otherwise
*/
export async function checkJavaSupported(): Promise<boolean> {
return new Promise<string>((resolve, reject) => {
let child;
try {
child = childProcess.spawn(
"java",
["-Duser.language=en", "-Dfile.encoding=UTF-8", "-version"],
{
stdio: ["inherit", "pipe", "pipe"],
}
);
} catch (err: any) {
return reject(
new FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { original: err })
);
}

let output = "";
let error = "";
child.stdout?.on("data", (data) => {
const str = data.toString("utf8");
logger.debug(str);
output += str;
});
child.stderr?.on("data", (data) => {
const str = data.toString("utf8");
logger.debug(str);
error += str;
});

child.once("error", (err) => {
reject(
new FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { original: err })
);
});

child.once("exit", (code, signal) => {
if (signal) {
// This is an unlikely situation where the short-lived Java process to
// check version was killed by a signal.
reject(new FirebaseError(`Process \`java -version\` was killed by signal ${signal}.`));
} else if (code && code !== 0) {
// `java -version` failed. For example, this may happen on some OS X
// where `java` is by default a stub that prints out more information on
// how to install Java. It is critical for us to relay stderr/stdout.
reject(
new FirebaseError(
`Process \`java -version\` has exited with code ${code}. ${JAVA_HINT}\n` +
`-----Original stdout-----\n${output}` +
`-----Original stderr-----\n${error}`
)
);
} else {
// Join child process stdout and stderr for further parsing. Order does
// not matter here because we'll parse only a small part later.
resolve(`${output}\n${error}`);
}
});
}).then((output) => {
const match = output.match(JAVA_VERSION_REGEX);
if (match) {
const version = match[1];
const versionInt = parseInt(version, 10);
if (!versionInt) {
utils.logLabeledWarning(
"emulators",
`Failed to parse Java version. Got "${match[0]}".`,
"warn"
);
} else {
logger.debug(`Parsed Java major version: ${versionInt}`);
return versionInt >= MIN_SUPPORTED_JAVA_MAJOR_VERSION;
}
} else {
logger.debug("java -version outputs:", output);
logger.warn(`Failed to parse Java version.`);
}
return false;
});
}

export const JAVA_DEPRECATION_WARNING =
"Support for Java version <= 10 will be dropped soon in firebase-tools@11. " +
"Please upgrade to Java version 11 or above to continue using the emulators.";
17 changes: 15 additions & 2 deletions src/emulator/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { EmulatorLogger } from "./emulatorLogger";
import * as portUtils from "./portUtils";
import { EmulatorHubClient } from "./hubClient";
import { promptOnce } from "../prompt";
import { FLAG_EXPORT_ON_EXIT_NAME } from "./commandUtils";
import { FLAG_EXPORT_ON_EXIT_NAME, JAVA_DEPRECATION_WARNING } from "./commandUtils";
import { fileExistsSync } from "../fsutils";
import { StorageEmulator } from "./storage";
import { getStorageRulesConfig } from "./storage/rules/config";
Expand All @@ -46,6 +46,7 @@ import { ParsedTriggerDefinition } from "./functionsEmulatorShared";
import { ExtensionsEmulator } from "./extensionsEmulator";
import { previews } from "../previews";
import { normalizeAndValidate } from "../functions/projectConfig";
import { requiresJava } from "./downloadableEmulators";

const START_LOGGING_EMULATOR = utils.envOverride(
"START_LOGGING_EMULATOR",
Expand Down Expand Up @@ -332,7 +333,10 @@ interface EmulatorOptions extends Options {
extDevEnv?: Record<string, string>;
}

export async function startAll(options: EmulatorOptions, showUI = true): Promise<void> {
export async function startAll(
options: EmulatorOptions,
showUI = true
): Promise<{ deprecationNotices: string[] }> {
// Emulators config is specified in firebase.json as:
// "emulators": {
// "firestore": {
Expand All @@ -353,6 +357,13 @@ export async function startAll(options: EmulatorOptions, showUI = true): Promise
`No emulators to start, run ${clc.bold("firebase init emulators")} to get started.`
);
}
const deprecationNotices = [];
if (targets.some(requiresJava)) {
if (!(await commandUtils.checkJavaSupported())) {
utils.logLabeledWarning("emulators", JAVA_DEPRECATION_WARNING, "warn");
deprecationNotices.push(JAVA_DEPRECATION_WARNING);
}
}
const hubLogger = EmulatorLogger.forEmulator(Emulators.HUB);
hubLogger.logLabeled("BULLET", "emulators", `Starting emulators: ${targets.join(", ")}`);

Expand Down Expand Up @@ -761,6 +772,8 @@ export async function startAll(options: EmulatorOptions, showUI = true): Promise
await instance.connect();
}
}

return { deprecationNotices };
}

/**
Expand Down
7 changes: 7 additions & 0 deletions src/emulator/downloadableEmulators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,13 @@ export async function handleEmulatorProcessError(emulator: Emulators, err: any):
}
}

export function requiresJava(emulator: Emulators): boolean {
if (emulator in Commands) {
return Commands[emulator as keyof typeof Commands].binary === "java";
}
return false;
}

async function _runBinary(
emulator: DownloadableEmulatorDetails,
command: DownloadableEmulatorCommand,
Expand Down