mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-03 15:43:45 +00:00
fix(windows): git path resolution for modified files across Git Bash, MSYS2, and Cygwin (#16422)
This commit is contained in:
@@ -25,6 +25,34 @@ async function writeConfig(dir: string, config: object, name = "opencode.json")
|
||||
await Filesystem.write(path.join(dir, name), JSON.stringify(config))
|
||||
}
|
||||
|
||||
async function check(map: (dir: string) => string) {
|
||||
if (process.platform !== "win32") return
|
||||
await using globalTmp = await tmpdir()
|
||||
await using tmp = await tmpdir({ git: true, config: { snapshot: true } })
|
||||
const prev = Global.Path.config
|
||||
;(Global.Path as { config: string }).config = globalTmp.path
|
||||
Config.global.reset()
|
||||
try {
|
||||
await writeConfig(globalTmp.path, {
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
snapshot: false,
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: map(tmp.path),
|
||||
fn: async () => {
|
||||
const cfg = await Config.get()
|
||||
expect(cfg.snapshot).toBe(true)
|
||||
expect(Instance.directory).toBe(Filesystem.resolve(tmp.path))
|
||||
expect(Instance.project.id).not.toBe("global")
|
||||
},
|
||||
})
|
||||
} finally {
|
||||
await Instance.disposeAll()
|
||||
;(Global.Path as { config: string }).config = prev
|
||||
Config.global.reset()
|
||||
}
|
||||
}
|
||||
|
||||
test("loads config with defaults when no files exist", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Instance.provide({
|
||||
@@ -56,6 +84,23 @@ test("loads JSON config file", async () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("loads project config from Git Bash and MSYS2 paths on Windows", async () => {
|
||||
// Git Bash and MSYS2 both use /<drive>/... paths on Windows.
|
||||
await check((dir) => {
|
||||
const drive = dir[0].toLowerCase()
|
||||
const rest = dir.slice(2).replaceAll("\\", "/")
|
||||
return `/${drive}${rest}`
|
||||
})
|
||||
})
|
||||
|
||||
test("loads project config from Cygwin paths on Windows", async () => {
|
||||
await check((dir) => {
|
||||
const drive = dir[0].toLowerCase()
|
||||
const rest = dir.slice(2).replaceAll("\\", "/")
|
||||
return `/cygdrive/${drive}${rest}`
|
||||
})
|
||||
})
|
||||
|
||||
test("ignores legacy tui keys in opencode config", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
|
||||
@@ -440,4 +440,67 @@ describe("filesystem", () => {
|
||||
expect(await fs.readFile(filepath, "utf-8")).toBe(content)
|
||||
})
|
||||
})
|
||||
|
||||
describe("resolve()", () => {
|
||||
test("resolves slash-prefixed drive paths on Windows", async () => {
|
||||
if (process.platform !== "win32") return
|
||||
await using tmp = await tmpdir()
|
||||
const forward = tmp.path.replaceAll("\\", "/")
|
||||
expect(Filesystem.resolve(`/${forward}`)).toBe(Filesystem.normalizePath(tmp.path))
|
||||
})
|
||||
|
||||
test("resolves slash-prefixed drive roots on Windows", async () => {
|
||||
if (process.platform !== "win32") return
|
||||
await using tmp = await tmpdir()
|
||||
const drive = tmp.path[0].toUpperCase()
|
||||
expect(Filesystem.resolve(`/${drive}:`)).toBe(Filesystem.resolve(`${drive}:/`))
|
||||
})
|
||||
|
||||
test("resolves Git Bash and MSYS2 paths on Windows", async () => {
|
||||
// Git Bash and MSYS2 both use /<drive>/... paths on Windows.
|
||||
if (process.platform !== "win32") return
|
||||
await using tmp = await tmpdir()
|
||||
const drive = tmp.path[0].toLowerCase()
|
||||
const rest = tmp.path.slice(2).replaceAll("\\", "/")
|
||||
expect(Filesystem.resolve(`/${drive}${rest}`)).toBe(Filesystem.normalizePath(tmp.path))
|
||||
})
|
||||
|
||||
test("resolves Git Bash and MSYS2 drive roots on Windows", async () => {
|
||||
// Git Bash and MSYS2 both use /<drive> paths on Windows.
|
||||
if (process.platform !== "win32") return
|
||||
await using tmp = await tmpdir()
|
||||
const drive = tmp.path[0].toLowerCase()
|
||||
expect(Filesystem.resolve(`/${drive}`)).toBe(Filesystem.resolve(`${drive.toUpperCase()}:/`))
|
||||
})
|
||||
|
||||
test("resolves Cygwin paths on Windows", async () => {
|
||||
if (process.platform !== "win32") return
|
||||
await using tmp = await tmpdir()
|
||||
const drive = tmp.path[0].toLowerCase()
|
||||
const rest = tmp.path.slice(2).replaceAll("\\", "/")
|
||||
expect(Filesystem.resolve(`/cygdrive/${drive}${rest}`)).toBe(Filesystem.normalizePath(tmp.path))
|
||||
})
|
||||
|
||||
test("resolves Cygwin drive roots on Windows", async () => {
|
||||
if (process.platform !== "win32") return
|
||||
await using tmp = await tmpdir()
|
||||
const drive = tmp.path[0].toLowerCase()
|
||||
expect(Filesystem.resolve(`/cygdrive/${drive}`)).toBe(Filesystem.resolve(`${drive.toUpperCase()}:/`))
|
||||
})
|
||||
|
||||
test("resolves WSL mount paths on Windows", async () => {
|
||||
if (process.platform !== "win32") return
|
||||
await using tmp = await tmpdir()
|
||||
const drive = tmp.path[0].toLowerCase()
|
||||
const rest = tmp.path.slice(2).replaceAll("\\", "/")
|
||||
expect(Filesystem.resolve(`/mnt/${drive}${rest}`)).toBe(Filesystem.normalizePath(tmp.path))
|
||||
})
|
||||
|
||||
test("resolves WSL mount roots on Windows", async () => {
|
||||
if (process.platform !== "win32") return
|
||||
await using tmp = await tmpdir()
|
||||
const drive = tmp.path[0].toLowerCase()
|
||||
expect(Filesystem.resolve(`/mnt/${drive}`)).toBe(Filesystem.resolve(`${drive.toUpperCase()}:/`))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,6 +22,13 @@ function env(PATH: string): NodeJS.ProcessEnv {
|
||||
}
|
||||
}
|
||||
|
||||
function envPath(Path: string): NodeJS.ProcessEnv {
|
||||
return {
|
||||
Path,
|
||||
PathExt: process.env["PathExt"] ?? process.env["PATHEXT"],
|
||||
}
|
||||
}
|
||||
|
||||
function same(a: string | null, b: string) {
|
||||
if (process.platform === "win32") {
|
||||
expect(a?.toLowerCase()).toBe(b.toLowerCase())
|
||||
@@ -79,4 +86,15 @@ describe("util.which", () => {
|
||||
|
||||
expect(which("pathext", { PATH: bin, PATHEXT: ".CMD" })).toBe(file)
|
||||
})
|
||||
|
||||
test("uses Windows Path casing fallback", async () => {
|
||||
if (process.platform !== "win32") return
|
||||
|
||||
await using tmp = await tmpdir()
|
||||
const bin = path.join(tmp.path, "bin")
|
||||
await fs.mkdir(bin)
|
||||
const file = await cmd(bin, "mixed")
|
||||
|
||||
same(which("mixed", envPath(bin)), file)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user