-
Notifications
You must be signed in to change notification settings - Fork 65
Expand file tree
/
Copy pathgit.mts
More file actions
66 lines (60 loc) · 2.81 KB
/
Copy pathgit.mts
File metadata and controls
66 lines (60 loc) · 2.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// Git subprocess wrappers for the hooks. Gate-free.
//
// Two flavors:
//
// git(...) — loose. Returns '' on failure. Used by callers that
// legitimately tolerate a missing ref (e.g. probing
// remote default-branch HEAD which may not be set up
// locally) and provide their own fallback. Silent
// by design — _shared/helpers.mts can't import the canonical
// logger because it runs before the Node-version
// gate has cleared, and a fire-and-forget dynamic
// import races process exit. Callers that need to
// know about failure should use gitOrThrow().
//
// gitOrThrow(...) — strict. Throws on either spawn error (git not on
// PATH, EAGAIN, …) or non-zero exit. Used by gitLines
// and every security-gate caller in pre-commit /
// pre-push: if `git diff --cached --name-only` fails
// we MUST refuse to greenlight the commit, not pass
// it with "no files to check."
//
// gitLines goes through gitOrThrow because every call site we have
// staged-file iteration, push-range walking, repo-toplevel lookup
// makes a security or correctness decision based on the result; an
// empty array from a failed git invocation is a fail-open.
import { spawnSync } from '@socketsecurity/lib-stable/process/spawn/child'
import { splitLines } from './scan-core.mts'
export const git = (...args: string[]): string => {
const result = spawnSync('git', args, { encoding: 'utf8' })
return (result.stdout ?? '').trim()
}
export const gitOrThrow = (...args: string[]): string => {
const result = spawnSync('git', args, { encoding: 'utf8' })
if (result.error) {
throw new Error(`git ${args.join(' ')}: ${result.error.message}`)
}
if (typeof result.status !== 'number' || result.status !== 0) {
const err = result.stderr?.trim() || `exit ${result.status}`
throw new Error(`git ${args.join(' ')}: ${err}`)
}
return (result.stdout ?? '').trim()
}
export const gitLines = (...args: string[]): string[] => {
const out = gitOrThrow(...args)
return out ? splitLines(out) : []
}
/**
* Run git with `stdin` piped in, returning the raw status and stdout.
*
* Unlike `git`/`gitOrThrow`, this surfaces the exit status instead of
* swallowing it or throwing — a query like `git check-ignore` uses its status
* as the ANSWER (0 = matched, 1 = no match), not as a failure.
*/
export function gitWithInput(
args: readonly string[],
stdin: string,
): { status: number | undefined; stdout: string } {
const result = spawnSync('git', [...args], { encoding: 'utf8', input: stdin })
return { status: result.status ?? undefined, stdout: result.stdout ?? '' }
}