22// @ts -check
33import { readFileSync } from 'fs' ;
44import { execSync } from 'child_process' ;
5+ import { relative , resolve } from 'path' ;
56
67const THRESHOLD = Number ( process . env . THRESHOLD ?? 60 ) ;
78const SUMMARY_PATH = 'coverage/coverage-summary.json' ;
@@ -19,17 +20,24 @@ try {
1920
2021// ── 2. Find files below threshold ────────────────────────────────────────────
2122const belowThreshold = [ ] ;
23+ const repoRoot = resolve ( '.' ) ;
2224
2325for ( const [ file , metrics ] of Object . entries ( summary ) ) {
2426 if ( file === 'total' ) continue ;
25- if ( ! file . startsWith ( 'src/' ) ) continue ;
27+
28+ // Normalize: c8/Istanbul may emit absolute paths or repo-relative paths
29+ const relPath = file . startsWith ( '/' ) || / ^ [ A - Z a - z ] : [ / \\ ] / . test ( file )
30+ ? relative ( repoRoot , file ) . replace ( / \\ / g, '/' )
31+ : file ;
32+
33+ if ( ! relPath . startsWith ( 'src/' ) ) continue ;
2634
2735 const linePct = metrics . lines . pct ?? 0 ;
2836 const branchPct = metrics . branches . pct ?? 0 ;
2937 const fnPct = metrics . functions . pct ?? 0 ;
3038
3139 if ( linePct < THRESHOLD || fnPct < THRESHOLD ) {
32- belowThreshold . push ( { file, linePct, branchPct, fnPct } ) ;
40+ belowThreshold . push ( { file : relPath , linePct, branchPct, fnPct } ) ;
3341 }
3442}
3543
@@ -44,7 +52,21 @@ if (belowThreshold.length === 0 && totalLines >= THRESHOLD) {
4452 process . exit ( 0 ) ;
4553}
4654
47- // ── 3. Build the issue body (this is the Copilot agent's instruction set) ────
55+ // ── 3. Check for existing open coverage issue (avoid duplicates) ─────────────
56+ try {
57+ const existing = execSync (
58+ `gh issue list --repo ${ REPO } --label test-coverage --state open --json number --jq '.[0].number'` ,
59+ { encoding : 'utf8' }
60+ ) . trim ( ) ;
61+ if ( existing ) {
62+ console . log ( `\n⏭️ Open coverage issue already exists: #${ existing } — skipping.` ) ;
63+ process . exit ( 0 ) ;
64+ }
65+ } catch {
66+ // gh CLI may fail if label doesn't exist yet — continue to create
67+ }
68+
69+ // ── 4. Build the issue body (this is the Copilot agent's instruction set) ────
4870belowThreshold . sort ( ( a , b ) => a . linePct - b . linePct ) ; // worst files first
4971
5072const tableRows = belowThreshold
@@ -111,7 +133,17 @@ For each file:
111133- Do **not** touch source files outside \`test/\`
112134` ;
113135
114- // ── 4. Open the issue via gh CLI ──────────────────────────────────────────────
136+ // ── 5. Ensure the test-coverage label exists ─────────────────────────────────
137+ try {
138+ execSync (
139+ `gh label create test-coverage --repo ${ REPO } --color 0075ca --description "Opened by the coverage agent" --force` ,
140+ { stdio : 'inherit' }
141+ ) ;
142+ } catch {
143+ // --force handles existing labels; ignore unexpected errors
144+ }
145+
146+ // ── 6. Open the issue via gh CLI ──────────────────────────────────────────────
115147const title = `chore: improve test coverage -- ${ belowThreshold . length } files below ${ THRESHOLD } % (run ${ new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) } )` ;
116148
117149const cmd = [
0 commit comments