Skip to content

Commit 677297a

Browse files
committed
store & show build logs for non-default targets
1 parent d0d0617 commit 677297a

12 files changed

+400
-71
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ jobs:
106106
107107
- name: run slow tests
108108
env:
109-
DOCSRS_INCLUDE_DEFAULT_TARGETS: false
109+
DOCSRS_INCLUDE_DEFAULT_TARGETS: true
110110
run: |
111111
cargo test --locked -- --ignored --test-threads=1
112112

.sqlx/query-3d98a27dab09b6f74a097b8cea8077063f77fb1758c9c4a075d2dfa4e4a80822.json

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.lock

+23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ hex = "0.4.3"
7474
# Async
7575
tokio = { version = "1.0", features = ["rt-multi-thread", "signal", "macros"] }
7676
futures-util = "0.3.5"
77+
async-stream = "0.3.5"
7778
aws-config = "1.0.0"
7879
aws-sdk-s3 = "1.3.0"
7980
aws-sdk-cloudfront = "1.3.0"

src/docbuilder/rustwide_builder.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ impl RustwideBuilder {
478478
}
479479

480480
let mut algs = HashSet::new();
481+
let mut target_build_logs = HashMap::new();
481482
if has_docs {
482483
debug!("adding documentation for the default target to the database");
483484
self.copy_docs(
@@ -493,14 +494,15 @@ impl RustwideBuilder {
493494
// Limit the number of targets so that no one can try to build all 200000 possible targets
494495
for target in other_targets.into_iter().take(limits.targets()) {
495496
debug!("building package {} {} for {}", name, version, target);
496-
self.build_target(
497+
let target_res = self.build_target(
497498
target,
498499
build,
499500
&limits,
500501
local_storage.path(),
501502
&mut successful_targets,
502503
&metadata,
503504
)?;
505+
target_build_logs.insert(target, target_res.build_log);
504506
}
505507
let (_, new_alg) = self.runtime.block_on(add_path_into_remote_archive(
506508
&self.async_storage,
@@ -588,6 +590,10 @@ impl RustwideBuilder {
588590
))?;
589591
let build_log_path = format!("build-logs/{build_id}/{default_target}.txt");
590592
self.storage.store_one(build_log_path, res.build_log)?;
593+
for (target, log) in target_build_logs {
594+
let build_log_path = format!("build-logs/{build_id}/{target}.txt");
595+
self.storage.store_one(build_log_path, log)?;
596+
}
591597

592598
// Some crates.io crate data is mutable, so we proactively update it during a release
593599
if !is_local {
@@ -640,7 +646,7 @@ impl RustwideBuilder {
640646
local_storage: &Path,
641647
successful_targets: &mut Vec<String>,
642648
metadata: &Metadata,
643-
) -> Result<()> {
649+
) -> Result<FullBuildResult> {
644650
let target_res = self.execute_build(target, false, build, limits, metadata, false)?;
645651
if target_res.result.successful {
646652
// Cargo is not giving any error and not generating documentation of some crates
@@ -652,7 +658,7 @@ impl RustwideBuilder {
652658
successful_targets.push(target.to_string());
653659
}
654660
}
655-
Ok(())
661+
Ok(target_res)
656662
}
657663

658664
fn get_coverage(
@@ -981,25 +987,26 @@ mod tests {
981987

982988
// check release record in the db (default and other targets)
983989
let mut conn = env.db().conn();
984-
let rows = conn
985-
.query(
990+
let row = conn
991+
.query_one(
986992
"SELECT
987993
r.rustdoc_status,
988994
r.default_target,
989995
r.doc_targets,
990996
r.archive_storage,
991-
cov.total_items
997+
cov.total_items,
998+
b.id as build_id
992999
FROM
9931000
crates as c
9941001
INNER JOIN releases AS r ON c.id = r.crate_id
1002+
INNER JOIN builds as b ON r.id = b.rid
9951003
LEFT OUTER JOIN doc_coverage AS cov ON r.id = cov.release_id
9961004
WHERE
9971005
c.name = $1 AND
9981006
r.version = $2",
9991007
&[&crate_, &version],
10001008
)
10011009
.unwrap();
1002-
let row = rows.first().unwrap();
10031010

10041011
assert!(row.get::<_, bool>("rustdoc_status"));
10051012
assert_eq!(row.get::<_, String>("default_target"), default_target);
@@ -1086,6 +1093,13 @@ mod tests {
10861093

10871094
assert!(target_docs_present);
10881095
assert_success(&target_url, web)?;
1096+
1097+
assert!(storage
1098+
.exists(&format!(
1099+
"build-logs/{}/{target}.txt",
1100+
row.get::<_, i32>("build_id")
1101+
))
1102+
.unwrap());
10891103
}
10901104
}
10911105

src/storage/database.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1+
use super::{Blob, FileRange};
2+
use crate::{db::Pool, error::Result, InstanceMetrics};
13
use chrono::{DateTime, Utc};
4+
use futures_util::stream::{Stream, TryStreamExt};
25
use sqlx::Acquire;
3-
4-
use super::{Blob, FileRange};
5-
use crate::db::Pool;
6-
use crate::error::Result;
7-
use crate::InstanceMetrics;
86
use std::{convert::TryFrom, sync::Arc};
97

108
pub(crate) struct DatabaseBackend {
@@ -167,6 +165,22 @@ impl DatabaseBackend {
167165
Ok(())
168166
}
169167

168+
pub(super) async fn list_prefix<'a>(
169+
&'a self,
170+
prefix: &'a str,
171+
) -> impl Stream<Item = Result<String>> + 'a {
172+
sqlx::query!(
173+
"SELECT path
174+
FROM files
175+
WHERE path LIKE $1
176+
ORDER BY path;",
177+
format!("{}%", prefix.replace('%', "\\%"))
178+
)
179+
.fetch(&self.pool)
180+
.map_err(Into::into)
181+
.map_ok(|row| row.path)
182+
}
183+
170184
pub(crate) async fn delete_prefix(&self, prefix: &str) -> Result<()> {
171185
sqlx::query!(
172186
"DELETE FROM files WHERE path LIKE $1;",

src/storage/mod.rs

+65-5
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@ mod s3;
66
pub use self::compression::{compress, decompress, CompressionAlgorithm, CompressionAlgorithms};
77
use self::database::DatabaseBackend;
88
use self::s3::S3Backend;
9-
use crate::error::Result;
10-
use crate::web::metrics::RenderingTimesRecorder;
11-
use crate::{db::Pool, utils::spawn_blocking, Config, InstanceMetrics};
9+
use crate::{
10+
db::Pool, error::Result, utils::spawn_blocking, web::metrics::RenderingTimesRecorder, Config,
11+
InstanceMetrics,
12+
};
1213
use anyhow::{anyhow, ensure};
1314
use chrono::{DateTime, Utc};
1415
use fn_error_context::context;
16+
use futures_util::stream::BoxStream;
1517
use path_slash::PathExt;
16-
use std::io::BufReader;
1718
use std::{
1819
collections::{HashMap, HashSet},
1920
ffi::OsStr,
20-
fmt, fs, io,
21+
fmt, fs,
22+
io::{self, BufReader},
2123
ops::RangeInclusive,
2224
path::{Path, PathBuf},
2325
sync::Arc,
@@ -558,6 +560,16 @@ impl AsyncStorage {
558560
}
559561
}
560562

563+
pub(super) async fn list_prefix<'a>(
564+
&'a self,
565+
prefix: &'a str,
566+
) -> BoxStream<'a, Result<String>> {
567+
match &self.backend {
568+
StorageBackend::Database(db) => Box::pin(db.list_prefix(prefix).await),
569+
StorageBackend::S3(s3) => Box::pin(s3.list_prefix(prefix).await),
570+
}
571+
}
572+
561573
pub(crate) async fn delete_prefix(&self, prefix: &str) -> Result<()> {
562574
match &self.backend {
563575
StorageBackend::Database(db) => db.delete_prefix(prefix).await,
@@ -752,6 +764,22 @@ impl Storage {
752764
self.runtime.block_on(self.inner.store_one(path, content))
753765
}
754766

767+
/// sync wrapper for the list_prefix function
768+
/// purely for testing purposes since it collects all files into a Vec.
769+
#[cfg(test)]
770+
pub(crate) fn list_prefix(&self, prefix: &str) -> impl Iterator<Item = Result<String>> {
771+
use futures_util::stream::StreamExt;
772+
self.runtime
773+
.block_on(async {
774+
self.inner
775+
.list_prefix(prefix)
776+
.await
777+
.collect::<Vec<_>>()
778+
.await
779+
})
780+
.into_iter()
781+
}
782+
755783
pub(crate) fn delete_prefix(&self, prefix: &str) -> Result<()> {
756784
self.runtime.block_on(self.inner.delete_prefix(prefix))
757785
}
@@ -964,6 +992,37 @@ mod backend_tests {
964992
Ok(())
965993
}
966994

995+
fn test_list_prefix(storage: &Storage) -> Result<()> {
996+
static FILENAMES: &[&str] = &["baz.txt", "some/bar.txt"];
997+
998+
storage.store_blobs(
999+
FILENAMES
1000+
.iter()
1001+
.map(|&filename| Blob {
1002+
path: filename.into(),
1003+
mime: "text/plain".into(),
1004+
date_updated: Utc::now(),
1005+
compression: None,
1006+
content: b"test content\n".to_vec(),
1007+
})
1008+
.collect(),
1009+
)?;
1010+
1011+
assert_eq!(
1012+
storage.list_prefix("").collect::<Result<Vec<String>>>()?,
1013+
FILENAMES
1014+
);
1015+
1016+
assert_eq!(
1017+
storage
1018+
.list_prefix("some/")
1019+
.collect::<Result<Vec<String>>>()?,
1020+
&["some/bar.txt"]
1021+
);
1022+
1023+
Ok(())
1024+
}
1025+
9671026
fn test_too_long_filename(storage: &Storage) -> Result<()> {
9681027
// minio returns ErrKeyTooLongError when the key is over 1024 bytes long.
9691028
// When testing, minio just gave me `XMinioInvalidObjectName`, so I'll check that too.
@@ -1321,6 +1380,7 @@ mod backend_tests {
13211380
test_get_range,
13221381
test_get_too_big,
13231382
test_too_long_filename,
1383+
test_list_prefix,
13241384
test_delete_prefix,
13251385
test_delete_prefix_without_matches,
13261386
test_delete_percent,

0 commit comments

Comments
 (0)