@@ -33,6 +33,12 @@ export interface Config {
3333
3434type AnnotationLevel = 'notice' | 'warning' | 'error' | 'ignore' ;
3535
36+ interface PendingAnnotation {
37+ level : AnnotationLevel ;
38+ message : string ;
39+ properties : core . AnnotationProperties ;
40+ }
41+
3642export interface ReportMatcher {
3743 /**
3844 * The format of the report e.g. `xml`
@@ -102,8 +108,7 @@ export async function run(): Promise<void> {
102108 core . endGroup ( ) ;
103109 }
104110
105- const tally = { errors : 0 , warnings : 0 , notices : 0 , total : 0 } ;
106- let maxAnnotationsReached = false ;
111+ const allAnnotations : PendingAnnotation [ ] = [ ] ;
107112
108113 for ( const [ matcherName , files ] of reportFiles ) {
109114 const matcher = reportMatchers [ matcherName ] ;
@@ -114,30 +119,55 @@ export async function run(): Promise<void> {
114119 core . debug ( `Parsing ${ file } ` ) ;
115120 switch ( matcher . format ) {
116121 case 'xml' :
117- maxAnnotationsReached = await parseXmlReport (
118- file ,
119- matcher ,
120- tally ,
121- config . maxAnnotations ,
122- ) ;
122+ await parseXmlReport ( file , matcher , allAnnotations ) ;
123123 break ;
124124 default :
125125 throw new Error (
126126 `Unsupported matcher format in ${ matcherName } : ${ matcher . format } ` ,
127127 ) ;
128128 }
129- if ( maxAnnotationsReached ) break ;
130129 }
131130 core . info (
132- `Parsed ${ tally . total } annotation(s) from ${ files . size } report(s)` ,
131+ `Parsed ${ allAnnotations . length } annotation(s) from ${ files . size } report(s)` ,
133132 ) ;
134133 core . endGroup ( ) ;
135- if ( maxAnnotationsReached ) break ;
136134 }
137- if ( maxAnnotationsReached )
135+
136+ // Sort annotations by priority: errors first, then warnings, then notices
137+ // Ignore level annotations are already filtered out during collection
138+ const priorityOrder : Record < AnnotationLevel , number > = {
139+ error : 0 ,
140+ warning : 1 ,
141+ notice : 2 ,
142+ ignore : 3 , // Should not appear in the array, but included for type completeness
143+ } ;
144+ allAnnotations . sort (
145+ ( a , b ) => priorityOrder [ a . level ] - priorityOrder [ b . level ] ,
146+ ) ;
147+
148+ // Apply the maxAnnotations limit and create the annotations
149+ const annotationsToCreate = allAnnotations . slice ( 0 , config . maxAnnotations ) ;
150+ const tally = { errors : 0 , warnings : 0 , notices : 0 , total : 0 } ;
151+
152+ for ( const annotation of annotationsToCreate ) {
153+ // Type assertion is safe because we filter out 'ignore' level during collection
154+ core [ annotation . level as 'error' | 'warning' | 'notice' ] (
155+ annotation . message ,
156+ annotation . properties ,
157+ ) ;
158+ if ( annotation . level === 'error' ) tally . errors ++ ;
159+ if ( annotation . level === 'warning' ) tally . warnings ++ ;
160+ if ( annotation . level === 'notice' ) tally . notices ++ ;
161+ tally . total ++ ;
162+ }
163+
164+ if ( allAnnotations . length > config . maxAnnotations ) {
138165 core . warning (
139- `Maximum number of annotations reached (${ config . maxAnnotations } )` ,
166+ `Maximum number of annotations reached (${ config . maxAnnotations } ). ${
167+ allAnnotations . length - config . maxAnnotations
168+ } annotations were not shown.`,
140169 ) ;
170+ }
141171 // Set outputs for other workflow steps to use.
142172 core . setOutput ( 'errors' , tally . errors ) ;
143173 core . setOutput ( 'warnings' , tally . warnings ) ;
@@ -204,24 +234,21 @@ async function loadConfig(): Promise<Config> {
204234async function parseXmlReport (
205235 file : string ,
206236 matcher : ReportMatcher ,
207- tally : Record < 'errors' | 'warnings' | 'notices' | 'total' , number > ,
208- maxAnnotations : number ,
209- ) : Promise < boolean > {
237+ allAnnotations : PendingAnnotation [ ] ,
238+ ) : Promise < void > {
210239 const report = await readFile ( file , 'utf8' ) ;
211240 core . debug ( `Parsing report:\n${ report } ` ) ;
212241 const doc = new DOMParser ( ) . parseFromString ( report , 'text/xml' ) ;
213242 let items = select ( matcher . item , doc ) ;
214243 if ( ! Array . isArray ( items ) && isNodeLike ( items ) ) items = [ items ] ;
215244 if ( ! isArrayOfNodes ( items ) ) {
216245 core . warning ( `No items found in ${ file } ` ) ;
217- return false ;
246+ return ;
218247 }
219248 core . debug ( `Found ${ items . length } items in ${ file } .` ) ;
220249
221250 for ( const item of items ) {
222- core . debug (
223- `Processing item ${ tally . total + 1 } /${ maxAnnotations } : ${ item } .` ,
224- ) ;
251+ core . debug ( `Processing item: ${ item } .` ) ;
225252 const xpath = xpathSelect ( item ) ;
226253 // Figure out the level of the annotation.
227254 let level : AnnotationLevel = 'error' ;
@@ -239,9 +266,10 @@ async function parseXmlReport(
239266 core . debug ( 'Ignoring item.' ) ;
240267 continue ;
241268 }
242- // Create the annotation.
269+
270+ // Create the annotation data.
243271 const message = xpath . string ( matcher . message ) ;
244- const annotation = {
272+ const properties = {
245273 title : matcher . title ? xpath . string ( matcher . title ) : undefined ,
246274 file : matcher . file ? xpath . string ( matcher . file ) : undefined ,
247275 startLine : matcher . startLine
@@ -255,14 +283,8 @@ async function parseXmlReport(
255283 ? xpath . number ( matcher . endColumn )
256284 : undefined ,
257285 } satisfies core . AnnotationProperties ;
258- core [ level ] ( message , annotation ) ;
259286
260- // Count the annotations for the output.
261- if ( level === 'error' ) tally . errors ++ ;
262- if ( level === 'warning' ) tally . warnings ++ ;
263- if ( level === 'notice' ) tally . notices ++ ;
264- tally . total ++ ;
265- if ( tally . total >= maxAnnotations ) return true ;
287+ // Collect non-ignore annotations
288+ allAnnotations . push ( { level, message, properties } ) ;
266289 }
267- return false ;
268290}
0 commit comments