Add real-time verbose command streaming via stderr
This commit is contained in:
parent
10e98e2dc5
commit
18887b9419
75
src/run.ts
75
src/run.ts
|
|
@ -29,12 +29,23 @@ type RunOptions = {
|
|||
}
|
||||
|
||||
const SENTINEL_PREFIX = "__SHOUT_SENTINEL_"
|
||||
const VERBOSE_MARKER = "__SHOUT_CMD_"
|
||||
|
||||
function buildScript(commands: Command[], sentinel: string): string {
|
||||
const lines: string[] = ["exec 2>&1"]
|
||||
function buildScript(commands: Command[], sentinel: string, verbose: boolean): string {
|
||||
const lines: string[] = []
|
||||
|
||||
if (verbose) {
|
||||
// Save original stderr to fd 3 before merging stderr into stdout
|
||||
lines.push("exec 3>&2 2>&1")
|
||||
} else {
|
||||
lines.push("exec 2>&1")
|
||||
}
|
||||
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
const cmd = commands[i]!
|
||||
if (verbose) {
|
||||
lines.push(`printf '${VERBOSE_MARKER}${i}\\n' >&3`)
|
||||
}
|
||||
lines.push(cmd.command)
|
||||
// Sentinel: printf to avoid echo interpretation issues
|
||||
// Format: __SHOUT_SENTINEL_<exitcode>_<index>__
|
||||
|
|
@ -111,6 +122,36 @@ function escapeRegex(s: string): string {
|
|||
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
||||
}
|
||||
|
||||
function streamVerboseMarkers(
|
||||
stderr: ReadableStream<Uint8Array>,
|
||||
commands: Command[],
|
||||
onCommand: (cmd: Command) => void,
|
||||
): void {
|
||||
const reader = stderr.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ""
|
||||
|
||||
const pump = (): void => {
|
||||
reader.read().then(({ done, value }) => {
|
||||
if (done) return
|
||||
if (value) buffer += decoder.decode(value, { stream: true })
|
||||
let nlIdx: number
|
||||
while ((nlIdx = buffer.indexOf("\n")) !== -1) {
|
||||
const line = buffer.slice(0, nlIdx)
|
||||
buffer = buffer.slice(nlIdx + 1)
|
||||
if (line.startsWith(VERBOSE_MARKER)) {
|
||||
const i = parseInt(line.slice(VERBOSE_MARKER.length), 10)
|
||||
if (i >= 0 && i < commands.length) {
|
||||
onCommand(commands[i]!)
|
||||
}
|
||||
}
|
||||
}
|
||||
pump()
|
||||
}).catch(() => {})
|
||||
}
|
||||
pump()
|
||||
}
|
||||
|
||||
export async function runFile(
|
||||
file: ShoutFile,
|
||||
options: RunOptions,
|
||||
|
|
@ -122,7 +163,8 @@ export async function runFile(
|
|||
}
|
||||
|
||||
const sentinel = SENTINEL_PREFIX
|
||||
const script = buildScript(file.commands, sentinel)
|
||||
const verbose = options.verbose && !!options.onCommand
|
||||
const script = buildScript(file.commands, sentinel, verbose)
|
||||
|
||||
const env: Record<string, string> = options.cleanEnv
|
||||
? {}
|
||||
|
|
@ -155,11 +197,19 @@ export async function runFile(
|
|||
})
|
||||
|
||||
try {
|
||||
if (verbose) {
|
||||
// Stream stderr for verbose command markers before writing script
|
||||
streamVerboseMarkers(proc.stderr, file.commands, options.onCommand!)
|
||||
}
|
||||
|
||||
proc.stdin.write(script)
|
||||
proc.stdin.end()
|
||||
|
||||
const stdout = await readWithTimeout(proc.stdout, options.timeout * file.commands.length)
|
||||
const stderr = await readWithTimeout(proc.stderr, 1000).catch(() => "")
|
||||
const totalTimeout = options.timeout * file.commands.length
|
||||
const stdout = await readWithTimeout(proc.stdout, totalTimeout)
|
||||
if (!verbose) {
|
||||
await readWithTimeout(proc.stderr, 1000).catch(() => "")
|
||||
}
|
||||
|
||||
await proc.exited
|
||||
|
||||
|
|
@ -169,16 +219,11 @@ export async function runFile(
|
|||
file.commands.length,
|
||||
)
|
||||
|
||||
const results: CommandResult[] = file.commands.map((cmd, i) => {
|
||||
if (options.verbose && options.onCommand) {
|
||||
options.onCommand(cmd)
|
||||
}
|
||||
return {
|
||||
command: cmd,
|
||||
actual: outputs[i] ?? [],
|
||||
exitCode: exitCodes[i] ?? 1,
|
||||
}
|
||||
})
|
||||
const results: CommandResult[] = file.commands.map((cmd, i) => ({
|
||||
command: cmd,
|
||||
actual: outputs[i] ?? [],
|
||||
exitCode: exitCodes[i] ?? 1,
|
||||
}))
|
||||
|
||||
return { file, results, tmpDir }
|
||||
} catch (err) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user