fix(run): enable Spring console colours with manual classpaths (#888)

Spring Boot runs with a manual classpath (or no classpath placeholder) skip Java classpath resolution and so also skipped the console colour VM option. The shared console VM options are now applied on that launch path too; explicit spring.output.ansi.enabled settings are kept and resolved classpaths get the option once.
This commit is contained in:
l0ng-ai
2026-11-02 23:37:52 +08:00
committed by GitHub
parent 51988985c5
commit c0c56dac8a
2 changed files with 46 additions and 2 deletions
+38
View File
@@ -87,6 +87,44 @@ it("finds the source module for an old saved configuration without a module", as
expect(ipc.runStart).toHaveBeenCalledWith(expect.objectContaining({ cwd: "/outside/modules" }), expect.anything());
});
it.each([["-cp", "lib/*"], []])("colours Spring Boot runs without classpath resolution (%j)", async (...classpath) => {
const args = ["-Xmx256m", ...classpath, "a.Main", "--spring.profiles.active=dev"];
const boot = { ...config, kind: "spring-boot", args };
const session = await startRun(boot);
expect(session?.status).toBe("running");
expect(ipc.runStart).toHaveBeenCalledWith(
expect.objectContaining({ args: ["-Dspring.output.ansi.enabled=always", ...args] }),
expect.anything(),
);
expect(ipc.javaResolveLaunch).not.toHaveBeenCalled();
expect(boot.args).toBe(args);
expect(args).not.toContain("-Dspring.output.ansi.enabled=always");
});
it.each(["never", "detect", "always"])("preserves an explicit Spring Boot console setting (%s)", async (value) => {
const args = [`-Dspring.output.ansi.enabled=${value}`, "-cp", "lib/*", "a.Main"];
await startRun({ ...config, kind: "spring-boot", args });
expect(ipc.runStart).toHaveBeenCalledWith(expect.objectContaining({ args }), expect.anything());
expect(ipc.javaResolveLaunch).not.toHaveBeenCalled();
});
it("keeps plain Java runs with an explicit classpath free of Spring Boot options", async () => {
const args = ["-cp", "lib/*", "a.Main"];
await startRun({ ...config, args });
expect(ipc.runStart).toHaveBeenCalledWith(expect.objectContaining({ args }), expect.anything());
expect(ipc.javaResolveLaunch).not.toHaveBeenCalled();
});
it("adds the Spring Boot console option once when resolving the module classpath", async () => {
await startRun({ ...config, kind: "spring-boot", module: "app" });
expect(ipc.runStart).toHaveBeenCalledWith(
expect.objectContaining({
args: ["-Dspring.output.ansi.enabled=always", "-cp", "/p/app/target/classes", "a.Main"],
}),
expect.anything(),
);
});
it("keeps an explicitly stored root module", async () => {
expect(await findJavaModule({ ...config, module: "", cwd: "sibling" })).toBe("");
expect(ipc.findFiles).not.toHaveBeenCalled();
+8 -2
View File
@@ -17,7 +17,13 @@ import {
type RunId,
type TestCaseReport,
} from "@/platform/ipc";
import { findJavaModule, isJvmApplication, parseJavaArgs, prepareJavaLaunch } from "@/platform/javaLaunch";
import {
consoleVmArgs,
findJavaModule,
isJvmApplication,
parseJavaArgs,
prepareJavaLaunch,
} from "@/platform/javaLaunch";
import { downloadJdkActions } from "@/platform/jdk";
import { useProject } from "@/platform/project";
import { executeAction } from "@/platform/actions";
@@ -721,7 +727,7 @@ async function resolveJvmApplication(sessionId: string, config: RunConfig): Prom
say(`No main class in '${config.name}': expected java [options] -cp \${classpath} <main class> [args]\n`, "stderr");
return null;
}
if (!parsed.classpath) return config;
if (!parsed.classpath) return { ...config, args: [...consoleVmArgs(config), ...config.args] };
const rt = runtime(sessionId)!;
const request = { id: nextRequestId(), controller: new AbortController() };
rt.resolveRequest = request;