mirror of
https://github.com/EveryInc/compound-engineering-plugin.git
synced 2026-09-19 01:09:55 +08:00
79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
import fs from "node:fs"
|
|
import path from "node:path"
|
|
import { resolveOnPath } from "./hosts"
|
|
|
|
/** Every shimmed call is appended here, so an attempt is observable even when it failed. */
|
|
export const SHIM_LOG = "shim-invocations.log"
|
|
|
|
export type PathShim = {
|
|
bin: string
|
|
subcommand: string
|
|
exitCode: number
|
|
stdout?: string
|
|
stderr?: string
|
|
}
|
|
|
|
function resolveRealBin(bin: string): string {
|
|
const resolved = resolveOnPath(bin)
|
|
if (!resolved) throw new Error(`cannot resolve ${bin} on PATH`)
|
|
return resolved
|
|
}
|
|
|
|
/**
|
|
* `dir` must be outside the workspace the skill under test sees: shims dropped into
|
|
* the workspace after its seed commit are untracked files the skill reads as its own
|
|
* dirty tree (and for the gh-pr commit-flow cells, the only dirty files there are).
|
|
*/
|
|
export function installPathShims(dir: string, shims: PathShim[]): Record<string, string> {
|
|
if (shims.length === 0) return {}
|
|
const binDir = path.join(dir, ".bin")
|
|
fs.mkdirSync(binDir, { recursive: true })
|
|
const byBin = new Map<string, PathShim[]>()
|
|
for (const shim of shims) {
|
|
const list = byBin.get(shim.bin) ?? []
|
|
list.push(shim)
|
|
byBin.set(shim.bin, list)
|
|
}
|
|
for (const [bin, list] of byBin) {
|
|
const real = resolveRealBin(bin)
|
|
for (const shim of list) {
|
|
const stem = `${bin}.${shim.subcommand}`
|
|
fs.writeFileSync(path.join(binDir, `${stem}.exit`), `${shim.exitCode}\n`)
|
|
fs.writeFileSync(path.join(binDir, `${stem}.stdout`), shim.stdout ?? "")
|
|
fs.writeFileSync(path.join(binDir, `${stem}.stderr`), shim.stderr ?? "")
|
|
}
|
|
const subcommands = list.map((s) => s.subcommand)
|
|
const script = `#!/bin/sh
|
|
REAL=${JSON.stringify(real)}
|
|
DIR=$(dirname "$0")
|
|
LOG="$DIR/${SHIM_LOG}"
|
|
cmd=""
|
|
skip=0
|
|
for arg in "$@"; do
|
|
if [ "$skip" = 1 ]; then skip=0; continue; fi
|
|
case "$arg" in
|
|
-C|--git-dir|--work-tree|--namespace|--config-env|-c|-R|--repo|--hostname) skip=1 ;;
|
|
--git-dir=*|--work-tree=*|--namespace=*|-c*|--repo=*|--hostname=*) ;;
|
|
-*) ;;
|
|
*) cmd=$arg; break ;;
|
|
esac
|
|
done
|
|
case "$cmd" in
|
|
${subcommands
|
|
.map(
|
|
(sub) => ` ${sub})
|
|
echo "${bin} $*" >> "$LOG"
|
|
[ -s "$DIR/${bin}.${sub}.stdout" ] && cat "$DIR/${bin}.${sub}.stdout"
|
|
[ -s "$DIR/${bin}.${sub}.stderr" ] && cat "$DIR/${bin}.${sub}.stderr" >&2
|
|
exit "$(cat "$DIR/${bin}.${sub}.exit")"
|
|
;;`,
|
|
)
|
|
.join("\n")}
|
|
esac
|
|
exec "$REAL" "$@"
|
|
`
|
|
fs.writeFileSync(path.join(binDir, bin), script, { mode: 0o755 })
|
|
}
|
|
return { PATH: `${binDir}:${process.env.PATH ?? ""}` }
|
|
}
|