Merge pull request #855 from luojiyin1987/fix/doctor-windows-opencode
fix: handle opencode.ps1 in doctor on Windows
This commit is contained in:
commit
8391b8a7a5
@ -43,6 +43,94 @@ describe("opencode check", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("command helpers", () => {
|
||||||
|
it("selects where on Windows", () => {
|
||||||
|
// #given win32 platform
|
||||||
|
// #when selecting lookup command
|
||||||
|
// #then should use where
|
||||||
|
expect(opencode.getBinaryLookupCommand("win32")).toBe("where")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("selects which on non-Windows", () => {
|
||||||
|
// #given linux platform
|
||||||
|
// #when selecting lookup command
|
||||||
|
// #then should use which
|
||||||
|
expect(opencode.getBinaryLookupCommand("linux")).toBe("which")
|
||||||
|
expect(opencode.getBinaryLookupCommand("darwin")).toBe("which")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("parses command output into paths", () => {
|
||||||
|
// #given raw output with multiple lines and spaces
|
||||||
|
const output = "C:\\\\bin\\\\opencode.ps1\r\nC:\\\\bin\\\\opencode.exe\n\n"
|
||||||
|
|
||||||
|
// #when parsing
|
||||||
|
const paths = opencode.parseBinaryPaths(output)
|
||||||
|
|
||||||
|
// #then should return trimmed, non-empty paths
|
||||||
|
expect(paths).toEqual(["C:\\\\bin\\\\opencode.ps1", "C:\\\\bin\\\\opencode.exe"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("prefers exe/cmd/bat over ps1 on Windows", () => {
|
||||||
|
// #given windows paths
|
||||||
|
const paths = [
|
||||||
|
"C:\\\\bin\\\\opencode.ps1",
|
||||||
|
"C:\\\\bin\\\\opencode.cmd",
|
||||||
|
"C:\\\\bin\\\\opencode.exe",
|
||||||
|
]
|
||||||
|
|
||||||
|
// #when selecting binary
|
||||||
|
const selected = opencode.selectBinaryPath(paths, "win32")
|
||||||
|
|
||||||
|
// #then should prefer exe
|
||||||
|
expect(selected).toBe("C:\\\\bin\\\\opencode.exe")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("falls back to ps1 when it is the only Windows candidate", () => {
|
||||||
|
// #given only ps1 path
|
||||||
|
const paths = ["C:\\\\bin\\\\opencode.ps1"]
|
||||||
|
|
||||||
|
// #when selecting binary
|
||||||
|
const selected = opencode.selectBinaryPath(paths, "win32")
|
||||||
|
|
||||||
|
// #then should return ps1 path
|
||||||
|
expect(selected).toBe("C:\\\\bin\\\\opencode.ps1")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("builds PowerShell command for ps1 on Windows", () => {
|
||||||
|
// #given a ps1 path on Windows
|
||||||
|
const command = opencode.buildVersionCommand(
|
||||||
|
"C:\\\\bin\\\\opencode.ps1",
|
||||||
|
"win32"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #when building command
|
||||||
|
// #then should use PowerShell
|
||||||
|
expect(command).toEqual([
|
||||||
|
"powershell",
|
||||||
|
"-NoProfile",
|
||||||
|
"-ExecutionPolicy",
|
||||||
|
"Bypass",
|
||||||
|
"-File",
|
||||||
|
"C:\\\\bin\\\\opencode.ps1",
|
||||||
|
"--version",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("builds direct command for non-ps1 binaries", () => {
|
||||||
|
// #given an exe on Windows and a binary on linux
|
||||||
|
const winCommand = opencode.buildVersionCommand(
|
||||||
|
"C:\\\\bin\\\\opencode.exe",
|
||||||
|
"win32"
|
||||||
|
)
|
||||||
|
const linuxCommand = opencode.buildVersionCommand("opencode", "linux")
|
||||||
|
|
||||||
|
// #when building commands
|
||||||
|
// #then should execute directly
|
||||||
|
expect(winCommand).toEqual(["C:\\\\bin\\\\opencode.exe", "--version"])
|
||||||
|
expect(linuxCommand).toEqual(["opencode", "--version"])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("getOpenCodeInfo", () => {
|
describe("getOpenCodeInfo", () => {
|
||||||
it("returns installed: false when binary not found", async () => {
|
it("returns installed: false when binary not found", async () => {
|
||||||
// #given no opencode binary
|
// #given no opencode binary
|
||||||
|
|||||||
@ -1,14 +1,70 @@
|
|||||||
import type { CheckResult, CheckDefinition, OpenCodeInfo } from "../types"
|
import type { CheckResult, CheckDefinition, OpenCodeInfo } from "../types"
|
||||||
import { CHECK_IDS, CHECK_NAMES, MIN_OPENCODE_VERSION, OPENCODE_BINARIES } from "../constants"
|
import { CHECK_IDS, CHECK_NAMES, MIN_OPENCODE_VERSION, OPENCODE_BINARIES } from "../constants"
|
||||||
|
|
||||||
|
const WINDOWS_EXECUTABLE_EXTS = [".exe", ".cmd", ".bat", ".ps1"]
|
||||||
|
|
||||||
|
export function getBinaryLookupCommand(platform: NodeJS.Platform): "which" | "where" {
|
||||||
|
return platform === "win32" ? "where" : "which"
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseBinaryPaths(output: string): string[] {
|
||||||
|
return output
|
||||||
|
.split(/\r?\n/)
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.filter((line) => line.length > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectBinaryPath(
|
||||||
|
paths: string[],
|
||||||
|
platform: NodeJS.Platform
|
||||||
|
): string | null {
|
||||||
|
if (paths.length === 0) return null
|
||||||
|
if (platform !== "win32") return paths[0]
|
||||||
|
|
||||||
|
const normalized = paths.map((path) => path.toLowerCase())
|
||||||
|
for (const ext of WINDOWS_EXECUTABLE_EXTS) {
|
||||||
|
const index = normalized.findIndex((path) => path.endsWith(ext))
|
||||||
|
if (index !== -1) return paths[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildVersionCommand(
|
||||||
|
binaryPath: string,
|
||||||
|
platform: NodeJS.Platform
|
||||||
|
): string[] {
|
||||||
|
if (
|
||||||
|
platform === "win32" &&
|
||||||
|
binaryPath.toLowerCase().endsWith(".ps1")
|
||||||
|
) {
|
||||||
|
return [
|
||||||
|
"powershell",
|
||||||
|
"-NoProfile",
|
||||||
|
"-ExecutionPolicy",
|
||||||
|
"Bypass",
|
||||||
|
"-File",
|
||||||
|
binaryPath,
|
||||||
|
"--version",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return [binaryPath, "--version"]
|
||||||
|
}
|
||||||
|
|
||||||
export async function findOpenCodeBinary(): Promise<{ binary: string; path: string } | null> {
|
export async function findOpenCodeBinary(): Promise<{ binary: string; path: string } | null> {
|
||||||
for (const binary of OPENCODE_BINARIES) {
|
for (const binary of OPENCODE_BINARIES) {
|
||||||
try {
|
try {
|
||||||
const proc = Bun.spawn(["which", binary], { stdout: "pipe", stderr: "pipe" })
|
const lookupCommand = getBinaryLookupCommand(process.platform)
|
||||||
|
const proc = Bun.spawn([lookupCommand, binary], { stdout: "pipe", stderr: "pipe" })
|
||||||
const output = await new Response(proc.stdout).text()
|
const output = await new Response(proc.stdout).text()
|
||||||
await proc.exited
|
await proc.exited
|
||||||
if (proc.exitCode === 0) {
|
if (proc.exitCode === 0) {
|
||||||
return { binary, path: output.trim() }
|
const paths = parseBinaryPaths(output)
|
||||||
|
const selectedPath = selectBinaryPath(paths, process.platform)
|
||||||
|
if (selectedPath) {
|
||||||
|
return { binary, path: selectedPath }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
continue
|
continue
|
||||||
@ -17,9 +73,13 @@ export async function findOpenCodeBinary(): Promise<{ binary: string; path: stri
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOpenCodeVersion(binary: string): Promise<string | null> {
|
export async function getOpenCodeVersion(
|
||||||
|
binaryPath: string,
|
||||||
|
platform: NodeJS.Platform = process.platform
|
||||||
|
): Promise<string | null> {
|
||||||
try {
|
try {
|
||||||
const proc = Bun.spawn([binary, "--version"], { stdout: "pipe", stderr: "pipe" })
|
const command = buildVersionCommand(binaryPath, platform)
|
||||||
|
const proc = Bun.spawn(command, { stdout: "pipe", stderr: "pipe" })
|
||||||
const output = await new Response(proc.stdout).text()
|
const output = await new Response(proc.stdout).text()
|
||||||
await proc.exited
|
await proc.exited
|
||||||
if (proc.exitCode === 0) {
|
if (proc.exitCode === 0) {
|
||||||
@ -61,7 +121,7 @@ export async function getOpenCodeInfo(): Promise<OpenCodeInfo> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const version = await getOpenCodeVersion(binaryInfo.binary)
|
const version = await getOpenCodeVersion(binaryInfo.path ?? binaryInfo.binary)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
installed: true,
|
installed: true,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user