use std::{ env::consts::ARCH, fs::File, path::{Path, PathBuf}, }; use anyhow::{ensure, Result}; use flate2::{write::GzEncoder, Compression}; use tar::Builder; use crate::{BIN_NAME, PKG_INCLUDE, PKG_VER}; pub fn generate_tar_gz(profile: &str, tag: Option<&str>) -> Result { let pkg_name = format!("{BIN_NAME}-{PKG_VER}-{ARCH}.tar.gz"); let target_dir = PathBuf::from("target").canonicalize()?; let profile_dir = target_dir.join(profile); let dist_dir = target_dir.join("dist"); let out_dir = find_out_dir(&profile_dir)?; let bin_path = profile_dir.join(BIN_NAME); let pkg_path = dist_dir.join(pkg_name); ensure!( !tag.is_some_and(|t| t.trim_start_matches('v') != PKG_VER), "Package version does not match provided tag: {PKG_VER} != {}", tag.unwrap().trim_start_matches('v') ); ensure!( bin_path.exists(), "Package binary does not exist: {}", bin_path.display() ); ensure!( out_dir.exists(), "Build's out directory does not exist: {}", out_dir.display() ); let _ = std::fs::remove_dir_all(&dist_dir); std::fs::create_dir_all(&dist_dir)?; let tar_gz = File::create(&pkg_path)?; let enc = GzEncoder::new(tar_gz, Compression::default()); let mut tar = Builder::new(enc); tar.append_path_with_name(bin_path, PathBuf::from("bin").join(BIN_NAME))?; tar.append_dir_all(".", out_dir)?; PKG_INCLUDE.iter().try_for_each(|p| tar.append_path(p))?; tar.into_inner()?.finish()?; Ok(pkg_path) } pub fn find_out_dir>(profile: P) -> Result { let build_dir = PathBuf::from("target").join(profile).join("build"); build_dir .canonicalize() .and_then(|p| p.read_dir()) .map_err(|_| { anyhow::anyhow!( "Package build directory does not exist: {}", build_dir.display() ) })? .flatten() .filter_map(|d| { d.file_name() .to_str()? .starts_with(BIN_NAME) .then(|| d.path().join("invoked.timestamp")) .filter(|p| p.exists()) }) .reduce(|acc, path_buf| { std::cmp::max_by_key(path_buf, acc, |p| { p.metadata() .and_then(|m| m.modified()) .unwrap_or(std::time::SystemTime::UNIX_EPOCH) }) }) .map(|p| p.with_file_name("out")) .ok_or(anyhow::anyhow!( "Package out directory not found in build directory: {}", build_dir.display() )) }