translation-pairing-git.ts 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. /** Git-blob operations owned by the bilingual pairing workflow. */
  2. import { spawnSync } from 'node:child_process'
  3. import { createHash } from 'node:crypto'
  4. const SNAPSHOT_REF_PREFIX = 'refs/dsh/translation-pairing/snapshots'
  5. /** Full SHA-1 Git blob hash (the 40-hex format used by pairing records). */
  6. export function gitBlobHash(content: Buffer): string {
  7. const hash = createHash('sha1')
  8. hash.update(`blob ${content.byteLength}\0`)
  9. hash.update(content)
  10. return hash.digest('hex')
  11. }
  12. function runGit(root: string, args: string[], operation: string, input?: Buffer): Buffer {
  13. const result = spawnSync('git', ['-C', root, ...args], {
  14. input,
  15. maxBuffer: 1 << 26,
  16. })
  17. if (result.error) {
  18. throw new Error(`${operation} failed: ${result.error.message}`, { cause: result.error })
  19. }
  20. if (result.status !== 0) {
  21. throw new Error(`${operation} failed with status ${String(result.status)}: ${result.stderr.toString('utf8').trim()}`)
  22. }
  23. return result.stdout
  24. }
  25. /**
  26. * Persist exact working-tree bytes so a pairing record can later recover them
  27. * with `git cat-file`, even when they have never appeared in the index or a
  28. * commit. The returned object ID is checked against the pairing format's own
  29. * content hash before the caller writes a sidecar.
  30. */
  31. export function storeGitBlob(root: string, content: Buffer): string {
  32. const expected = gitBlobHash(content)
  33. const stored = runGit(root, ['hash-object', '-w', '--stdin'], 'git hash-object -w --stdin', content)
  34. .toString('utf8')
  35. .trim()
  36. if (stored !== expected) {
  37. throw new Error(`git hash-object -w --stdin returned unexpected object ID ${JSON.stringify(stored)}; expected ${expected}`)
  38. }
  39. runGit(
  40. root,
  41. ['update-ref', `${SNAPSHOT_REF_PREFIX}/${stored}`, stored],
  42. 'git update-ref for translation snapshot',
  43. )
  44. return stored
  45. }