OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / scripts / check-audit-owned-diff.mjs

135 lines · javascript ref:
#!/usr/bin/env node

import fs from 'node:fs'
import path from 'node:path'
import { spawnSync } from 'node:child_process'
import { fileURLToPath } from 'node:url'
import { buildOwnedDiffReport } from './lib/audit-owned-diff.mjs'

const usage = `
Usage:
  node scripts/check-audit-owned-diff.mjs [--json]
  node scripts/check-audit-owned-diff.mjs --status-file <git-status-fixture> [--json]

Options:
  --root <path>         Repository root. Defaults to the HiveRelay checkout.
  --status-file <path>  Read git status --porcelain=v1 output from a fixture.
  --json                Print machine-readable output.
`

const here = path.dirname(fileURLToPath(import.meta.url))
const defaultRoot = path.resolve(here, '..')

let args
try {
  args = parseArgs(process.argv.slice(2))
} catch (err) {
  console.error(err.message)
  console.error(usage.trim())
  process.exit(1)
}

if (args.help) {
  console.log(usage.trim())
  process.exit(0)
}

const root = path.resolve(args.root || defaultRoot)
let statusText

try {
  statusText = readStatusText(root, args.statusFile)
} catch (err) {
  const report = {
    schemaVersion: 1,
    kind: 'hiverelay-audit-owned-diff',
    scope: 'audit-owned development diff only; release closure still requires a clean worktree',
    status: 'error',
    error: err.message
  }
  if (args.json) console.log(JSON.stringify(report, null, 2))
  else {
    console.log('HiveRelay audit-owned diff check')
    console.log('status=error')
    console.log(err.message)
  }
  process.exit(1)
}

const report = buildOwnedDiffReport(statusText)

if (args.json) console.log(JSON.stringify(report, null, 2))
else console.log(formatReport(report))

process.exit(report.status === 'pass' ? 0 : 1)

function parseArgs (argv) {
  const out = {}
  for (let i = 0; i < argv.length; i++) {
    const arg = argv[i]
    if (arg === '--help' || arg === '-h') {
      out.help = true
      continue
    }
    if (arg === '--json') {
      out.json = true
      continue
    }
    if (arg === '--root') {
      out.root = readValue(argv, ++i, arg)
      continue
    }
    if (arg === '--status-file') {
      out.statusFile = readValue(argv, ++i, arg)
      continue
    }
    throw new Error(`Unknown argument: ${arg}`)
  }
  return out
}

function readValue (argv, index, flag) {
  const value = argv[index]
  if (!value || value.startsWith('--')) throw new Error(`Missing value for ${flag}`)
  return value
}

function readStatusText (root, statusFile) {
  if (statusFile) return fs.readFileSync(path.resolve(statusFile), 'utf8')

  const res = spawnSync('git', ['status', '--porcelain=v1'], {
    cwd: root,
    encoding: 'utf8',
    timeout: 10000
  })

  if (res.error) throw new Error(`git status failed: ${res.error.message}`)
  if (res.status !== 0) throw new Error(`git status failed: ${(res.stderr || '').trim() || `exit ${res.status}`}`)
  return res.stdout
}

function formatReport (report) {
  const lines = [
    'HiveRelay audit-owned diff check',
    `scope=${report.scope}`,
    `status=${report.status}`,
    `changed=${report.totals.changed} owned=${report.totals.owned} unknown=${report.totals.unknown} slices=${report.totals.slices}`
  ]

  if (report.entries.length === 0) {
    lines.push('PASS worktree has no changed or untracked paths.')
    return lines.join('\n')
  }

  for (const entry of report.entries) {
    const owners = entry.owners.map(owner => owner.id).join(', ')
    lines.push(`${entry.owners.length === 0 ? 'FAIL' : 'PASS'} ${entry.status} ${entry.path} — ${owners || 'unowned'}`)
  }

  if (report.unknown.length > 0) {
    lines.push('Unknown paths must be assigned to an audit slice before this development diff is reviewable.')
  }

  return lines.join('\n')
}