Skip to content

Commit a4fef7a

Browse files
committed
add dev-util to import release-docs from production for local testing
1 parent f51c3d9 commit a4fef7a

15 files changed

Lines changed: 732 additions & 14 deletions

File tree

Cargo.lock

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ postcard = { version = "1.1.3", default-features = false, features = ["use-std"]
4848
pretty_assertions = "1.4.0"
4949
rand = "0.9"
5050
regex = "1"
51-
reqwest = { version = "0.13", features = ["json", "gzip"] }
51+
reqwest = { version = "0.13", features = ["json", "gzip", "stream"] }
5252
sentry = { version = "0.46.0", features = ["panic", "tracing", "tower-http", "anyhow", "backtrace"] }
5353
serde = { version = "1.0", features = ["derive"] }
5454
serde_json = "1.0"
@@ -64,3 +64,4 @@ toml = "0.9.2"
6464
tracing = "0.1.37"
6565
url = { version = "2.1.1", features = ["serde"] }
6666
walkdir = "2"
67+
zip = { version = "7.0.0", default-features = false, features = ["bzip2"] }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[package]
2+
name = "docs_rs_import_release"
3+
version.workspace = true
4+
authors.workspace = true
5+
license.workspace = true
6+
repository.workspace = true
7+
edition.workspace = true
8+
description = "Import a successfully built release from docs.rs into a test deployment"
9+
10+
[dependencies]
11+
anyhow = { workspace = true }
12+
async-tar = { version = "0.6.0", default-features = false, features = ["runtime-tokio", "xattr"] }
13+
clap = { workspace = true }
14+
docs_rs_cargo_metadata = { path = "../../lib/docs_rs_cargo_metadata" }
15+
docs_rs_context = { path = "../../lib/docs_rs_context" }
16+
docs_rs_database = { path = "../../lib/docs_rs_database" }
17+
docs_rs_logging = { path = "../../lib/docs_rs_logging" }
18+
docs_rs_registry_api = { path = "../../lib/docs_rs_registry_api" }
19+
docs_rs_repository_stats = { path = "../../lib/docs_rs_repository_stats" }
20+
docs_rs_rustdoc_json = { path = "../../lib/docs_rs_rustdoc_json" }
21+
docs_rs_storage = { path = "../../lib/docs_rs_storage" }
22+
docs_rs_types = { path = "../../lib/docs_rs_types" }
23+
docs_rs_utils = { path = "../../lib/docs_rs_utils" }
24+
docsrs-metadata = { path = "../../lib/metadata" }
25+
futures-util = { workspace = true }
26+
regex = { workspace = true }
27+
reqwest = { workspace = true }
28+
serde = { workspace = true }
29+
sqlx = { workspace = true }
30+
tempfile = { workspace = true }
31+
tokio = { workspace = true }
32+
tracing = { workspace = true }
33+
walkdir = { workspace = true }
34+
zip = { workspace = true }
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use anyhow::Result;
2+
use docs_rs_utils::{APP_USER_AGENT, spawn_blocking};
3+
use futures_util::StreamExt as _;
4+
use std::{fmt, sync::LazyLock};
5+
use tokio::{
6+
fs,
7+
io::{AsyncSeekExt as _, AsyncWriteExt as _},
8+
};
9+
use tracing::debug;
10+
11+
pub(crate) const DOCS_RS: &str = "https://docs.rs";
12+
pub(crate) static CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
13+
reqwest::Client::builder()
14+
.user_agent(APP_USER_AGENT)
15+
.build()
16+
.expect("can't create request client & connection pool")
17+
});
18+
19+
pub(crate) async fn download(url: impl reqwest::IntoUrl + fmt::Debug) -> Result<Vec<u8>> {
20+
debug!("downloading...");
21+
22+
Ok(CLIENT
23+
.get(url)
24+
.send()
25+
.await?
26+
.error_for_status()?
27+
.bytes()
28+
.await?
29+
.to_vec())
30+
}
31+
32+
pub(crate) async fn download_to_temp_file(url: impl reqwest::IntoUrl) -> Result<fs::File> {
33+
debug!("downloading to temp file..");
34+
35+
let response = CLIENT.get(url).send().await?.error_for_status()?;
36+
37+
// NOTE: even after being convert to a `tokio::fs::File`, this kind of temporary file
38+
// will be cleaned up by the OS, when the last handle is closed.
39+
let mut file = fs::File::from_std(spawn_blocking(|| Ok(tempfile::tempfile()?)).await?);
40+
41+
let mut stream = response.bytes_stream();
42+
while let Some(chunk) = stream.next().await {
43+
let chunk = chunk?;
44+
file.write_all(&chunk).await?;
45+
}
46+
47+
file.sync_all().await?;
48+
file.seek(std::io::SeekFrom::Start(0)).await?;
49+
Ok(file)
50+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use crate::common::download_to_temp_file;
2+
use anyhow::{Result, bail};
3+
use async_tar::Archive;
4+
use docs_rs_storage::compression::wrap_reader_for_decompression;
5+
use docs_rs_types::{CompressionAlgorithm, KrateName, Version};
6+
use docs_rs_utils::spawn_blocking;
7+
use std::path::{Path, PathBuf};
8+
use tokio::io;
9+
use tracing::debug;
10+
11+
#[derive(Debug)]
12+
pub(crate) struct SourceDir {
13+
_temp_dir: tempfile::TempDir,
14+
pub(crate) source_path: PathBuf,
15+
}
16+
17+
impl AsRef<Path> for SourceDir {
18+
fn as_ref(&self) -> &Path {
19+
&self.source_path
20+
}
21+
}
22+
23+
pub(crate) async fn download_and_extract_source(
24+
name: &KrateName,
25+
version: &Version,
26+
) -> Result<SourceDir> {
27+
debug!("downloading source");
28+
let crate_archive = download_to_temp_file(format!(
29+
"https://static.crates.io/crates/{name}/{name}-{version}.crate"
30+
))
31+
.await?;
32+
33+
let temp_dir = spawn_blocking(|| Ok(tempfile::tempdir()?)).await?;
34+
35+
debug!("unpacking source archive");
36+
{
37+
let mut file = io::BufReader::new(crate_archive);
38+
let mut decompressed = wrap_reader_for_decompression(&mut file, CompressionAlgorithm::Gzip);
39+
let archive = Archive::new(&mut decompressed);
40+
archive.unpack(&temp_dir).await?;
41+
}
42+
43+
let source_path = temp_dir.path().join(format!("{name}-{version}"));
44+
if !source_path.is_dir() {
45+
bail!(
46+
"broken crate archive, missing source directory {:?}",
47+
source_path
48+
);
49+
};
50+
51+
Ok(SourceDir {
52+
source_path,
53+
_temp_dir: temp_dir,
54+
})
55+
}

0 commit comments

Comments
 (0)