pull: always extract tars when archive pulling, merging dirs
Some checks failed
CI / build (push) Failing after 3m18s
Some checks failed
CI / build (push) Failing after 3m18s
This commit is contained in:
147
src/pull.rs
147
src/pull.rs
@@ -1,6 +1,7 @@
|
||||
use std::cmp::min;
|
||||
use std::error::Error;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::package_info::PackageInfo;
|
||||
|
||||
@@ -110,23 +111,71 @@ fn copy_dir_all(src: &Path, dst: &Path) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn extract_archive(path: &Path, dest: &Path) -> Result<(), Box<dyn Error>> {
|
||||
let file = File::open(path)?;
|
||||
/// Helper function to extract tar archive with progress tracking
|
||||
fn extract_tar_archive<D, F>(
|
||||
file_path: &Path,
|
||||
dest: &Path,
|
||||
progress: ProgressCallback<'_>,
|
||||
decoder_factory: F,
|
||||
) -> Result<Vec<String>, Box<dyn Error>>
|
||||
where
|
||||
D: std::io::Read,
|
||||
F: Fn(File) -> D,
|
||||
{
|
||||
let file = File::open(file_path)?;
|
||||
let decoder = decoder_factory(file);
|
||||
let mut archive = Archive::new(decoder);
|
||||
|
||||
// Get total number of entries for progress tracking
|
||||
let total_entries = archive.entries()?.count();
|
||||
let mut current_entry = 0;
|
||||
|
||||
// Reset the archive to read entries again
|
||||
let file = File::open(file_path)?;
|
||||
let decoder = decoder_factory(file);
|
||||
let mut archive = Archive::new(decoder);
|
||||
|
||||
let mut extracted_files = Vec::new();
|
||||
|
||||
for entry in archive.entries()? {
|
||||
let mut entry = entry?;
|
||||
let path = entry.path()?.to_path_buf();
|
||||
let dest_path = dest.join(&path);
|
||||
|
||||
// Create parent directories if needed
|
||||
if let Some(parent) = dest_path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
// Extract the file
|
||||
entry.unpack(&dest_path)?;
|
||||
extracted_files.push(dest_path.to_string_lossy().to_string());
|
||||
|
||||
current_entry += 1;
|
||||
|
||||
// Report progress
|
||||
if let Some(cb) = progress {
|
||||
cb("", "Extracting...", current_entry, total_entries);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(extracted_files)
|
||||
}
|
||||
|
||||
fn extract_archive(
|
||||
path: &Path,
|
||||
dest: &Path,
|
||||
progress: ProgressCallback<'_>,
|
||||
) -> Result<Vec<String>, Box<dyn Error>> {
|
||||
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)?;
|
||||
extract_tar_archive(path, dest, progress, GzDecoder::new)
|
||||
} 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)?;
|
||||
extract_tar_archive(path, dest, progress, XzDecoder::new)
|
||||
} else {
|
||||
return Err(format!("Unsupported archive format: {}", filename).into());
|
||||
Err(format!("Unsupported archive format: {}", filename).into())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn checkout_pristine_tar(package_dir: &Path, filename: &str) -> Result<(), Box<dyn Error>> {
|
||||
@@ -326,59 +375,45 @@ async fn fetch_archive_sources(
|
||||
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 {
|
||||
// Extract all tar archives, merging extracted directories
|
||||
if file.name.contains(".tar.") {
|
||||
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"))
|
||||
&& let Err(e) = extract_archive(&path, &extract_dir)
|
||||
{
|
||||
return Err(format!("Failed to extract {}: {}", file.name, e).into());
|
||||
let extracted = extract_archive(&path, &extract_dir, progress)?;
|
||||
|
||||
// Special case: the debian tar does only contain 'debian'
|
||||
if file.name.contains("debian.tar.") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove archive after extraction
|
||||
std::fs::remove_file(&path)?;
|
||||
// List root directories extracted and use the first one as the source directory
|
||||
debug!("Root directories extracted:");
|
||||
let mut source_dir: Option<PathBuf> = None;
|
||||
for file in &extracted {
|
||||
let path = Path::new(file);
|
||||
// Check if this is a directory and is at the archive root level
|
||||
// (i.e., the path relative to extract_dir has no parent components)
|
||||
if let Ok(relative_path) = path.strip_prefix(&extract_dir)
|
||||
&& relative_path.components().count() == 1
|
||||
&& path.is_dir()
|
||||
{
|
||||
debug!("- {}", relative_path.file_name().unwrap().to_string_lossy());
|
||||
// Use the first directory found as the source
|
||||
if source_dir.is_none() {
|
||||
source_dir = Some(path.to_path_buf());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the orig tarball if present and not native package
|
||||
if !info.is_native()
|
||||
&& let Some(orig_file) = info
|
||||
.stanza
|
||||
.files
|
||||
.iter()
|
||||
.find(|f| f.name.contains(".orig.tar."))
|
||||
{
|
||||
let path = package_dir.join(&orig_file.name);
|
||||
let extract_dir = package_dir;
|
||||
|
||||
if (orig_file.name.ends_with(".tar.xz") || orig_file.name.ends_with(".tar.gz"))
|
||||
&& let Err(e) = extract_archive(&path, extract_dir)
|
||||
{
|
||||
return Err(format!("Failed to extract {}: {}", orig_file.name, e).into());
|
||||
}
|
||||
|
||||
// Rename from 'package-origversion' to 'package', merging with existing directory
|
||||
// Use the extracted directory as the source, assuming there is only one
|
||||
if let Some(src_dir) = source_dir {
|
||||
let target_dir = package_dir.join(&info.stanza.package);
|
||||
let entries = std::fs::read_dir(package_dir)?;
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
let entry_path = entry.path();
|
||||
if entry_path.is_dir() {
|
||||
let dir_name = entry.file_name().to_string_lossy().to_string();
|
||||
if dir_name.starts_with(&format!("{}-", info.stanza.package)) {
|
||||
// Found a directory like 'package-version', rename it to 'package'
|
||||
|
||||
if target_dir.exists() {
|
||||
// Target exists, we need to merge contents
|
||||
for sub_entry in std::fs::read_dir(&entry_path)? {
|
||||
for sub_entry in std::fs::read_dir(&src_dir)? {
|
||||
let sub_entry = sub_entry?;
|
||||
let sub_path = sub_entry.path();
|
||||
let target_path = target_dir.join(sub_entry.file_name());
|
||||
@@ -390,11 +425,9 @@ async fn fetch_archive_sources(
|
||||
std::fs::copy(&sub_path, &target_path)?;
|
||||
}
|
||||
}
|
||||
std::fs::remove_dir_all(&entry_path)?;
|
||||
std::fs::remove_dir_all(&src_dir)?;
|
||||
} else {
|
||||
std::fs::rename(&entry_path, &target_dir)?;
|
||||
}
|
||||
break;
|
||||
std::fs::rename(&src_dir, &target_dir)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user