fix(debug): prefer dlv for go directory launches
Prefer directory-capable adapters when selecting a launch adapter for a resolved directory program. This keeps Go package directories on dlv in mixed projects that also expose native-debugger root markers such as a Makefile, instead of selecting gdb/lldb-dap first and rejecting the directory during validation. Added a regression test covering a Go module with both go.mod and Makefile plus local dlv/gdb adapter shims. Fixes #2020
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `debug` tool refusing every `dlv` launch on Go modules. The launch handler ran `validateLaunchProgram` before adapter selection and rejected any directory program with `launch program resolves to a directory`, while dlv's default `mode=debug` requires a Go package path (a directory or `.go` source file). Adapter resolution now precedes validation, the rejection only fires when the resolved adapter does not advertise `acceptsDirectoryProgram` (set on `dlv` in `dap/defaults.json`), and dlv's `mode` is derived from the program shape — directories and `.go` files launch as `mode=debug`, other files as `mode=exec` — so `omp` can debug both Go packages and pre-built binaries ([#2020](https://github.com/can1357/oh-my-pi/issues/2020)).
|
||||
- Fixed `debug` tool refusing every `dlv` launch on Go modules. The launch handler ran `validateLaunchProgram` before adapter selection and rejected any directory program with `launch program resolves to a directory`, while dlv's default `mode=debug` requires a Go package path (a directory or `.go` source file). Adapter resolution now precedes validation, directory programs prefer adapters that advertise `acceptsDirectoryProgram` before falling back to native extensionless debuggers, the rejection only fires when the resolved adapter does not advertise that flag (set on `dlv` in `dap/defaults.json`), and dlv's `mode` is derived from the program shape — directories and `.go` files launch as `mode=debug`, other files as `mode=exec` — so `omp` can debug both Go packages and pre-built binaries ([#2020](https://github.com/can1357/oh-my-pi/issues/2020)).
|
||||
|
||||
## [15.10.0] - 2026-06-06
|
||||
|
||||
|
||||
@@ -126,12 +126,19 @@ function sortAdaptersForLaunch(program: string, cwd: string, adapters: DapResolv
|
||||
return rootAware.map(entry => entry.adapter);
|
||||
}
|
||||
|
||||
export function selectLaunchAdapter(program: string, cwd: string, adapterName?: string): DapResolvedAdapter | null {
|
||||
export function selectLaunchAdapter(
|
||||
program: string,
|
||||
cwd: string,
|
||||
adapterName?: string,
|
||||
programKind: LaunchProgramKind = "file",
|
||||
): DapResolvedAdapter | null {
|
||||
if (adapterName) {
|
||||
return resolveAdapter(adapterName, cwd);
|
||||
}
|
||||
const matches = getMatchingAdapters(program, cwd);
|
||||
const sorted = sortAdaptersForLaunch(program, cwd, matches);
|
||||
const candidates =
|
||||
programKind === "directory" ? matches.filter(adapter => adapter.acceptsDirectoryProgram) : matches;
|
||||
const sorted = sortAdaptersForLaunch(program, cwd, candidates.length > 0 ? candidates : matches);
|
||||
return sorted[0] ?? null;
|
||||
}
|
||||
|
||||
|
||||
@@ -687,7 +687,7 @@ export class DebugTool implements AgentTool<typeof debugSchema, DebugToolDetails
|
||||
const commandCwd = params.cwd ? resolveToCwd(params.cwd, this.session.cwd) : this.session.cwd;
|
||||
const program = resolveToCwd(params.program, commandCwd);
|
||||
const programKind = await classifyLaunchProgram(program);
|
||||
const adapter = selectLaunchAdapter(program, commandCwd, params.adapter);
|
||||
const adapter = selectLaunchAdapter(program, commandCwd, params.adapter, programKind);
|
||||
if (!adapter) {
|
||||
if (params.adapter === "debugpy") {
|
||||
throw new ToolError("adapter 'debugpy' is not available: python not found in PATH");
|
||||
|
||||
@@ -411,6 +411,43 @@ describe("DebugTool launch validation", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("prefers directory-capable dlv over native adapters for extensionless Go package directories", async () => {
|
||||
const dapModule = await import("../../src/dap");
|
||||
const sessionLaunchSpy = spyOn(dapModule.dapSessionManager, "launch").mockImplementation(async opts => {
|
||||
throw Object.assign(new Error("captured launch"), { capturedOptions: opts });
|
||||
});
|
||||
try {
|
||||
const cwd = await fs.mkdtemp(path.join(os.tmpdir(), "omp-debug-dlv-mixed-roots-"));
|
||||
try {
|
||||
await fs.writeFile(path.join(cwd, "go.mod"), "module hello\n\ngo 1.22\n");
|
||||
await fs.writeFile(path.join(cwd, "Makefile"), "all:\n\tgo build ./...\n");
|
||||
await fs.mkdir(path.join(cwd, "bin"));
|
||||
await fs.writeFile(path.join(cwd, "bin", "dlv"), "");
|
||||
await fs.writeFile(path.join(cwd, "bin", "gdb"), "");
|
||||
await fs.mkdir(path.join(cwd, "cmd", "hello"), { recursive: true });
|
||||
const session: ToolSession = {
|
||||
cwd,
|
||||
hasUI: false,
|
||||
getSessionFile: () => null,
|
||||
getSessionSpawns: () => "*",
|
||||
settings: Settings.isolated({ "debug.enabled": true }),
|
||||
};
|
||||
const tool = new DebugTool(session);
|
||||
|
||||
await expect(tool.execute("call", { action: "launch", program: "cmd/hello" })).rejects.toThrow(
|
||||
/captured launch/,
|
||||
);
|
||||
const [opts] = sessionLaunchSpy.mock.calls[0]!;
|
||||
expect(opts.adapter.name).toBe("dlv");
|
||||
expect(opts.extraLaunchArguments).toEqual({ mode: "debug" });
|
||||
} finally {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
}
|
||||
} finally {
|
||||
sessionLaunchSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("dlv launch with a compiled binary switches mode from debug to exec", async () => {
|
||||
const dapModule = await import("../../src/dap");
|
||||
const dlvAdapter: DapResolvedAdapter = {
|
||||
|
||||
Reference in New Issue
Block a user