@@ -93,8 +93,13 @@ pub(crate) async fn build_details_handler(
9393    . ok_or ( AxumNope :: BuildNotFound ) ?; 
9494
9595    let  ( output,  all_log_filenames,  current_filename)  = if  let  Some ( output)  = row. output  { 
96+         // legacy case, for old builds the build log was stored in the database. 
9697        ( output,  Vec :: new ( ) ,  None ) 
9798    }  else  { 
99+         // for newer builds we have the build logs stored in S3. 
100+         // For a long time only for one target, then we started storing the logs for other targets 
101+         // toFor a long time only for one target, then we started storing the logs for other 
102+         // targets. In any case, all the logfiles are put into a folder we can just query. 
98103        let  prefix = format ! ( "build-logs/{}/" ,  id) ; 
99104        let  all_log_filenames:  Vec < _ >  = storage
100105            . list_prefix ( & prefix)  // the result from S3 is ordered by key 
@@ -107,42 +112,34 @@ pub(crate) async fn build_details_handler(
107112            . try_collect ( ) 
108113            . await ?; 
109114
110-         let  fetch_file = |filename :  String | async  { 
111-             let  file = File :: from_path ( & storage,  & format ! ( "{prefix}{filename}" ) ,  & config) . await ?; 
112-             Ok :: < _ ,  anyhow:: Error > ( String :: from_utf8 ( file. 0 . content ) . context ( "non utf8" ) ?) 
115+         let  current_filename = if  let  Some ( filename)  = params. filename  { 
116+             // if we have a given filename in the URL, we use that one. 
117+             Some ( filename) 
118+         }  else  if  let  Some ( default_target)  = row. default_target  { 
119+             // without a filename in the URL, we try to show the build log 
120+             // for the default target, if we have one. 
121+             let  wanted_filename = format ! ( "{default_target}.txt" ) ; 
122+             if  all_log_filenames. contains ( & wanted_filename)  { 
123+                 Some ( wanted_filename) 
124+             }  else  { 
125+                 None 
126+             } 
127+         }  else  { 
128+             // this can only happen when `releases.default_target` is NULL, 
129+             // which is the case for in-progress builds or builds which errored 
130+             // before we could determine the target. 
131+             // For the "error" case we show `row.errors`, which should contain what we need to see. 
132+             None 
113133        } ; 
114134
115-         let  ( filename,  file_content)  = if  let  Some ( filename)  = params. filename  { 
116-             ( Some ( filename. clone ( ) ) ,  fetch_file ( filename. clone ( ) ) . await ?) 
117-         }  else  if  let  Some ( default_target)  = row. default_target  { 
118-             let  filename = format ! ( "{default_target}.txt" ) ; 
119-             ( 
120-                 Some ( filename) , 
121-                 match  fetch_file ( filename. clone ( ) ) . await  { 
122-                     Ok ( content)  => content, 
123-                     Err ( _err)  => "" . to_string ( ) , 
124-                 } , 
125-             ) 
135+         let  file_content = if  let  Some ( ref  filename)  = current_filename { 
136+             let  file = File :: from_path ( & storage,  & format ! ( "{prefix}{filename}" ) ,  & config) . await ?; 
137+             String :: from_utf8 ( file. 0 . content ) . context ( "non utf8" ) ?
126138        }  else  { 
127-             ( None ,   "" . into ( ) ) 
139+             "" . to_string ( ) 
128140        } ; 
129141
130-         // .or(row.default_target.map(|target| format!("{}.txt", target))) 
131-         // { 
132-         //     let path = format!("{prefix}{current_filename}"); 
133-         //     let file = File::from_path(&storage, &path, &config).await?; 
134-         //     ( 
135-         //         String::from_utf8(file.0.content).context("non utf8")?, 
136-         //         Some(current_filename), 
137-         //     ) 
138-         // } else { 
139-         //     // this can only happen when `releases.default_target` is NULL, 
140-         //     // which is the case for in-progress builds or builds which errored 
141-         //     // before we could determine the target. 
142-         //     // For the "error" case we show `row.errors`, which should contain what we need to see. 
143-         //     ("".into(), Vec::new(), None) 
144-         // } 
145-         ( file_content,  all_log_filenames,  filename) 
142+         ( file_content,  all_log_filenames,  current_filename) 
146143    } ; 
147144
148145    Ok ( BuildDetailsPage  { 
@@ -216,6 +213,44 @@ mod tests {
216213        } ) ; 
217214    } 
218215
216+     #[ test]  
217+     fn  test_partial_build_result_plus_default_target_from_previous_build ( )  { 
218+         async_wrapper ( |env| async  move  { 
219+             let  mut  conn = env. async_db ( ) . await . async_conn ( ) . await ; 
220+             let  ( release_id,  build_id)  = fake_release_that_failed_before_build ( 
221+                 & mut  conn, 
222+                 "foo" , 
223+                 "0.1.0" , 
224+                 "some random error" , 
225+             ) 
226+             . await ?; 
227+ 
228+             sqlx:: query!( 
229+                 "UPDATE releases SET default_target = 'x86_64-unknown-linux-gnu' WHERE id = $1" , 
230+                 release_id. 0 
231+             ) 
232+             . execute ( & mut  * conn) 
233+             . await ?; 
234+ 
235+             let  page = kuchikiki:: parse_html ( ) . one ( 
236+                 env. web_app ( ) 
237+                     . await 
238+                     . get ( & format ! ( "/crate/foo/0.1.0/builds/{build_id}" ) ) 
239+                     . await ?
240+                     . error_for_status ( ) ?
241+                     . text ( ) 
242+                     . await ?, 
243+             ) ; 
244+ 
245+             let  info_text = page. select ( "pre" ) . unwrap ( ) . next ( ) . unwrap ( ) . text_contents ( ) ; 
246+ 
247+             assert ! ( info_text. contains( "# pre-build errors" ) ,  "{}" ,  info_text) ; 
248+             assert ! ( info_text. contains( "some random error" ) ,  "{}" ,  info_text) ; 
249+ 
250+             Ok ( ( ) ) 
251+         } ) ; 
252+     } 
253+ 
219254    #[ test]  
220255    fn  db_build_logs ( )  { 
221256        async_wrapper ( |env| async  move  { 
@@ -308,6 +343,66 @@ mod tests {
308343        } ) ; 
309344    } 
310345
346+     // #[test] 
347+     // fn build_details_for_build_with_pre_build_errors() { 
348+     //     async_wrapper(|env| async move { 
349+     //         let mut conn = env.async_db().await.async_conn().await; 
350+     //         let (release_id, build_id) = 
351+     //             fake_release_that_failed_before_build(&mut conn, "foo", "0.1.0", "some error") 
352+     //                 .await?; 
353+ 
354+     //         let web = env.web_app().await; 
355+ 
356+     //         let page = kuchikiki::parse_html().one( 
357+     //             web.get("/crate/foo/0.1.0/builds") 
358+     //                 .await? 
359+     //                 .error_for_status()? 
360+     //                 .text() 
361+     //                 .await?, 
362+     //         ); 
363+ 
364+     //         let node = page.select("ul > li a.release").unwrap().next().unwrap(); 
365+     //         let build_url = { 
366+     //             let attrs = node.attributes.borrow(); 
367+     //             attrs.get("href").unwrap().to_owned() 
368+     //         }; 
369+ 
370+     //         let page = kuchikiki::parse_html().one( 
371+     //             web.get(&build_url) 
372+     //                 .await? 
373+     //                 .error_for_status()? 
374+     //                 .text() 
375+     //                 .await?, 
376+     //         ); 
377+ 
378+     //         let log = page.select("pre").unwrap().next().unwrap().text_contents(); 
379+ 
380+     //         assert!(log.contains("some error")); 
381+ 
382+     //         let all_log_links = get_all_log_links(&page); 
383+     //         assert_eq!( 
384+     //             all_log_links, 
385+     //             vec![( 
386+     //                 "x86_64-unknown-linux-gnu.txt".into(), 
387+     //                 format!("{build_url}/x86_64-unknown-linux-gnu.txt") 
388+     //             )] 
389+     //         ); 
390+ 
391+     //         // now get the log with the specific filename in the URL 
392+     //         let log = kuchikiki::parse_html() 
393+     //             .one(web.get(&all_log_links[0].1).await?.text().await?) 
394+     //             .select("pre") 
395+     //             .unwrap() 
396+     //             .next() 
397+     //             .unwrap() 
398+     //             .text_contents(); 
399+ 
400+     //         assert!(log.contains("some error")); 
401+ 
402+     //         Ok(()) 
403+     //     }); 
404+     // } 
405+ 
311406    #[ test]  
312407    fn  s3_build_logs_multiple_targets ( )  { 
313408        async_wrapper ( |env| async  move  { 
0 commit comments