get: archive download fallback for non-git packages
This commit is contained in:
@@ -22,6 +22,8 @@ indicatif = "0.17"
|
||||
indicatif-log-bridge = "0.2.3"
|
||||
env_logger = "0.11.8"
|
||||
futures-util = { version = "0.3.31", features = ["tokio-io"] }
|
||||
tar = "0.4"
|
||||
xz2 = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.10.1"
|
||||
|
||||
78
src/get.rs
78
src/get.rs
@@ -62,6 +62,28 @@ use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use futures_util::StreamExt;
|
||||
use flate2::read::GzDecoder;
|
||||
use xz2::read::XzDecoder;
|
||||
use tar::Archive;
|
||||
|
||||
fn extract_archive(path: &Path, dest: &Path) -> Result<(), Box<dyn Error>> {
|
||||
let file = File::open(path)?;
|
||||
let filename = path.file_name().unwrap().to_string_lossy();
|
||||
|
||||
if filename.ends_with(".tar.gz") || filename.ends_with(".tgz") {
|
||||
let tar = GzDecoder::new(file);
|
||||
let mut archive = Archive::new(tar);
|
||||
archive.unpack(dest)?;
|
||||
} else if filename.ends_with(".tar.xz") || filename.ends_with(".txz") {
|
||||
let tar = XzDecoder::new(file);
|
||||
let mut archive = Archive::new(tar);
|
||||
archive.unpack(dest)?;
|
||||
} else {
|
||||
return Err(format!("Unsupported archive format: {}", filename).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn checkout_pristine_tar(package_dir: &Path, filename: &str) -> Result<(), Box<dyn Error>> {
|
||||
let output = Command::new("pristine-tar")
|
||||
@@ -181,6 +203,42 @@ async fn fetch_orig_tarball(info: &PackageInfo, cwd: Option<&Path>, progress: Pr
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fetch_archive_sources(info: &PackageInfo, cwd: Option<&Path>, progress: ProgressCallback<'_>) -> Result<(), Box<dyn Error>> {
|
||||
let package_dir = if let Some(path) = cwd {
|
||||
path.join(&info.stanza.package)
|
||||
} else {
|
||||
Path::new(&info.stanza.package).to_path_buf()
|
||||
};
|
||||
|
||||
std::fs::create_dir_all(&package_dir)?;
|
||||
|
||||
for file in &info.stanza.files {
|
||||
let url = format!("{}/{}", info.archive_url, file.name);
|
||||
download_file_checksum(&url, &file.sha256, &package_dir, progress).await?;
|
||||
}
|
||||
|
||||
// Extract the debian tarball or diff
|
||||
let debian_file = info.stanza.files.iter().find(|f| {
|
||||
f.name.contains(".debian.tar.") || f.name.contains(".diff.gz")
|
||||
});
|
||||
|
||||
if let Some(file) = debian_file {
|
||||
let path = package_dir.join(&file.name);
|
||||
let extract_dir = package_dir.join(&info.stanza.package);
|
||||
|
||||
if file.name.ends_with(".tar.xz") || file.name.ends_with(".tar.gz") {
|
||||
if let Err(e) = extract_archive(&path, &extract_dir) {
|
||||
return Err(format!("Failed to extract {}: {}", file.name, e).into());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove archive after extraction
|
||||
std::fs::remove_file(&path)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get(package: &str, _version: &str, series: &str, pocket: &str, _ppa: &str, cwd: Option<&Path>, progress: ProgressCallback<'_>) -> Result<(), Box<dyn Error>> {
|
||||
if let Some(cb) = progress {
|
||||
cb(&format!("Resolving package info for {}...", package), "", 0, 0);
|
||||
@@ -205,7 +263,10 @@ pub async fn get(package: &str, _version: &str, series: &str, pocket: &str, _ppa
|
||||
}
|
||||
fetch_orig_tarball(&info, Some(&package_dir), progress).await?;
|
||||
} else {
|
||||
return Err(format!("No VCS URL found for package {}", package).into());
|
||||
if let Some(cb) = progress {
|
||||
cb("Downloading from archive...", "", 0, 0);
|
||||
}
|
||||
fetch_archive_sources(&info, Some(cwd.unwrap_or(Path::new("."))), progress).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -227,9 +288,9 @@ mod tests {
|
||||
|
||||
let package_dir = cwd.join(package);
|
||||
assert!(package_dir.exists(), "Package directory not created");
|
||||
let git_repo_dir = package_dir.join(package);
|
||||
assert!(git_repo_dir.exists(), "Package git repo directory not created");
|
||||
assert!(git_repo_dir.join(".git").exists(), "Git repo not cloned");
|
||||
let package_source_dir = package_dir.join(package);
|
||||
assert!(package_source_dir.exists(), "Package git repo directory not created");
|
||||
assert!(package_source_dir.join("debian").exists(), "debian directory not present");
|
||||
|
||||
// Check for orig tarball in package dir
|
||||
let mut found_tarball = false;
|
||||
@@ -255,12 +316,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(expected = "No VCS URL found for package agg")]
|
||||
async fn test_get_agg_svn_should_fail() {
|
||||
// agg uses Vcs-Svn, which is not supported.
|
||||
// We expect this to fail with "No VCS URL found".
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let cwd = temp_dir.path();
|
||||
get("agg", "", "sid", "", "", Some(cwd), None).await.unwrap();
|
||||
async fn test_get_agg_svn_fallback_ok() {
|
||||
test_get_package_end_to_end("agg", "sid").await;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user