use super::types::{AssetInfo, BinaryInfo, PackageConfig, PackageManifest};
use super::utils;
use anyhow::{Context, Result};
use flate2::write::GzEncoder;
use std::fs;
use std::path::{Path, PathBuf};
pub(super) fn create_package(
project_root: &Path,
project_name: &str,
project_version: &str,
config: &PackageConfig,
manifest: &PackageManifest,
binaries: &[BinaryInfo],
assets: &[AssetInfo],
) -> Result<PathBuf> {
fs::create_dir_all(&config.output_dir)?;
let staging_dir = config.output_dir.join("staging");
let _ = fs::remove_dir_all(&staging_dir);
fs::create_dir_all(&staging_dir)?;
let bin_dir = staging_dir.join("bin");
fs::create_dir_all(&bin_dir)?;
let target_dir = project_root
.join("target")
.join(config.target_arch.as_str())
.join(&config.build_profile);
for binary in binaries {
let source_name = binary.name.trim_end_matches(".exe");
let source_path = target_dir.join(source_name);
let dest_path = bin_dir.join(&binary.name);
fs::copy(&source_path, &dest_path).with_context(|| format!("Failed to copy binary: {}", binary.name))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&dest_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&dest_path, perms)?;
}
}
let config_dir = staging_dir.join("config");
fs::create_dir_all(&config_dir)?;
for config_root in [project_root.join("config"), project_root.join("configs")] {
if config_root.exists() {
utils::copy_dir_recursive(&config_root, &config_dir)?;
}
}
if !assets.is_empty() {
let asset_dir = staging_dir.join("assets");
fs::create_dir_all(&asset_dir)?;
for asset_root in [
project_root.join("assets"),
project_root.join("models"),
project_root.join("data"),
project_root.join("calibration"),
] {
if asset_root.exists() {
utils::copy_dir_recursive(&asset_root, &asset_dir)?;
}
}
}
let manifest_path = staging_dir.join("manifest.json");
fs::write(&manifest_path, serde_json::to_string_pretty(&manifest)?)?;
let package_filename = format!(
"{}-{}-{}.tar.gz",
project_name,
project_version,
config.target_arch.as_str()
);
let package_path = config.output_dir.join(&package_filename);
create_tarball(&staging_dir, &package_path)?;
let _ = fs::remove_dir_all(&staging_dir);
Ok(package_path)
}
pub(super) fn create_tarball(source_dir: &Path, output_path: &Path) -> Result<()> {
let tar_gz = fs::File::create(output_path)?;
let enc = GzEncoder::new(tar_gz, flate2::Compression::default());
let mut tar = tar::Builder::new(enc);
tar.append_dir_all(".", source_dir)?;
tar.finish()?;
Ok(())
}