refactor: unwrap Shell namespace + self-reexport (#22964)
This commit is contained in:
@@ -8,103 +8,103 @@ import { setTimeout as sleep } from "node:timers/promises"
|
|||||||
|
|
||||||
const SIGKILL_TIMEOUT_MS = 200
|
const SIGKILL_TIMEOUT_MS = 200
|
||||||
|
|
||||||
export namespace Shell {
|
const BLACKLIST = new Set(["fish", "nu"])
|
||||||
const BLACKLIST = new Set(["fish", "nu"])
|
const LOGIN = new Set(["bash", "dash", "fish", "ksh", "sh", "zsh"])
|
||||||
const LOGIN = new Set(["bash", "dash", "fish", "ksh", "sh", "zsh"])
|
const POSIX = new Set(["bash", "dash", "ksh", "sh", "zsh"])
|
||||||
const POSIX = new Set(["bash", "dash", "ksh", "sh", "zsh"])
|
|
||||||
|
|
||||||
export async function killTree(proc: ChildProcess, opts?: { exited?: () => boolean }): Promise<void> {
|
export async function killTree(proc: ChildProcess, opts?: { exited?: () => boolean }): Promise<void> {
|
||||||
const pid = proc.pid
|
const pid = proc.pid
|
||||||
if (!pid || opts?.exited?.()) return
|
if (!pid || opts?.exited?.()) return
|
||||||
|
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], {
|
const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], {
|
||||||
stdio: "ignore",
|
stdio: "ignore",
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
})
|
|
||||||
killer.once("exit", () => resolve())
|
|
||||||
killer.once("error", () => resolve())
|
|
||||||
})
|
})
|
||||||
return
|
killer.once("exit", () => resolve())
|
||||||
}
|
killer.once("error", () => resolve())
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
process.kill(-pid, "SIGTERM")
|
process.kill(-pid, "SIGTERM")
|
||||||
await sleep(SIGKILL_TIMEOUT_MS)
|
await sleep(SIGKILL_TIMEOUT_MS)
|
||||||
if (!opts?.exited?.()) {
|
if (!opts?.exited?.()) {
|
||||||
process.kill(-pid, "SIGKILL")
|
process.kill(-pid, "SIGKILL")
|
||||||
}
|
}
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
proc.kill("SIGTERM")
|
proc.kill("SIGTERM")
|
||||||
await sleep(SIGKILL_TIMEOUT_MS)
|
await sleep(SIGKILL_TIMEOUT_MS)
|
||||||
if (!opts?.exited?.()) {
|
if (!opts?.exited?.()) {
|
||||||
proc.kill("SIGKILL")
|
proc.kill("SIGKILL")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function full(file: string) {
|
|
||||||
if (process.platform !== "win32") return file
|
|
||||||
const shell = Filesystem.windowsPath(file)
|
|
||||||
if (path.win32.dirname(shell) !== ".") {
|
|
||||||
if (shell.startsWith("/") && name(shell) === "bash") return gitbash() || shell
|
|
||||||
return shell
|
|
||||||
}
|
|
||||||
return which(shell) || shell
|
|
||||||
}
|
|
||||||
|
|
||||||
function pick() {
|
|
||||||
const pwsh = which("pwsh.exe")
|
|
||||||
if (pwsh) return pwsh
|
|
||||||
const powershell = which("powershell.exe")
|
|
||||||
if (powershell) return powershell
|
|
||||||
}
|
|
||||||
|
|
||||||
function select(file: string | undefined, opts?: { acceptable?: boolean }) {
|
|
||||||
if (file && (!opts?.acceptable || !BLACKLIST.has(name(file)))) return full(file)
|
|
||||||
if (process.platform === "win32") {
|
|
||||||
const shell = pick()
|
|
||||||
if (shell) return shell
|
|
||||||
}
|
|
||||||
return fallback()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function gitbash() {
|
|
||||||
if (process.platform !== "win32") return
|
|
||||||
if (Flag.OPENCODE_GIT_BASH_PATH) return Flag.OPENCODE_GIT_BASH_PATH
|
|
||||||
const git = which("git")
|
|
||||||
if (!git) return
|
|
||||||
const file = path.join(git, "..", "..", "bin", "bash.exe")
|
|
||||||
if (Filesystem.stat(file)?.size) return file
|
|
||||||
}
|
|
||||||
|
|
||||||
function fallback() {
|
|
||||||
if (process.platform === "win32") {
|
|
||||||
const file = gitbash()
|
|
||||||
if (file) return file
|
|
||||||
return process.env.COMSPEC || "cmd.exe"
|
|
||||||
}
|
|
||||||
if (process.platform === "darwin") return "/bin/zsh"
|
|
||||||
const bash = which("bash")
|
|
||||||
if (bash) return bash
|
|
||||||
return "/bin/sh"
|
|
||||||
}
|
|
||||||
|
|
||||||
export function name(file: string) {
|
|
||||||
if (process.platform === "win32") return path.win32.parse(Filesystem.windowsPath(file)).name.toLowerCase()
|
|
||||||
return path.basename(file).toLowerCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function login(file: string) {
|
|
||||||
return LOGIN.has(name(file))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function posix(file: string) {
|
|
||||||
return POSIX.has(name(file))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const preferred = lazy(() => select(process.env.SHELL))
|
|
||||||
|
|
||||||
export const acceptable = lazy(() => select(process.env.SHELL, { acceptable: true }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function full(file: string) {
|
||||||
|
if (process.platform !== "win32") return file
|
||||||
|
const shell = Filesystem.windowsPath(file)
|
||||||
|
if (path.win32.dirname(shell) !== ".") {
|
||||||
|
if (shell.startsWith("/") && name(shell) === "bash") return gitbash() || shell
|
||||||
|
return shell
|
||||||
|
}
|
||||||
|
return which(shell) || shell
|
||||||
|
}
|
||||||
|
|
||||||
|
function pick() {
|
||||||
|
const pwsh = which("pwsh.exe")
|
||||||
|
if (pwsh) return pwsh
|
||||||
|
const powershell = which("powershell.exe")
|
||||||
|
if (powershell) return powershell
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(file: string | undefined, opts?: { acceptable?: boolean }) {
|
||||||
|
if (file && (!opts?.acceptable || !BLACKLIST.has(name(file)))) return full(file)
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
const shell = pick()
|
||||||
|
if (shell) return shell
|
||||||
|
}
|
||||||
|
return fallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function gitbash() {
|
||||||
|
if (process.platform !== "win32") return
|
||||||
|
if (Flag.OPENCODE_GIT_BASH_PATH) return Flag.OPENCODE_GIT_BASH_PATH
|
||||||
|
const git = which("git")
|
||||||
|
if (!git) return
|
||||||
|
const file = path.join(git, "..", "..", "bin", "bash.exe")
|
||||||
|
if (Filesystem.stat(file)?.size) return file
|
||||||
|
}
|
||||||
|
|
||||||
|
function fallback() {
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
const file = gitbash()
|
||||||
|
if (file) return file
|
||||||
|
return process.env.COMSPEC || "cmd.exe"
|
||||||
|
}
|
||||||
|
if (process.platform === "darwin") return "/bin/zsh"
|
||||||
|
const bash = which("bash")
|
||||||
|
if (bash) return bash
|
||||||
|
return "/bin/sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
export function name(file: string) {
|
||||||
|
if (process.platform === "win32") return path.win32.parse(Filesystem.windowsPath(file)).name.toLowerCase()
|
||||||
|
return path.basename(file).toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function login(file: string) {
|
||||||
|
return LOGIN.has(name(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function posix(file: string) {
|
||||||
|
return POSIX.has(name(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const preferred = lazy(() => select(process.env.SHELL))
|
||||||
|
|
||||||
|
export const acceptable = lazy(() => select(process.env.SHELL, { acceptable: true }))
|
||||||
|
|
||||||
|
export * as Shell from "./shell"
|
||||||
|
|||||||
Reference in New Issue
Block a user