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::cmp::min;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::package_info::PackageInfo;
|
use crate::package_info::PackageInfo;
|
||||||
|
|
||||||
@@ -110,23 +111,71 @@ fn copy_dir_all(src: &Path, dst: &Path) -> Result<(), Box<dyn Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_archive(path: &Path, dest: &Path) -> Result<(), Box<dyn Error>> {
|
/// Helper function to extract tar archive with progress tracking
|
||||||
let file = File::open(path)?;
|
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();
|
let filename = path.file_name().unwrap().to_string_lossy();
|
||||||
|
|
||||||
if filename.ends_with(".tar.gz") || filename.ends_with(".tgz") {
|
if filename.ends_with(".tar.gz") || filename.ends_with(".tgz") {
|
||||||
let tar = GzDecoder::new(file);
|
extract_tar_archive(path, dest, progress, GzDecoder::new)
|
||||||
let mut archive = Archive::new(tar);
|
|
||||||
archive.unpack(dest)?;
|
|
||||||
} else if filename.ends_with(".tar.xz") || filename.ends_with(".txz") {
|
} else if filename.ends_with(".tar.xz") || filename.ends_with(".txz") {
|
||||||
let tar = XzDecoder::new(file);
|
extract_tar_archive(path, dest, progress, XzDecoder::new)
|
||||||
let mut archive = Archive::new(tar);
|
|
||||||
archive.unpack(dest)?;
|
|
||||||
} else {
|
} 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>> {
|
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 {
|
for file in &info.stanza.files {
|
||||||
let url = format!("{}/{}", info.archive_url, file.name);
|
let url = format!("{}/{}", info.archive_url, file.name);
|
||||||
download_file_checksum(&url, &file.sha256, package_dir, progress).await?;
|
download_file_checksum(&url, &file.sha256, package_dir, progress).await?;
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the debian tarball or diff
|
// Extract all tar archives, merging extracted directories
|
||||||
let debian_file = info
|
if file.name.contains(".tar.") {
|
||||||
.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 path = package_dir.join(&file.name);
|
||||||
let extract_dir = package_dir.join(&info.stanza.package);
|
let extract_dir = package_dir.join(&info.stanza.package);
|
||||||
|
|
||||||
if (file.name.ends_with(".tar.xz") || file.name.ends_with(".tar.gz"))
|
let extracted = extract_archive(&path, &extract_dir, progress)?;
|
||||||
&& let Err(e) = extract_archive(&path, &extract_dir)
|
|
||||||
{
|
// Special case: the debian tar does only contain 'debian'
|
||||||
return Err(format!("Failed to extract {}: {}", file.name, e).into());
|
if file.name.contains("debian.tar.") {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove archive after extraction
|
// List root directories extracted and use the first one as the source directory
|
||||||
std::fs::remove_file(&path)?;
|
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
|
// Use the extracted directory as the source, assuming there is only one
|
||||||
if !info.is_native()
|
if let Some(src_dir) = source_dir {
|
||||||
&& 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
|
|
||||||
let target_dir = package_dir.join(&info.stanza.package);
|
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() {
|
if target_dir.exists() {
|
||||||
// Target exists, we need to merge contents
|
// 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_entry = sub_entry?;
|
||||||
let sub_path = sub_entry.path();
|
let sub_path = sub_entry.path();
|
||||||
let target_path = target_dir.join(sub_entry.file_name());
|
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::copy(&sub_path, &target_path)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::fs::remove_dir_all(&entry_path)?;
|
std::fs::remove_dir_all(&src_dir)?;
|
||||||
} else {
|
} else {
|
||||||
std::fs::rename(&entry_path, &target_dir)?;
|
std::fs::rename(&src_dir, &target_dir)?;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user