feat(start-work): add worktree path detection
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
808a50d808
commit
9a94e12065
79
src/hooks/start-work/worktree-detector.test.ts
Normal file
79
src/hooks/start-work/worktree-detector.test.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
|
import { describe, expect, test, spyOn, beforeEach, afterEach } from "bun:test"
|
||||||
|
import * as childProcess from "node:child_process"
|
||||||
|
import { detectWorktreePath } from "./worktree-detector"
|
||||||
|
|
||||||
|
describe("detectWorktreePath", () => {
|
||||||
|
let execFileSyncSpy: ReturnType<typeof spyOn>
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
execFileSyncSpy = spyOn(childProcess, "execFileSync").mockImplementation(
|
||||||
|
((_file: string, _args: string[]) => "") as typeof childProcess.execFileSync,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
execFileSyncSpy.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("when directory is a valid git worktree", () => {
|
||||||
|
test("#given valid git dir #when detecting #then returns worktree root path", () => {
|
||||||
|
execFileSyncSpy.mockImplementation(
|
||||||
|
((_file: string, _args: string[]) => "/home/user/my-repo\n") as typeof childProcess.execFileSync,
|
||||||
|
)
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = detectWorktreePath("/home/user/my-repo/src")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBe("/home/user/my-repo")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("#given git output with trailing newline #when detecting #then trims output", () => {
|
||||||
|
execFileSyncSpy.mockImplementation(
|
||||||
|
((_file: string, _args: string[]) => "/projects/worktree-a\n\n") as typeof childProcess.execFileSync,
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = detectWorktreePath("/projects/worktree-a")
|
||||||
|
|
||||||
|
expect(result).toBe("/projects/worktree-a")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("#given valid dir #when detecting #then calls git rev-parse with cwd", () => {
|
||||||
|
execFileSyncSpy.mockImplementation(
|
||||||
|
((_file: string, _args: string[]) => "/repo\n") as typeof childProcess.execFileSync,
|
||||||
|
)
|
||||||
|
|
||||||
|
detectWorktreePath("/repo/some/subdir")
|
||||||
|
|
||||||
|
expect(execFileSyncSpy).toHaveBeenCalledWith(
|
||||||
|
"git",
|
||||||
|
["rev-parse", "--show-toplevel"],
|
||||||
|
expect.objectContaining({ cwd: "/repo/some/subdir" }),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("when directory is not a git worktree", () => {
|
||||||
|
test("#given non-git directory #when detecting #then returns null", () => {
|
||||||
|
execFileSyncSpy.mockImplementation((_file: string, _args: string[]) => {
|
||||||
|
throw new Error("not a git repository")
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = detectWorktreePath("/tmp/not-a-repo")
|
||||||
|
|
||||||
|
expect(result).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("#given non-existent directory #when detecting #then returns null", () => {
|
||||||
|
execFileSyncSpy.mockImplementation((_file: string, _args: string[]) => {
|
||||||
|
throw new Error("ENOENT: no such file or directory")
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = detectWorktreePath("/nonexistent/path")
|
||||||
|
|
||||||
|
expect(result).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
14
src/hooks/start-work/worktree-detector.ts
Normal file
14
src/hooks/start-work/worktree-detector.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { execFileSync } from "node:child_process"
|
||||||
|
|
||||||
|
export function detectWorktreePath(directory: string): string | null {
|
||||||
|
try {
|
||||||
|
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
||||||
|
cwd: directory,
|
||||||
|
encoding: "utf-8",
|
||||||
|
timeout: 5000,
|
||||||
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
|
}).trim()
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user