Skip to content

Commit a30a248

Browse files
committed
Unpack tarball contents
1 parent d8124d2 commit a30a248

File tree

1 file changed

+47
-18
lines changed
  • crates/rv/src/commands

1 file changed

+47
-18
lines changed

crates/rv/src/commands/ci.rs

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use bytes::Bytes;
2-
use camino::Utf8Path;
32
use camino::Utf8PathBuf;
3+
use flate2::read::GzDecoder;
44
use futures_util::StreamExt;
55
use futures_util::TryStreamExt;
66
use reqwest::Client;
77
use rv_lockfile::datatypes::GemSection;
8+
use rv_lockfile::datatypes::GemVersion;
89
use rv_lockfile::datatypes::GemfileDotLock;
910
use rv_lockfile::datatypes::Spec;
1011
use url::Url;
@@ -82,7 +83,7 @@ fn install_gems(downloaded: Vec<Downloaded>) -> Result<()> {
8283
let bundle_path = find_bundle_path()?;
8384
// 2. Unpack all the tarballs
8485
for download in downloaded {
85-
download.unpack_tarball(&bundle_path)?;
86+
download.unpack_tarball(bundle_path.clone())?;
8687
}
8788
// 3. Generate binstubs into DIR/bin/
8889
// 4. Handle compiling native extensions for gems with native extensions
@@ -131,37 +132,65 @@ struct Downloaded<'i> {
131132
}
132133

133134
impl<'i> Downloaded<'i> {
134-
fn unpack_tarball(self, bundle_path: &Utf8Path) -> Result<()> {
135+
fn unpack_tarball(self, bundle_path: Utf8PathBuf) -> Result<()> {
136+
eprintln!("Unpacking {bundle_path}");
135137
// Unpack the tarball into DIR/gems/
136-
// each inner tarball inside a .gem goes into a directory that uses
137-
// the gem's name tuple of NAME-VERSION(-PLATFORM), like this:
138-
// nokogiri-1.18.10-arm64-darwin racc-1.8.1 rack-3.2.3 rake-13.3.0
139-
// So first let's find the directory:
140-
let name = self.spec.gem_version.name;
141-
let version = self.spec.gem_version.version;
142-
let this_gem_dir = format!("{name}/{version}");
143-
let gem_dst = bundle_path.join("gems").join(this_gem_dir);
144-
std::fs::create_dir_all(&gem_dst)?;
138+
// It should contain a metadata zip, and a data zip
139+
// (and optionally, a checksum zip).
140+
let GemVersion { name, version } = self.spec.gem_version;
141+
let nameversion = format!("{name}-{version}");
145142

146143
// Then unpack the tarball into it.
147144
let contents = std::io::Cursor::new(self.contents);
148145
let mut archive = tar::Archive::new(contents);
149-
eprintln!("Unpacking gem tarball to {gem_dst}");
150146
for e in archive.entries()? {
151-
let mut entry = e?;
147+
let entry = e?;
152148
let entry_path = entry.path()?;
153149
match entry_path.display().to_string().as_str() {
154150
"metadata.gz" => {
155-
// Idk what to do with this.
151+
eprintln!("\tData archive");
152+
// Unzip the metadata file,
153+
// then write it to
154+
// BUNDLEPATH/specifications/name-version.gemspec
155+
156+
// First, create the destination.
157+
let metadata_dir = bundle_path.join("specifications/");
158+
std::fs::create_dir_all(&metadata_dir)?;
159+
let filename = format!("{nameversion}.gemspec");
160+
let dst_path = metadata_dir.join(filename);
161+
let mut dst = std::fs::File::create(dst_path)?;
162+
163+
// Then write the (unzipped) source into the destination.
164+
let mut unzipped_contents = GzDecoder::new(entry);
165+
std::io::copy(&mut unzipped_contents, &mut dst)?;
156166
}
157167
"data.tar.gz" => {
158-
// TODO: Unpack this data
168+
// for every ENTRY in the data tar, unpack it to
169+
// data.tar.gz => BUNDLEPATH/gems/name-version/ENTRY
170+
let data_dir: std::path::PathBuf =
171+
bundle_path.join("gems").join(&nameversion).into();
172+
std::fs::create_dir_all(&data_dir)?;
173+
let mut gem_data_archive = tar::Archive::new(GzDecoder::new(entry));
174+
eprintln!("\tData archive");
175+
for e in gem_data_archive.entries()? {
176+
let mut entry = e?;
177+
let entry_path = entry.path()?;
178+
let dst = data_dir.join(entry_path);
179+
180+
eprintln!("\t\tUnpacking to {}", dst.display());
181+
// Not sure if this is strictly necessary, or if we can know the
182+
// intermediate directories ahead of time.
183+
if let Some(dst_parent) = dst.parent() {
184+
std::fs::create_dir_all(dst_parent)?;
185+
}
186+
entry.unpack(dst)?;
187+
}
159188
}
160189
"checksums.yaml.gz" => {
161190
// TODO: Validate these checksums
162191
}
163-
_ => {
164-
// Unknown entry, just ignore it.
192+
other => {
193+
eprintln!("Unknown dir {other}")
165194
}
166195
}
167196
}

0 commit comments

Comments
 (0)