fix(ast-grep): add isValidBinary check to all path resolutions

- Check file size >10KB to filter out placeholder files
- Check cached binary first
- Then npm package paths with validation
- Homebrew paths as last resort
- Fixes SIGTRAP/ENOEXEC from invalid binaries
This commit is contained in:
YeonGyu-Kim
2025-12-05 22:18:09 +09:00
parent 54e13e4330
commit 3bcb869a5d

View File

@@ -1,6 +1,10 @@
import { createRequire } from "module"
import { dirname, join } from "path"
import { existsSync, statSync } from "fs" import { existsSync, statSync } from "fs"
import { getCachedBinaryPath } from "./downloader" import { getCachedBinaryPath } from "./downloader"
type Platform = "darwin" | "linux" | "win32" | "unsupported"
function isValidBinary(filePath: string): boolean { function isValidBinary(filePath: string): boolean {
try { try {
return statSync(filePath).size > 10000 return statSync(filePath).size > 10000
@@ -9,12 +13,70 @@ function isValidBinary(filePath: string): boolean {
} }
} }
function getPlatformPackageName(): string | null {
const platform = process.platform as Platform
const arch = process.arch
const platformMap: Record<string, string> = {
"darwin-arm64": "@ast-grep/cli-darwin-arm64",
"darwin-x64": "@ast-grep/cli-darwin-x64",
"linux-arm64": "@ast-grep/cli-linux-arm64-gnu",
"linux-x64": "@ast-grep/cli-linux-x64-gnu",
"win32-x64": "@ast-grep/cli-win32-x64-msvc",
"win32-arm64": "@ast-grep/cli-win32-arm64-msvc",
"win32-ia32": "@ast-grep/cli-win32-ia32-msvc",
}
return platformMap[`${platform}-${arch}`] ?? null
}
export function findSgCliPathSync(): string | null { export function findSgCliPathSync(): string | null {
const binaryName = process.platform === "win32" ? "sg.exe" : "sg"
const cachedPath = getCachedBinaryPath() const cachedPath = getCachedBinaryPath()
if (cachedPath && isValidBinary(cachedPath)) { if (cachedPath && isValidBinary(cachedPath)) {
return 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) && isValidBinary(sgPath)) {
return sgPath
}
} catch {
// @ast-grep/cli not installed
}
const platformPkg = getPlatformPackageName()
if (platformPkg) {
try {
const require = createRequire(import.meta.url)
const pkgPath = require.resolve(`${platformPkg}/package.json`)
const pkgDir = dirname(pkgPath)
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep"
const binaryPath = join(pkgDir, astGrepName)
if (existsSync(binaryPath) && isValidBinary(binaryPath)) {
return binaryPath
}
} catch {
// Platform-specific package not installed
}
}
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
}
}
}
return null return null
} }