@@ -93,37 +93,53 @@ 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) ; 
104+         let  all_log_filenames:  Vec < _ >  = storage
105+             . list_prefix ( & prefix)  // the result from S3 is ordered by key 
106+             . await 
107+             . map_ok ( |path| { 
108+                 path. strip_prefix ( & prefix) 
109+                     . expect ( "since we query for the prefix, it has to be always there" ) 
110+                     . to_owned ( ) 
111+             } ) 
112+             . try_collect ( ) 
113+             . await ?; 
99114
100-         if  let  Some ( current_filename)  = params
101-             . filename 
102-             . or ( row. default_target . map ( |target| format ! ( "{}.txt" ,  target) ) ) 
103-         { 
104-             let  path = format ! ( "{prefix}{current_filename}" ) ; 
105-             let  file = File :: from_path ( & storage,  & path,  & config) . await ?; 
106-             ( 
107-                 String :: from_utf8 ( file. 0 . content ) . context ( "non utf8" ) ?, 
108-                 storage
109-                     . list_prefix ( & prefix)  // the result from S3 is ordered by key 
110-                     . await 
111-                     . map_ok ( |path| { 
112-                         path. strip_prefix ( & prefix) 
113-                             . expect ( "since we query for the prefix, it has to be always there" ) 
114-                             . to_owned ( ) 
115-                     } ) 
116-                     . try_collect ( ) 
117-                     . await ?, 
118-                 Some ( current_filename) , 
119-             ) 
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+             } 
120127        }  else  { 
121128            // this can only happen when `releases.default_target` is NULL, 
122129            // which is the case for in-progress builds or builds which errored 
123130            // before we could determine the target. 
124131            // For the "error" case we show `row.errors`, which should contain what we need to see. 
125-             ( "" . into ( ) ,  Vec :: new ( ) ,  None ) 
126-         } 
132+             None 
133+         } ; 
134+ 
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" ) ?
138+         }  else  { 
139+             "" . to_string ( ) 
140+         } ; 
141+ 
142+         ( file_content,  all_log_filenames,  current_filename) 
127143    } ; 
128144
129145    Ok ( BuildDetailsPage  { 
@@ -197,6 +213,44 @@ mod tests {
197213        } ) ; 
198214    } 
199215
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+ 
200254    #[ test]  
201255    fn  db_build_logs ( )  { 
202256        async_wrapper ( |env| async  move  { 
0 commit comments