Files
orcs-code/src/utils/ripgrep.test.ts
KRATOS ee0d930093 fix(ripgrep): use @vscode/ripgrep package as the builtin source (#911) (#932)
The vendored-binary lookup at vendor/ripgrep/<arch>-<platform>/rg never
resolved in this fork — that directory does not ship — so users without
a system rg had no working fallback. Switch to the @vscode/ripgrep
package so Microsoft maintains the platform/arch matrix and the binary
is delivered via npm.

- src/utils/ripgrep.ts: replace hand-rolled vendor-path resolution with
  rgPath from @vscode/ripgrep. Lazy require so a missing package falls
  through to the system rg branch instead of throwing at import.
  Drop builtinExists from the config args; builtinCommand is now a
  string-or-null. The system override (USE_BUILTIN_RIPGREP=0), the
  Bun-compiled standalone embedded mode, the macOS codesign hook, and
  all retry/timeout/error logic are preserved untouched.
- scripts/build.ts: mark @vscode/ripgrep as external. The package
  resolves rgPath via __dirname at runtime, so bundling would freeze
  the build host's absolute path into dist/cli.mjs.
- src/utils/ripgrep.test.ts: update for the new config shape and add
  tests covering USE_BUILTIN_RIPGREP=0, embedded mode, last-resort
  fallback, and null builtin path.

Tested locally on Linux (Bun 1.3.13). macOS (codesign hook) and
Windows (rg.exe extension) need contributor verification.
2026-04-30 00:58:46 +08:00

116 lines
3.2 KiB
TypeScript

import { expect, test } from 'bun:test'
import path from 'path'
import { resolveRipgrepConfig, wrapRipgrepUnavailableError } from './ripgrep.js'
const MOCK_BUILTIN_PATH = path.normalize(
process.platform === 'win32'
? `node_modules/@vscode/ripgrep/bin/rg.exe`
: `node_modules/@vscode/ripgrep/bin/rg`,
)
test('falls back to system rg when @vscode/ripgrep cannot be resolved', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: false,
bundledMode: false,
builtinCommand: null,
systemExecutablePath: '/usr/bin/rg',
processExecPath: '/fake/bun',
})
expect(config).toMatchObject({
mode: 'system',
command: 'rg',
args: [],
})
})
test('uses builtin @vscode/ripgrep path when the package resolves', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: false,
bundledMode: false,
builtinCommand: MOCK_BUILTIN_PATH,
systemExecutablePath: '/usr/bin/rg',
processExecPath: '/fake/bun',
})
expect(config).toMatchObject({
mode: 'builtin',
command: MOCK_BUILTIN_PATH,
args: [],
})
})
test('honors USE_BUILTIN_RIPGREP=0 by selecting system rg even when builtin is available', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: true,
bundledMode: false,
builtinCommand: MOCK_BUILTIN_PATH,
systemExecutablePath: '/usr/bin/rg',
processExecPath: '/fake/bun',
})
expect(config).toMatchObject({
mode: 'system',
command: 'rg',
args: [],
})
})
test('keeps embedded mode for Bun-compiled standalone executables', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: false,
bundledMode: true,
builtinCommand: null,
systemExecutablePath: '/usr/bin/rg',
processExecPath: '/opt/openclaude/bin/openclaude',
})
expect(config).toMatchObject({
mode: 'embedded',
command: '/opt/openclaude/bin/openclaude',
args: ['--no-config'],
argv0: 'rg',
})
})
test('falls through to system rg as a last resort even when not on PATH', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: false,
bundledMode: false,
builtinCommand: null,
systemExecutablePath: 'rg',
processExecPath: '/fake/bun',
})
expect(config).toMatchObject({
mode: 'system',
command: 'rg',
args: [],
})
})
test('wrapRipgrepUnavailableError explains missing packaged fallback', () => {
const error = wrapRipgrepUnavailableError(
{ code: 'ENOENT', message: 'spawn rg ENOENT' },
{ mode: 'builtin', command: 'C:\\fake\\node_modules\\@vscode\\ripgrep\\bin\\rg.exe', args: [] },
'win32',
)
expect(error.name).toBe('RipgrepUnavailableError')
expect(error.code).toBe('ENOENT')
expect(error.message).toContain('packaged ripgrep fallback')
expect(error.message).toContain('winget install BurntSushi.ripgrep.MSVC')
})
test('wrapRipgrepUnavailableError explains missing system ripgrep', () => {
const error = wrapRipgrepUnavailableError(
{ code: 'ENOENT', message: 'spawn rg ENOENT' },
{ mode: 'system', command: 'rg', args: [] },
'linux',
)
expect(error.message).toContain('system ripgrep binary was not found on PATH')
expect(error.message).toContain('apt install ripgrep')
})