teqo/scripts/check-docs-conflict-markers.mjs

68 lines
2 KiB
JavaScript

#!/usr/bin/env node
/**
* check-docs-conflict-markers — CI guard (OPS44): a PR diff touching
* markdown/mdc files (docs/, AGENTS.md, .agents/, …) must not commit
* conflict markers. Extends the OPS41 repo-wide scan
* (tests/unit/codebaseConventions.unit.spec.ts, which shares the regex via
* scripts/lib/conflictMarkers.mjs) to docs-only PRs, where the unit suite
* is skipped (code_mode: none) — the exact class of the OPS41 incident
* (a marker committed to AGENTS.md at the repo root).
*
* Paths from git diff (merge-base…HEAD) filtered to markdown-ish files.
*/
import { execFileSync } from 'node:child_process'
import { findConflictMarkerLines, markdownPathsOf } from './lib/conflictMarkers.mjs'
const base = `origin/${process.env.GITHUB_BASE_REF ?? 'main'}`
function die(message) {
console.error(`[check-docs-conflict-markers] ${message}`)
process.exit(1)
}
function git(args) {
return execFileSync('git', args, { encoding: 'utf8' }).trim()
}
let mergeBase
try {
mergeBase = git(['merge-base', 'HEAD', base])
} catch {
die(`no merge-base with ${base} (shallow clone?)`)
}
const raw = execFileSync(
'git',
['diff', '--name-only', '--diff-filter=ACMR', `${mergeBase}...HEAD`],
{ encoding: 'utf8' },
)
const changedPaths = raw
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
const scannedPaths = markdownPathsOf(changedPaths)
if (scannedPaths.length === 0) {
console.log('[check-docs-conflict-markers] ok — no markdown changes')
process.exit(0)
}
const offenders = []
for (const path of scannedPaths) {
let content
try {
content = git(['show', `HEAD:${path}`])
} catch {
die(`failed to read HEAD:${path}`)
}
for (const { line } of findConflictMarkerLines(content)) {
offenders.push(`${path}:${line}`)
}
}
if (offenders.length > 0) {
die(`marcadores de conflito em markdown:\n ${offenders.join('\n ')}`)
}
console.log(`[check-docs-conflict-markers] ok — ${scannedPaths.length} markdown file(s) clean`)