Skip to content

Commit 30a1592

Browse files
committed
Actually download gems from a server
1 parent b6385a5 commit 30a1592

File tree

5 files changed

+95
-12
lines changed

5 files changed

+95
-12
lines changed

Cargo.lock

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

crates/rv-lockfile/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
mod datatypes;
2-
pub mod parser;
1+
pub mod datatypes;
2+
mod parser;
33
#[cfg(test)]
44
mod tests;
55

crates/rv-lockfile/tests/inputs/Gemfile.lock.withpath

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ PATH
44
pathgem (0.1.0)
55

66
GEM
7-
remote: https://example.com
7+
remote: https://gem.coop
88
specs:
99
actioncable (7.2.2.1)
1010

crates/rv/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ once_cell = { workspace = true }
5252
rayon-tracing = { workspace = true }
5353
clap_complete = "4.5.57"
5454
clap_complete_nushell = "4.5.8"
55+
url = "2.5.7"
56+
bytes = "1.10.1"
5557

5658
[dev-dependencies]
5759
insta = { workspace = true }

crates/rv/src/commands/ruby/ci.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,96 @@
1+
use bytes::Bytes;
2+
use camino::Utf8PathBuf;
3+
use futures_util::StreamExt;
4+
use futures_util::TryStreamExt;
5+
use reqwest::Client;
6+
use rv_lockfile::datatypes::GemfileDotLock;
7+
use url::Url;
8+
19
use crate::config::Config;
210
use std::io;
311

12+
const CONCURRENT_REQUESTS_PER_SOURCE: usize = 10;
13+
414
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
515
pub enum Error {
616
#[error(transparent)]
717
Parse(#[from] rv_lockfile::ParseErrors),
818
#[error(transparent)]
919
Io(#[from] io::Error),
20+
#[error(transparent)]
21+
Reqwest(#[from] reqwest::Error),
22+
#[error("Invalid remote URL")]
23+
BadRemote {
24+
remote: String,
25+
err: url::ParseError,
26+
},
27+
#[error(transparent)]
28+
UrlError(#[from] url::ParseError),
1029
}
1130

1231
type Result<T> = std::result::Result<T, Error>;
1332

14-
pub async fn ci(_config: &Config, gemfile: camino::Utf8PathBuf) -> Result<()> {
33+
pub async fn ci(_config: &Config, gemfile: Utf8PathBuf) -> Result<()> {
34+
ci_inner(gemfile).await
35+
}
36+
37+
async fn ci_inner(gemfile: Utf8PathBuf) -> Result<()> {
1538
let gemfile_contents = std::fs::read_to_string(gemfile)?;
1639
let lockfile = rv_lockfile::parse(&gemfile_contents)?;
40+
download_gems(lockfile).await?;
41+
Ok(())
42+
}
43+
44+
async fn download_gems<'i>(lockfile: GemfileDotLock<'i>) -> Result<()> {
45+
let client = reqwest::Client::builder()
46+
.user_agent(format!("rv-{}", env!("CARGO_PKG_VERSION")))
47+
.build()?;
48+
for gem_source in lockfile.gem {
49+
// Get all URLs for downloading all gems from this source.
50+
let urls = gem_source
51+
.specs
52+
.iter()
53+
.map(|gem| {
54+
let remote = gem_source.remote;
55+
let gem_name = gem.gem_version.name;
56+
let gem_version = gem.gem_version.version;
57+
let path = format!("gems/{gem_name}-{gem_version}.gem");
58+
let url = url::Url::parse(remote)
59+
.map_err(|err| Error::BadRemote {
60+
remote: remote.to_owned(),
61+
err,
62+
})?
63+
.join(&path)?;
64+
Ok(url)
65+
})
66+
.collect::<Result<Vec<Url>>>()?;
67+
let url_stream = futures_util::stream::iter(urls);
68+
let _downloaded_gems: Vec<(Bytes, Url)> = url_stream
69+
.map(|url| download_gem(url, &client))
70+
.buffered(CONCURRENT_REQUESTS_PER_SOURCE)
71+
.try_collect()
72+
.await?;
73+
// TODO: Extract the gems and put them somewhere on the filesystem
74+
}
1775
Ok(())
1876
}
77+
78+
async fn download_gem(url: Url, client: &Client) -> Result<(Bytes, Url)> {
79+
eprintln!("Downloading from {url}");
80+
// TODO: Validate the checksum from the Lockfile if present.
81+
let contents = client.get(url.clone()).send().await?.bytes().await?;
82+
Ok((contents, url))
83+
}
84+
85+
#[cfg(test)]
86+
mod tests {
87+
use super::*;
88+
89+
#[tokio::test]
90+
async fn test_download_gems() -> Result<()> {
91+
eprintln!("{:?}", std::env::current_dir());
92+
let file = "../rv-lockfile/tests/inputs/Gemfile.lock.test0".into();
93+
ci_inner(file).await?;
94+
Ok(())
95+
}
96+
}

0 commit comments

Comments
 (0)