use std::path::PathBuf; use color_eyre::eyre::eyre; use rpc_toolkit::command; use tracing::instrument; use crate::context::SdkContext; use crate::s9pk::builder::S9pkPacker; use crate::s9pk::manifest::Manifest; use crate::s9pk::reader::S9pkReader; use crate::util::display_none; use crate::volume::Volume; use crate::{Error, ResultExt}; pub mod builder; pub mod header; pub mod manifest; pub mod reader; pub const SIG_CONTEXT: &'static [u8] = b"s9pk"; #[command(cli_only, display(display_none), blocking)] #[instrument(skip(ctx))] pub fn pack(#[context] ctx: SdkContext, #[arg] path: Option) -> Result<(), Error> { use std::fs::File; use std::io::Read; let path = if let Some(path) = path { path } else { std::env::current_dir()? }; let manifest: Manifest = if path.join("manifest.toml").exists() { let mut s = String::new(); File::open(path.join("manifest.toml"))?.read_to_string(&mut s)?; serde_toml::from_str(&s).with_kind(crate::ErrorKind::Deserialization)? } else if path.join("manifest.yaml").exists() { serde_yaml::from_reader(File::open(path.join("manifest.yaml"))?) .with_kind(crate::ErrorKind::Deserialization)? } else if path.join("manifest.json").exists() { serde_json::from_reader(File::open(path.join("manifest.json"))?) .with_kind(crate::ErrorKind::Deserialization)? } else { return Err(Error::new( eyre!("manifest not found"), crate::ErrorKind::Pack, )); }; let outfile_path = path.join(format!("{}.s9pk", manifest.id)); let mut outfile = File::create(outfile_path)?; S9pkPacker::builder() .manifest(&manifest) .writer(&mut outfile) .license( File::open(path.join(manifest.assets.license_path())).with_ctx(|_| { ( crate::ErrorKind::Filesystem, manifest.assets.license_path().display().to_string(), ) })?, ) .icon( File::open(path.join(manifest.assets.icon_path())).with_ctx(|_| { ( crate::ErrorKind::Filesystem, manifest.assets.icon_path().display().to_string(), ) })?, ) .instructions( File::open(path.join(manifest.assets.instructions_path())).with_ctx(|_| { ( crate::ErrorKind::Filesystem, manifest.assets.instructions_path().display().to_string(), ) })?, ) .docker_images( File::open(path.join(manifest.assets.docker_images_path())).with_ctx(|_| { ( crate::ErrorKind::Filesystem, manifest.assets.docker_images_path().display().to_string(), ) })?, ) .assets({ let mut assets = tar::Builder::new(Vec::new()); // TODO: Ideally stream this? best not to buffer in memory for (asset_volume, _) in manifest .volumes .iter() .filter(|(_, v)| matches!(v, &&Volume::Assets {})) { assets.append_dir_all( asset_volume, path.join(manifest.assets.assets_path()).join(asset_volume), )?; } std::io::Cursor::new(assets.into_inner()?) }) .build() .pack(&ctx.developer_key()?)?; outfile.sync_all()?; Ok(()) } #[command(cli_only, display(display_none))] pub async fn verify(#[arg] path: PathBuf) -> Result<(), Error> { let mut s9pk = S9pkReader::open(path).await?; s9pk.validate().await?; Ok(()) }