use std::{ env::consts::ARCH, fs::File, path::{Path, PathBuf}, }; use anyhow::{ensure, Result}; use flate2::{write::GzEncoder, Compression}; use tar::Builder; const BIN_NAME: &str = "projectr"; const PKG_VER: &str = env!("CARGO_PKG_VERSION"); const PKG_INCLUDE: &[&str] = &[ "bin/tmux-projectr", "CONTRIBUTING.md", "README.md", "LICENSE", ]; pub fn generate_tar_gz>( root: &P, profile: &str, tag: Option<&str>, ) -> Result { let pkg_name = format!("{BIN_NAME}-{PKG_VER}-{ARCH}.tar.gz"); let target_dir = root.as_ref().join("target"); 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); std::env::set_current_dir(root)?; 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_dir: P) -> Result { let build_dir = profile_dir.as_ref().join("build"); build_dir .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: {}", profile_dir.as_ref().display() )) }