33 * @description Generate GHES release notes from github/releases issues using Copilot CLI
44 *
55 * Generate GHES release notes by:
6- * 1. Querying github/releases for open issues labeled "GHES <version>"
6+ * 1. Querying github/releases issues labeled "GHES <version>" (all states by default)
77 * 2. Finding corresponding changelog PRs in github/blog
88 * 3. Running each through the ghes-release-notes agent via Copilot CLI
99 * 4. Stitching the YAML outputs into a release notes file
@@ -25,6 +25,12 @@ import {
2525 buildReleaseNotesYaml ,
2626 appendNoteLines ,
2727} from '@/ghes-releases/lib/parse-release-notes'
28+ import {
29+ type IssueState ,
30+ buildReleaseIssueListArgs ,
31+ isExcludedReleaseIssue ,
32+ parseIssueState ,
33+ } from '@/ghes-releases/lib/release-issues'
2834
2935// ─── Ctrl+C handling ─────────────────────────────────────────────────────────
3036// Copilot CLI puts the terminal in raw mode, so we catch Ctrl+C (0x03) manually.
@@ -94,25 +100,12 @@ function gh(args: string[]): string {
94100}
95101
96102/**
97- * Fetch open release issues labeled "GHES <version>"
103+ * Fetch release issues labeled "GHES <version>" using the selected issue state.
98104 */
99- function fetchReleaseIssues ( version : string ) : ReleaseIssue [ ] {
100- const label = `GHES ${ version } `
101- const output = gh ( [
102- 'issue' ,
103- 'list' ,
104- '--repo' ,
105- 'github/releases' ,
106- '--label' ,
107- label ,
108- '--state' ,
109- 'open' ,
110- '--limit' ,
111- '200' ,
112- '--json' ,
113- 'number,title,url,body,labels' ,
114- ] )
115- return JSON . parse ( output ) as ReleaseIssue [ ]
105+ function fetchReleaseIssues ( version : string , issueState : IssueState ) : ReleaseIssue [ ] {
106+ const output = gh ( buildReleaseIssueListArgs ( version , issueState ) )
107+ const issues = JSON . parse ( output ) as ReleaseIssue [ ]
108+ return issues . filter ( ( issue ) => ! isExcludedReleaseIssue ( issue ) )
116109}
117110
118111interface ChangelogInfo {
@@ -484,6 +477,11 @@ program
484477 return true
485478 } )
486479 . option ( '--stdout' , 'Print output to console instead of writing to file' )
480+ . option (
481+ '--issue-state <state>' ,
482+ 'Issue state filter for github/releases issues (open, closed, all). Defaults to all.' ,
483+ 'all' ,
484+ )
487485 . option (
488486 '-i, --issue <value>' ,
489487 'Process a single issue by number or URL (replaces its entry if it already exists)' ,
@@ -507,12 +505,20 @@ program
507505 release : string
508506 rc : boolean
509507 stdout ?: boolean
508+ issueState ?: string
510509 issue ?: number
511510 force ?: boolean
512511 } ) => {
513512 const { release, stdout, issue : singleIssue , force } = options
514513 const rc = options . rc ?? false
515514 const spinner = ora ( )
515+ let issueState : IssueState
516+ try {
517+ issueState = parseIssueState ( options . issueState )
518+ } catch ( error ) {
519+ console . error ( `Error: ${ ( error as Error ) . message } ` )
520+ process . exit ( 1 )
521+ }
516522
517523 // ── Prerequisite checks ──
518524 try {
@@ -560,17 +566,25 @@ program
560566 'number,title,url,body,labels' ,
561567 ] )
562568 const issue = JSON . parse ( output ) as ReleaseIssue
569+ if ( isExcludedReleaseIssue ( issue ) ) {
570+ spinner . fail (
571+ `Issue #${ singleIssue } is excluded by label (public roadmap or not planned).` ,
572+ )
573+ process . exit ( 0 )
574+ }
563575 issues = [ issue ]
564576 spinner . succeed ( `Fetched issue #${ singleIssue } : ${ issue . title } ` )
565577 } catch ( error ) {
566578 spinner . fail ( `Failed to fetch issue #${ singleIssue } : ${ ( error as Error ) . message } ` )
567579 process . exit ( 1 )
568580 }
569581 } else {
570- spinner . start ( `Fetching open issues labeled "GHES ${ release } "...` )
582+ spinner . start ( `Fetching issues labeled "GHES ${ release } " (state: ${ issueState } ) ...` )
571583 try {
572- issues = fetchReleaseIssues ( release )
573- spinner . succeed ( `Found ${ issues . length } open issues labeled "GHES ${ release } "` )
584+ issues = fetchReleaseIssues ( release , issueState )
585+ spinner . succeed (
586+ `Found ${ issues . length } issues labeled "GHES ${ release } " (state: ${ issueState } )` ,
587+ )
574588 } catch ( error ) {
575589 spinner . fail ( `Failed to fetch issues: ${ ( error as Error ) . message } ` )
576590 process . exit ( 1 )
0 commit comments