@@ -75,6 +75,8 @@ export default function Home() {
7575 skips : job . skips ,
7676 required : job . required ,
7777 weather : getWeatherIndex ( job ) ,
78+ reruns : job . reruns ,
79+ total_reruns : job . reruns . reduce ( ( total , r ) => total + r , 0 ) ,
7880 } ) )
7981 ) ;
8082 setLoading ( false ) ;
@@ -94,6 +96,8 @@ export default function Home() {
9496 skips : check . skips ,
9597 required : check . required ,
9698 weather : getWeatherIndex ( check ) ,
99+ reruns : check . reruns ,
100+ total_reruns : check . reruns . reduce ( ( total , r ) => total + r , 0 ) ,
97101 } ) )
98102 ) ;
99103 setLoading ( false ) ;
@@ -137,21 +141,63 @@ export default function Home() {
137141 } ;
138142
139143 const maintainRefs = useRef ( [ ] ) ;
144+ const rerunRefs = useRef ( [ ] ) ;
140145
141146 const rowExpansionTemplate = ( data ) => {
142147 const job = ( display === "nightly"
143148 ? jobs
144149 : checks ) . find ( ( job ) => job . name === data . name ) ;
145150
151+ if ( ! job ) return (
152+ < div className = "p-3 bg-gray-100" >
153+ No data available for this job.
154+ </ div >
155+ ) ;
156+
146157 // Prepare run data
147- const runs = [ ] ;
148- for ( let i = 0 ; i < job . runs ; i ++ ) {
149- runs . push ( {
150- run_num : job . run_nums [ i ] ,
151- result : job . results [ i ] ,
152- url : job . urls [ i ] ,
153- } ) ;
154- }
158+ const getRunStatusIcon = ( runs ) => {
159+ if ( Array . isArray ( runs ) ) {
160+ const allPass = runs . every ( run => run === "Pass" ) ;
161+ const allFail = runs . every ( run => run === "Fail" ) ;
162+
163+ if ( allPass ) { return "✅" ; }
164+ if ( allFail ) { return "❌" ; }
165+ } else if ( runs === "Pass" ) {
166+ return "✅" ;
167+ } else if ( runs === "Fail" ) {
168+ return "❌" ;
169+ }
170+ return "⚠️" ; // return a warning if a mix of results
171+ } ;
172+
173+ const runEntries = job . run_nums . map ( ( run_num , idx ) => ( {
174+ run_num,
175+ result : job . results [ idx ] ,
176+ reruns : job . reruns [ idx ] ,
177+ rerun_result : job . rerun_results [ idx ] ,
178+ url : job . urls [ idx ] ,
179+ attempt_urls : job . attempt_urls [ idx ] ,
180+ } ) ) ;
181+
182+ // Find maintainers for the given job
183+ const maintainerData = MaintainerMapping . mappings
184+ . filter ( ( { regex } ) => new RegExp ( regex ) . test ( job . name ) )
185+ . flatMap ( ( match ) =>
186+ match . owners . map ( ( owner ) => ( {
187+ ...owner ,
188+ group : match . group ,
189+ } ) )
190+ ) ;
191+
192+ // Group maintainers by their group name
193+ const groupedMaintainers = maintainerData . reduce ( ( acc , owner ) => {
194+ if ( ! acc [ owner . group ] ) {
195+ acc [ owner . group ] = [ ] ;
196+ }
197+ acc [ owner . group ] . push ( owner ) ;
198+ return acc ;
199+ } , { } ) ;
200+
155201
156202 // Find maintainers for the given job
157203 const maintainerData = MaintainerMapping . mappings
@@ -177,25 +223,154 @@ export default function Home() {
177223 < div key = { `${ job . name } -runs` } className = "p-3 bg-gray-100" >
178224 { /* Display last 10 runs */ }
179225 < div className = "flex flex-wrap gap-4" >
180- { runs . length > 0 ? (
181- runs . map ( ( run ) => {
182- const emoji =
183- run . result === "Pass"
184- ? "✅"
185- : run . result === "Fail"
186- ? "❌"
187- : "⚠️" ;
188- return (
189- < span key = { `${ job . name } -runs-${ run . run_num } ` } >
190- < a href = { run . url } target = "_blank" rel = "noopener noreferrer" >
191- { emoji } { run . run_num }
226+ { runEntries . map ( ( {
227+ run_num,
228+ result,
229+ url,
230+ reruns,
231+ rerun_result,
232+ attempt_urls
233+ } , idx ) => {
234+ const allResults = rerun_result
235+ ? [ result , ...rerun_result ]
236+ : [ result ] ;
237+
238+ const runStatuses = allResults . map ( ( result , idx ) =>
239+ `${ allResults . length - idx } . ${ result === 'Pass'
240+ ? '✅ Success'
241+ : result === 'Fail'
242+ ? '❌ Fail'
243+ : '⚠️ Warning' } `) ;
244+
245+ // IDs can't have a '/'...
246+ const sanitizedJobName = job . name . replace ( / [ ^ a - z A - Z 0 - 9 - _ ] / g, '' ) ;
247+
248+ const badgeReruns = `reruns-${ sanitizedJobName } -${ run_num } ` ;
249+
250+ rerunRefs . current [ badgeReruns ] = rerunRefs . current [ badgeReruns ]
251+ || React . createRef ( ) ;
252+
253+ return (
254+ < div key = { run_num } className = "flex" >
255+ < div key = { idx } className = "flex items-center" >
256+ { /* <a href={url} target="_blank" rel="noopener noreferrer"> */ }
257+ < a href = { attempt_urls [ 0 ] } target = "_blank" rel = "noopener noreferrer" >
258+ { getRunStatusIcon ( allResults ) } { run_num }
192259 </ a >
193-
194- </ span >
195- ) ;
196- } )
260+ </ div >
261+ { reruns > 0 && (
262+ < span className = "p-overlay-badge" >
263+ < sup className = "text-xs font-bold align-super ml-1"
264+ onMouseEnter = { ( e ) =>
265+ rerunRefs . current [ badgeReruns ] . current . toggle ( e ) } >
266+ { reruns + 1 }
267+ </ sup >
268+ < OverlayPanel ref = { rerunRefs . current [ badgeReruns ] } dismissable
269+ onMouseLeave = { ( e ) =>
270+ rerunRefs . current [ badgeReruns ] . current . toggle ( e ) } >
271+ < ul className = "bg-white border rounded shadow-lg p-2" >
272+ { runStatuses . map ( ( status , index ) => (
273+ < li key = { index } className = "p-2 hover:bg-gray-200" >
274+ < a
275+ href = { attempt_urls [ index ] || `${ url } /attempts/${ index } ` }
276+ target = "_blank"
277+ rel = "noopener noreferrer" >
278+ { status }
279+ </ a >
280+ </ li >
281+ ) ) }
282+ </ ul >
283+ </ OverlayPanel >
284+ </ span >
285+ ) }
286+ </ div >
287+ ) ;
288+ } ) }
289+ </ div >
290+ { /* Display Maintainers, if there's any */ }
291+ < div className = "mt-4 p-2 bg-gray-300 w-full" >
292+ { Object . keys ( groupedMaintainers ) . length > 0 ? (
293+ < div className = "grid grid-cols-2 p-2 gap-6" >
294+ { Object . entries ( groupedMaintainers ) . map (
295+ ( [ group , owners ] , groupIndex ) => (
296+ < div key = { groupIndex } className = "flex flex-col max-w-xs" >
297+ { /* List the group name */ }
298+ < strong className = "pl-2" > { group } :</ strong >
299+ < div >
300+ { /* List all maintainers for the group */ }
301+ { owners . map ( ( owner , ownerIndex ) => {
302+ const badgeMaintain = `maintain-${ owner . github } ` ;
303+ maintainRefs . current [ badgeMaintain ] =
304+ maintainRefs . current [ badgeMaintain ] || React . createRef ( ) ;
305+
306+ return (
307+ // Create the OverlayPanel with contact information.
308+ < span key = { ownerIndex } >
309+ < span
310+ onMouseEnter = { ( e ) =>
311+ maintainRefs . current [ badgeMaintain ] . current . toggle ( e )
312+ }
313+ >
314+ < a
315+ href = { `https://github.com/${ owner . github } ` }
316+ target = "_blank"
317+ rel = "noopener noreferrer"
318+ className = "text-blue-500 underline pl-2 whitespace-nowrap"
319+ >
320+ { owner . fullname }
321+ </ a >
322+ { ownerIndex < owners . length - 1 && ", " }
323+ </ span >
324+ < OverlayPanel
325+ ref = { maintainRefs . current [ badgeMaintain ] }
326+ dismissable
327+ onMouseLeave = { ( e ) =>
328+ maintainRefs . current [ badgeMaintain ] . current . toggle ( e )
329+ }
330+ >
331+ < ul className = "bg-white border rounded shadow-lg p-2" >
332+ < li className = "p-2 hover:bg-gray-200" >
333+ < span className = "font-bold mr-4" > Email:</ span > { " " }
334+ { owner . email }
335+ </ li >
336+ < a
337+ href = { `https://github.com/${ owner . github } ` }
338+ target = "_blank"
339+ rel = "noopener noreferrer"
340+ >
341+ < li className = "p-2 hover:bg-gray-200 flex justify-between" >
342+ < span className = "font-bold mr-4" >
343+ GitHub:
344+ </ span >
345+ < span className = "text-right" >
346+ { owner . github }
347+ </ span >
348+ </ li >
349+ </ a >
350+ < a
351+ href = { `${ owner . slackurl } ` }
352+ target = "_blank"
353+ rel = "noopener noreferrer"
354+ >
355+ < li className = "p-2 hover:bg-gray-200 flex justify-between" >
356+ < span className = "font-bold mr-4" > Slack:</ span >
357+ < span className = "text-right" >
358+ @{ owner . slack }
359+ </ span >
360+ </ li >
361+ </ a >
362+ </ ul >
363+ </ OverlayPanel >
364+ </ span >
365+ ) ;
366+ } ) }
367+ </ div >
368+ </ div >
369+ )
370+ ) }
371+ </ div >
197372 ) : (
198- < div > No Nightly Runs associated with this job </ div >
373+ < div > No Maintainer Information Available </ div >
199374 ) }
200375 </ div >
201376 { /* Display Maintainers, if there's any */ }
@@ -315,6 +490,7 @@ export default function Home() {
315490 header = "Runs"
316491 className = "whitespace-nowrap px-2"
317492 sortable />
493+ < Column field = "total_reruns" header = "Reruns" sortable />
318494 < Column field = "fails" header = "Fails" sortable />
319495 < Column field = "skips" header = "Skips" sortable />
320496 < Column
@@ -351,6 +527,7 @@ export default function Home() {
351527 header = "Runs"
352528 className = "whitespace-nowrap px-2"
353529 sortable />
530+ < Column field = "total_reruns" header = "Reruns" sortable />
354531 < Column field = "fails" header = "Fails" sortable />
355532 < Column field = "skips" header = "Skips" sortable />
356533 < Column
0 commit comments