From f94590a668356704e2838d4e048f1672cfd407be Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Tue, 19 May 2026 14:39:34 -0700 Subject: [PATCH] Manually construct flake archive, update tar --- Cargo.lock | 6 ++-- Cargo.toml | 4 +-- src/flake_info.rs | 74 +++++++++++++++++++++++++++-------------------- 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0900a45..689ad91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -639,6 +639,7 @@ dependencies = [ "tracing-subscriber", "url", "uuid", + "walkdir", ] [[package]] @@ -3247,8 +3248,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.40" -source = "git+https://github.com/DeterminateSystems/tar-rs.git?branch=force-mtime#b25211be672398a005b39061ee35d28a2a3d7e8a" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", diff --git a/Cargo.toml b/Cargo.toml index 22973ea..42c7fe4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,7 @@ reqwest = { version = "0.12.3", default-features = false, features = ["rustls-tl serde = { version = "1.0.164", features = ["derive"] } serde_json = "1.0.97" gix = { version = "0.83.0", features = ["async-network-client", "serde"] } -#tar = { version = "0.4.38", features = ["xattr"] } -tar = { git = "https://github.com/DeterminateSystems/tar-rs.git", branch = "force-mtime", features = ["xattr"] } +tar = { version = "0.4.46", features = ["xattr"] } flate2 = "1.0.26" tempfile = "3.6.0" ring = "0.17.14" @@ -34,6 +33,7 @@ url = { version = "2.5.0", features = ["serde"] } http = "1.1.0" gitlab = "0.1706.0" flake-schemas = "0.3.0" +walkdir = "2.5.0" [profile.release] strip = true # Automatically strip symbols from the binary. diff --git a/src/flake_info.rs b/src/flake_info.rs index 1e6e29a..97bb88c 100644 --- a/src/flake_info.rs +++ b/src/flake_info.rs @@ -1,7 +1,4 @@ -use std::{ - io::Write, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use color_eyre::eyre::{eyre, Result, WrapErr}; use flake_schemas::{InspectOptions, InspectOutput}; @@ -204,39 +201,52 @@ impl FlakeMetadata { }; tracing::debug!("lastModified = {}", last_modified); - let mut tarball_builder = tar::Builder::new(vec![]); + let output = flate2::write::GzEncoder::new(vec![], flate2::Compression::default()); + let output = std::io::BufWriter::new(output); + let mut tarball_builder = tar::Builder::new(output); tarball_builder.follow_symlinks(false); - tarball_builder.force_mtime(last_modified); - tracing::trace!("Creating tarball"); - // `tar` works according to the current directory (yay) - // So we change dir and restory it after - // TODO: Fix this let source = &self.source_dir; // refactor to be known when we create struct with from_dir - let current_dir = std::env::current_dir().wrap_err("Could not get current directory")?; - std::env::set_current_dir( - source - .parent() - .ok_or_else(|| eyre!("Getting parent directory"))?, - )?; - let dirname = self - .source_dir - .file_name() - .ok_or_else(|| eyre!("No file name of directory"))?; - tarball_builder - .append_dir_all(dirname, dirname) - .wrap_err_with(|| eyre!("Adding `{}` to tarball", self.source_dir.display()))?; - std::env::set_current_dir(current_dir).wrap_err("Could not set current directory")?; + let parent = source + .parent() + .ok_or_else(|| eyre!("Source dir had no parent, cannot continue"))?; + + tracing::trace!("Creating compressed tarball"); + for entry in walkdir::WalkDir::new(source).sort_by_file_name() { + let entry = entry?; + let path = entry.path(); + let subpath = path.strip_prefix(parent)?; + + let metadata = path.symlink_metadata()?; + + let mut header = tar::Header::new_gnu(); + header.set_metadata_in_mode(&metadata, tar::HeaderMode::Deterministic); + header.set_mtime(last_modified); + header.set_uid(0); + header.set_gid(0); + + if metadata.is_dir() { + tarball_builder.append_data(&mut header, subpath, std::io::Cursor::new([]))?; + } else if metadata.is_file() { + let src = std::fs::File::open(path).map(std::io::BufReader::new)?; + tarball_builder.append_data(&mut header, subpath, src)?; + } else if metadata.is_symlink() { + let target = path.read_link()?; + tarball_builder.append_link(&mut header, subpath, target)?; + } else { + tracing::warn!(?path, "Ignoring unexpected special file"); + continue; + } + } let tarball = tarball_builder.into_inner().wrap_err("Creating tarball")?; - tracing::trace!("Created tarball, compressing..."); - let mut gzip_encoder = - flate2::write::GzEncoder::new(vec![], flate2::Compression::default()); - gzip_encoder - .write_all(&tarball[..]) - .wrap_err("Adding tarball to gzip")?; - let compressed_tarball = gzip_encoder.finish().wrap_err("Creating gzip")?; - tracing::trace!("Compressed tarball"); + tracing::trace!("Created tarball, finishing compression..."); + let compressed_tarball = tarball + .into_inner() + .wrap_err("Creating gzip")? + .finish() + .wrap_err("Finalizing compression")?; + tracing::trace!("Finished tarball"); let flake_tarball_hash = { let mut context = ring::digest::Context::new(&ring::digest::SHA256);