@@ -93,8 +93,13 @@ pub(crate) async fn build_details_handler(
93
93
. ok_or ( AxumNope :: BuildNotFound ) ?;
94
94
95
95
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.
96
97
( output, Vec :: new ( ) , None )
97
98
} 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.
98
103
let prefix = format ! ( "build-logs/{}/" , id) ;
99
104
let all_log_filenames: Vec < _ > = storage
100
105
. list_prefix ( & prefix) // the result from S3 is ordered by key
@@ -107,42 +112,34 @@ pub(crate) async fn build_details_handler(
107
112
. try_collect ( )
108
113
. await ?;
109
114
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
113
133
} ;
114
134
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" ) ?
126
138
} else {
127
- ( None , "" . into ( ) )
139
+ "" . to_string ( )
128
140
} ;
129
141
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)
146
143
} ;
147
144
148
145
Ok ( BuildDetailsPage {
@@ -216,6 +213,44 @@ mod tests {
216
213
} ) ;
217
214
}
218
215
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
+
219
254
#[ test]
220
255
fn db_build_logs ( ) {
221
256
async_wrapper ( |env| async move {
@@ -308,6 +343,66 @@ mod tests {
308
343
} ) ;
309
344
}
310
345
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
+
311
406
#[ test]
312
407
fn s3_build_logs_multiple_targets ( ) {
313
408
async_wrapper ( |env| async move {
0 commit comments