mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 16:02:29 +00:00
* fix(git): reduce queries and preserve data across execution hosts * fix(ci): exercise pinned Git and serialize mobile dependency entrypoints * fix(relay): preserve fresh diff retries after hung shared reads * test(git): wait for fetch barrier before canceling preparation * fix(i18n): describe index-preserving discard in every locale * fix(git): retain clone diagnostics and allow WSL policy startup * test(git): refresh default-base and branch-safety fixtures
100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import {
|
|
signalProcessTree,
|
|
forceTerminateProcessTree
|
|
} from '../shared/child-process/process-tree-termination'
|
|
import type { ProcessTerminationBarrier } from '../shared/child-process/process-spec'
|
|
import { runProcess } from '../shared/child-process/run-process'
|
|
import { GitAdmissionScheduler } from '../shared/git-admission-scheduler'
|
|
import type { GitAdmissionRequest } from '../shared/git-admission-state'
|
|
import { gitCommandTimeoutMs } from '../shared/git-command-timeout'
|
|
|
|
export const MAX_GIT_BUFFER = 10 * 1024 * 1024
|
|
let scheduler = new GitAdmissionScheduler()
|
|
|
|
export function _resetRelayGitAdmissionForTests(replacement = new GitAdmissionScheduler()): void {
|
|
scheduler = replacement
|
|
}
|
|
|
|
export const acquireRelayGitAdmission = (request: GitAdmissionRequest) => scheduler.acquire(request)
|
|
|
|
type GitTerminationOptions = {
|
|
cwd?: string
|
|
env?: NodeJS.ProcessEnv
|
|
timeout?: number
|
|
maxBuffer?: number
|
|
signal?: AbortSignal
|
|
captureStdoutAsBytes?: boolean
|
|
outputCapture?: 'tail'
|
|
observeStderr?: ProcessTerminationBarrier['observeStderr']
|
|
}
|
|
|
|
export async function runGitToTermination(
|
|
args: string[],
|
|
options: GitTerminationOptions,
|
|
stdin: string | undefined
|
|
): Promise<{ stdout: string; stderr: string; stdoutBytes?: Buffer }> {
|
|
const grant = await acquireRelayGitAdmission({
|
|
args,
|
|
cwd: options.cwd ?? '.',
|
|
signal: options.signal
|
|
})
|
|
// A rejected capture can precede child termination; the child owns the grant.
|
|
const result = await runProcess(
|
|
{
|
|
program: 'git',
|
|
args,
|
|
cwd: options.cwd,
|
|
env: options.env,
|
|
timeoutMs: gitCommandTimeoutMs(args, options.timeout) ?? null,
|
|
maxOutputBytes: options.maxBuffer ?? MAX_GIT_BUFFER,
|
|
captureStdoutAsBytes: options.captureStdoutAsBytes,
|
|
killOnOutputLimit: options.outputCapture !== 'tail',
|
|
signal: options.signal,
|
|
terminationBarrier: options.observeStderr
|
|
? {
|
|
observeStderr: options.observeStderr,
|
|
signal: signalProcessTree,
|
|
force: forceTerminateProcessTree
|
|
}
|
|
: true,
|
|
onChildTerminated: grant.release,
|
|
...(stdin === undefined ? {} : { input: stdin })
|
|
},
|
|
options.outputCapture
|
|
)
|
|
const outputExceeded = result.outputTruncated === true && options.outputCapture !== 'tail'
|
|
if (
|
|
result.code === 0 &&
|
|
!result.signal &&
|
|
!result.timedOut &&
|
|
!options.signal?.aborted &&
|
|
!outputExceeded
|
|
) {
|
|
return {
|
|
stdout: result.stdout,
|
|
stderr: result.stderr,
|
|
...(result.stdoutBytes ? { stdoutBytes: result.stdoutBytes } : {})
|
|
}
|
|
}
|
|
const error = new Error(
|
|
outputExceeded
|
|
? 'git output exceeded maxBuffer.'
|
|
: result.timedOut
|
|
? `git ${args[0] ?? 'command'} timed out.`
|
|
: options.signal?.aborted
|
|
? 'The operation was aborted.'
|
|
: result.stderr.trim() || `git ${args[0] ?? 'command'} failed.`
|
|
)
|
|
if (options.signal?.aborted) {
|
|
error.name = 'AbortError'
|
|
}
|
|
throw Object.assign(error, {
|
|
code: outputExceeded ? 'ENOBUFS' : result.code,
|
|
timedOut: result.timedOut,
|
|
killed: result.timedOut || result.signal !== null || options.signal?.aborted === true,
|
|
signal: result.signal,
|
|
stdout: result.stdoutBytes ?? result.stdout,
|
|
stderr: result.stderr
|
|
})
|
|
}
|