Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/services/ripgrep/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,20 @@ vi.mock("../../../utils/fs", () => ({
fileExistsAtPath: vi.fn(),
}))

vi.mock("fs", () => ({
existsSync: vi.fn(),
}))

vi.mock("module", () => ({
createRequire: vi.fn(),
}))

import * as fs from "fs"
import { createRequire } from "module"

const mockFileExists = vi.mocked(fileExistsAtPath)
const mockExistsSync = vi.mocked(fs.existsSync)
const mockCreateRequire = vi.mocked(createRequire)

describe("Ripgrep line truncation", () => {
// The default MAX_LINE_LENGTH is 500 in the implementation
Expand Down Expand Up @@ -67,6 +80,9 @@ describe("getBinPath", () => {
beforeEach(() => {
mockFileExists.mockReset()
mockFileExists.mockResolvedValue(false)
mockExistsSync.mockReset()
mockExistsSync.mockReturnValue(false)
mockCreateRequire.mockReset()
})

it("resolves ripgrep from the classic @vscode/ripgrep layout", async () => {
Expand Down Expand Up @@ -95,4 +111,29 @@ describe("getBinPath", () => {

expect(await getBinPath(appRoot)).toBeUndefined()
})

// Regression test for https://github.com/Zoo-Code-Org/Zoo-Code/issues/1024
// VS Code 1.130+ ships @vscode/ripgrep >=1.18, where the binary lives in a
// platform-specific optional package resolved via the wrapper's package.json.
// None of the six hardcoded candidate paths match this layout, so getBinPath
// returns undefined on affected Windows installs, causing every task to hang.
it("resolves ripgrep via the @vscode/ripgrep >=1.18 platform-package layout", async () => {
const platformBin = `/vscode/ripgrep-${process.platform}-${process.arch}/bin/${binName}`

// All static candidates miss.
mockFileExists.mockResolvedValue(false)

// Simulate @vscode/ripgrep wrapper manifest existing and resolving to the platform bin.
mockExistsSync.mockReturnValue(true)
const mockRequireFromWrapper = { resolve: vi.fn().mockReturnValue(platformBin) }
const mockRequireFromApp = { resolve: vi.fn().mockReturnValue("/vscode/ripgrep/index.js") }
mockCreateRequire
.mockReturnValueOnce(mockRequireFromApp as unknown as NodeRequire)
.mockReturnValueOnce(mockRequireFromWrapper as unknown as NodeRequire)

// The resolved platform bin exists on disk.
mockFileExists.mockImplementation(async (p: string) => p === platformBin)

expect(await getBinPath(appRoot)).toBe(platformBin)
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
})
30 changes: 27 additions & 3 deletions src/services/ripgrep/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as childProcess from "child_process"
import * as fs from "fs"
import * as path from "path"
import * as readline from "readline"
import { createRequire } from "module"

import * as vscode from "vscode"

Expand Down Expand Up @@ -104,19 +106,41 @@ export function ripgrepCandidatePaths(vscodeAppRoot: string): readonly string[]
]
}

/**
* Resolves ripgrep for @vscode/ripgrep >=1.18, which ships the binary inside a
* platform-specific optional package (e.g. @vscode/ripgrep-win32-x64) rather
* than directly in @vscode/ripgrep/bin/. VS Code 1.130+ uses this layout.
*/
export function resolvePlatformRipgrepPath(vscodeAppRoot: string): string | undefined {
try {
const wrapperManifest = path.join(vscodeAppRoot, "node_modules", "@vscode", "ripgrep", "package.json")
if (!fs.existsSync(wrapperManifest)) return undefined
const requireFromApp = createRequire(path.join(vscodeAppRoot, "package.json"))
const wrapperEntry = requireFromApp.resolve("@vscode/ripgrep")
const requireFromWrapper = createRequire(wrapperEntry)
return requireFromWrapper.resolve(`@vscode/ripgrep-${process.platform}-${process.arch}/bin/${binName}`)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
} catch {
return undefined
}
}

/**
* Get the path to the ripgrep binary shipped inside the VS Code installation.
*
* Both the long-standing `@vscode/ripgrep` layout and the newer
* `@vscode/ripgrep-universal` layout are checked — the latter is what VS Code
* Insiders' staged-install builds use (see microsoft/vscode#252063).
* Checks the long-standing @vscode/ripgrep and @vscode/ripgrep-universal static
* layouts first, then falls back to the @vscode/ripgrep >=1.18 platform-package
* layout used by VS Code 1.130+ (see microsoft/vscode#252063).
*
* Returns `undefined` when ripgrep cannot be located.
*/
export async function getBinPath(vscodeAppRoot: string): Promise<string | undefined> {
for (const candidate of ripgrepCandidatePaths(vscodeAppRoot)) {
if (await fileExistsAtPath(candidate)) return candidate
}

const platformPackagePath = resolvePlatformRipgrepPath(vscodeAppRoot)
if (platformPackagePath && (await fileExistsAtPath(platformPackagePath))) return platformPackagePath

return undefined
}

Expand Down
Loading