fix(ast-grep): validate binary before using, prioritize homebrew path

- Add isValidBinary() check: file must be >10KB (placeholder files are ~100 bytes)
- Check homebrew paths first on macOS (most reliable)
- Check cached binary second
- npm package paths last (prone to placeholder issues)

Fixes ENOEXEC error when @ast-grep/cli has placeholder instead of real binary
This commit is contained in:
YeonGyu-Kim
2025-12-05 22:03:05 +09:00
parent f19cd8fc71
commit 316cdc1a62

View File

@@ -22,16 +22,39 @@ function getPlatformPackageName(): string | null {
return platformMap[`${platform}-${arch}`] ?? null
}
function isValidBinary(filePath: string): boolean {
try {
const stats = require("fs").statSync(filePath)
return stats.size > 10000
} catch {
return false
}
}
export function findSgCliPathSync(): string | null {
const binaryName = process.platform === "win32" ? "sg.exe" : "sg"
if (process.platform === "darwin") {
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"]
for (const path of homebrewPaths) {
if (existsSync(path) && isValidBinary(path)) {
return path
}
}
}
const cachedPath = getCachedBinaryPath()
if (cachedPath && isValidBinary(cachedPath)) {
return cachedPath
}
try {
const require = createRequire(import.meta.url)
const cliPkgPath = require.resolve("@ast-grep/cli/package.json")
const cliDir = dirname(cliPkgPath)
const sgPath = join(cliDir, binaryName)
if (existsSync(sgPath)) {
if (existsSync(sgPath) && isValidBinary(sgPath)) {
return sgPath
}
} catch {
@@ -47,7 +70,7 @@ export function findSgCliPathSync(): string | null {
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep"
const binaryPath = join(pkgDir, astGrepName)
if (existsSync(binaryPath)) {
if (existsSync(binaryPath) && isValidBinary(binaryPath)) {
return binaryPath
}
} catch {
@@ -55,20 +78,6 @@ export function findSgCliPathSync(): string | null {
}
}
if (process.platform === "darwin") {
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"]
for (const path of homebrewPaths) {
if (existsSync(path)) {
return path
}
}
}
const cachedPath = getCachedBinaryPath()
if (cachedPath) {
return cachedPath
}
return null
}