@@ -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,43 @@ 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+ } ) ) ;
155181
156182 // Find maintainers for the given job
157183 const maintainerData = MaintainerMapping . mappings
@@ -177,28 +203,70 @@ export default function Home() {
177203 < div key = { `${ job . name } -runs` } className = "p-3 bg-gray-100" >
178204 { /* Display last 10 runs */ }
179205 < 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 }
206+ { runEntries . map ( ( {
207+ run_num,
208+ result,
209+ url,
210+ reruns,
211+ rerun_result,
212+ attempt_urls
213+ } , idx ) => {
214+ const allResults = rerun_result
215+ ? [ result , ...rerun_result ]
216+ : [ result ] ;
217+
218+ const runStatuses = allResults . map ( ( result , idx ) =>
219+ `${ allResults . length - idx } . ${ result === 'Pass'
220+ ? '✅ Success'
221+ : result === 'Fail'
222+ ? '❌ Fail'
223+ : '⚠️ Warning' } `) ;
224+
225+ // IDs can't have a '/'...
226+ const sanitizedJobName = job . name . replace ( / [ ^ a - z A - Z 0 - 9 - _ ] / g, '' ) ;
227+
228+ const badgeReruns = `reruns-${ sanitizedJobName } -${ run_num } ` ;
229+
230+ rerunRefs . current [ badgeReruns ] = rerunRefs . current [ badgeReruns ]
231+ || React . createRef ( ) ;
232+
233+ return (
234+ < div key = { run_num } className = "flex" >
235+ < div key = { idx } className = "flex items-center" >
236+ { /* <a href={url} target="_blank" rel="noopener noreferrer"> */ }
237+ < a href = { attempt_urls [ 0 ] } target = "_blank" rel = "noopener noreferrer" >
238+ { getRunStatusIcon ( allResults ) } { run_num }
192239 </ a >
193-
194- </ span >
195- ) ;
196- } )
197- ) : (
198- < div > No Nightly Runs associated with this job</ div >
199- ) }
240+ </ div >
241+ { reruns > 0 && (
242+ < span className = "p-overlay-badge" >
243+ < sup className = "text-xs font-bold align-super ml-1"
244+ onMouseEnter = { ( e ) =>
245+ rerunRefs . current [ badgeReruns ] . current . toggle ( e ) } >
246+ { reruns + 1 }
247+ </ sup >
248+ < OverlayPanel ref = { rerunRefs . current [ badgeReruns ] } dismissable
249+ onMouseLeave = { ( e ) =>
250+ rerunRefs . current [ badgeReruns ] . current . toggle ( e ) } >
251+ < ul className = "bg-white border rounded shadow-lg p-2" >
252+ { runStatuses . map ( ( status , index ) => (
253+ < li key = { index } className = "p-2 hover:bg-gray-200" >
254+ < a
255+ href = { attempt_urls [ index ] || `${ url } /attempts/${ index } ` }
256+ target = "_blank"
257+ rel = "noopener noreferrer" >
258+ { status }
259+ </ a >
260+ </ li >
261+ ) ) }
262+ </ ul >
263+ </ OverlayPanel >
264+ </ span >
265+ ) }
266+ </ div >
267+ ) ;
268+ } ) }
200269 </ div >
201-
202270 { /* Display Maintainers, if there's any */ }
203271 < div className = "mt-4 p-2 bg-gray-300 w-full" >
204272 { Object . keys ( groupedMaintainers ) . length > 0 ? (
@@ -316,6 +384,7 @@ export default function Home() {
316384 header = "Runs"
317385 className = "whitespace-nowrap px-2"
318386 sortable />
387+ < Column field = "total_reruns" header = "Reruns" sortable />
319388 < Column field = "fails" header = "Fails" sortable />
320389 < Column field = "skips" header = "Skips" sortable />
321390 < Column
@@ -352,6 +421,7 @@ export default function Home() {
352421 header = "Runs"
353422 className = "whitespace-nowrap px-2"
354423 sortable />
424+ < Column field = "total_reruns" header = "Reruns" sortable />
355425 < Column field = "fails" header = "Fails" sortable />
356426 < Column field = "skips" header = "Skips" sortable />
357427 < Column
0 commit comments