diff --git a/cuddle_cli/src/actions/shell.rs b/cuddle_cli/src/actions/shell.rs index 36ed98a..add8292 100644 --- a/cuddle_cli/src/actions/shell.rs +++ b/cuddle_cli/src/actions/shell.rs @@ -1,8 +1,8 @@ -use std::{ - io::{BufRead, BufReader}, - process::{Command, Stdio}, -}; +use std::process::Command; +use crate::cli::CuddleVariable; + +#[allow(dead_code)] pub struct ShellAction { path: String, name: String, @@ -13,7 +13,7 @@ impl ShellAction { Self { path, name } } - pub fn execute(self) -> anyhow::Result<()> { + pub fn execute(self, variables: Vec) -> anyhow::Result<()> { log::debug!("executing shell action: {}", self.path.clone()); log::info!( @@ -28,18 +28,12 @@ Starting running shell action: {} let mut process = Command::new(self.path) .current_dir(".") - //.stdout(Stdio::piped()) - //.stderr(Stdio::piped()) - .spawn()?; + .envs(variables.iter().map(|v| { + log::trace!("{:?}", v); - //{ - // let stdout = process.stdout.as_mut().unwrap(); - // let stdout_reader = BufReader::new(stdout); - // let mut stdout_lines = stdout_reader.lines(); - // while let Some(Ok(line)) = stdout_lines.next() { - // log::info!("{}", line); - // } - //} + (v.name.to_uppercase(), v.value.clone()) + })) + .spawn()?; process.wait()?; diff --git a/cuddle_cli/src/cli.rs b/cuddle_cli/src/cli.rs index e1ebc9a..644c7b6 100644 --- a/cuddle_cli/src/cli.rs +++ b/cuddle_cli/src/cli.rs @@ -1,12 +1,18 @@ use std::{ + env::current_dir, path::PathBuf, sync::{Arc, Mutex}, }; use anyhow::anyhow; use clap::Command; +use git2::Repository; -use crate::{actions, context::CuddleContext, model::CuddleScript}; +use crate::{ + actions, + context::{CuddleContext, CuddleTreeType}, + model::CuddleScript, +}; #[derive(Debug, Clone)] #[allow(dead_code)] @@ -21,7 +27,7 @@ impl CuddleAction { Self { script, path, name } } - pub fn execute(self) { + pub fn execute(self, variables: Vec) { match self.script { CuddleScript::Shell(_s) => { match actions::shell::ShellAction::new( @@ -34,7 +40,7 @@ impl CuddleAction { self.name ), ) - .execute() + .execute(variables) { Ok(()) => {} Err(e) => { @@ -47,26 +53,112 @@ impl CuddleAction { } } +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct CuddleVariable { + pub name: String, + pub value: String, +} + +impl CuddleVariable { + pub fn new(name: String, value: String) -> Self { + Self { name, value } + } +} + +#[derive(Debug)] +struct GitCommit { + commit_sha: String, +} + +impl GitCommit { + fn new() -> anyhow::Result { + let repo = Repository::open(current_dir().expect("having current_dir available")).map_err( + |e| { + log::debug!("{}", e); + anyhow::anyhow!("could not open repository") + }, + )?; + let head_ref = repo + .head() + .map_err(|e| { + log::warn!("{}", e); + anyhow::anyhow!("could not get HEAD") + })? + .target() + .ok_or(anyhow::anyhow!( + "could not extract head -> target to commit_sha" + ))?; + + Ok(Self { + commit_sha: head_ref.to_string(), + }) + } +} + #[derive(Debug, Clone)] pub struct CuddleCli<'a> { scripts: Vec, + variables: Vec, context: Arc>>, command: Option>, + tmp_dir: Option, } impl<'a> CuddleCli<'a> { pub fn new(context: Arc>>) -> anyhow::Result> { let mut cli = CuddleCli { scripts: vec![], + variables: vec![], context: context.clone(), command: None, + tmp_dir: None, }; - cli = cli.process_scripts().build_cli(); + cli = cli + .process_variables() + .process_scripts() + .process_templates()? + .build_cli(); Ok(cli) } + fn process_variables(mut self) -> Self { + if let Ok(context_iter) = self.context.clone().lock() { + for ctx in context_iter.iter() { + if let Some(variables) = ctx.plan.vars.clone() { + for (name, var) in variables { + self.variables.push(CuddleVariable::new(name, var)) + } + } + if let CuddleTreeType::Root = ctx.node_type { + let mut temp_path = ctx.path.clone(); + temp_path.push(".cuddle/tmp/"); + + self.variables.push(CuddleVariable::new( + "tmp".into(), + temp_path.clone().to_string_lossy().to_string(), + )); + + self.tmp_dir = Some(temp_path); + } + } + } + + match GitCommit::new() { + Ok(commit) => self.variables.push(CuddleVariable::new( + "commit_sha".into(), + commit.commit_sha.clone(), + )), + Err(e) => { + log::debug!("{}", e); + } + } + + self + } + fn process_scripts(mut self) -> Self { if let Ok(context_iter) = self.context.clone().lock() { for ctx in context_iter.iter() { @@ -82,25 +174,70 @@ impl<'a> CuddleCli<'a> { self } + fn process_templates(self) -> anyhow::Result { + if let None = self.tmp_dir { + log::debug!("cannot process template as bare bones cli"); + return Ok(self); + } + + // Make sure tmp_dir exists and clean it up first + let tmp_dir = self + .tmp_dir + .clone() + .ok_or(anyhow::anyhow!("tmp_dir does not exist aborting"))?; + if tmp_dir.exists() && tmp_dir.ends_with("tmp") { + std::fs::remove_dir_all(tmp_dir.clone())?; + } + std::fs::create_dir_all(tmp_dir.clone())?; + // Handle all templating with variables and such. + // TODO: use actual templating engine, for new we just copy templates to the final folder + + if let Ok(context_iter) = self.context.clone().lock() { + for ctx in context_iter.iter() { + let mut template_path = ctx.path.clone(); + template_path.push("templates"); + + log::trace!("template path: {}", template_path.clone().to_string_lossy()); + + if !template_path.exists() { + continue; + } + + for file in std::fs::read_dir(template_path)?.into_iter() { + let f = file?; + let mut dest_file = tmp_dir.clone(); + dest_file.push(f.file_name()); + + std::fs::copy(f.path(), dest_file)?; + } + } + } + + Ok(self) + } + fn build_cli(mut self) -> Self { let mut root_cmd = Command::new("cuddle") .version("1.0") .author("kjuulh ") .about("cuddle is your domain specific organization tool. It enabled widespread sharing through repositories, as well as collaborating while maintaining speed and integrity") + .subcommand_required(true) .propagate_version(true); - let mut execute_cmd = Command::new("x").about("x is your entry into your domains scripts, scripts inherited from parents will also be present here"); + if self.scripts.len() > 0 { + let mut execute_cmd = Command::new("x").about("x is your entry into your domains scripts, scripts inherited from parents will also be present here").subcommand_required(true); - for script in self.scripts.iter() { - let action_cmd = Command::new(script.name.clone()); + for script in self.scripts.iter() { + let action_cmd = Command::new(script.name.clone()); - // TODO: Some way to add an about for clap, requires conversion from String -> &str + // TODO: Some way to add an about for clap, requires conversion from String -> &str - execute_cmd = execute_cmd.subcommand(action_cmd); + execute_cmd = execute_cmd.subcommand(action_cmd); + } + + root_cmd = root_cmd.subcommand(execute_cmd); } - root_cmd = root_cmd.subcommand(execute_cmd); - self.command = Some(root_cmd); self @@ -119,7 +256,7 @@ impl<'a> CuddleCli<'a> { log::trace!(action=name; "running action"); match self.scripts.iter().find(|ele| ele.name == name) { Some(script) => { - script.clone().execute(); + script.clone().execute(self.variables.clone()); Ok(()) } _ => (Err(anyhow!("could not find a match"))), diff --git a/cuddle_cli/src/context.rs b/cuddle_cli/src/context.rs index 27015eb..11be2b3 100644 --- a/cuddle_cli/src/context.rs +++ b/cuddle_cli/src/context.rs @@ -12,10 +12,17 @@ use crate::{ model::{CuddleBase, CuddlePlan}, }; +#[derive(Debug, Clone, PartialEq)] +pub enum CuddleTreeType { + Root, + Leaf, +} + #[derive(Debug)] pub struct CuddleContext { pub plan: CuddlePlan, pub path: PathBuf, + pub node_type: CuddleTreeType, } pub fn extract_cuddle(config: CuddleConfig) -> anyhow::Result>>> { @@ -41,6 +48,7 @@ pub fn extract_cuddle(config: CuddleConfig) -> anyhow::Result>>) -> any ctxs.push(CuddleContext { plan: cuddle_plan.clone(), path: path.clone(), + node_type: CuddleTreeType::Leaf, }); } else { return Err(anyhow::anyhow!("Could not acquire lock, aborting")); diff --git a/cuddle_cli/src/main.rs b/cuddle_cli/src/main.rs index e59074f..6f0d9ef 100644 --- a/cuddle_cli/src/main.rs +++ b/cuddle_cli/src/main.rs @@ -1,3 +1,8 @@ +use std::{ + env::current_dir, + sync::{Arc, Mutex}, +}; + use config::CuddleConfig; mod actions; @@ -11,8 +16,16 @@ fn main() -> anyhow::Result<()> { let config = CuddleConfig::from_env()?; - let context = context::extract_cuddle(config.clone())?; - _ = cli::CuddleCli::new(context)?.execute()?; + match git2::Repository::open(current_dir()?) { + Ok(_) => { + let context = context::extract_cuddle(config.clone())?; + _ = cli::CuddleCli::new(context)?.execute()?; + } + Err(_) => { + // Only build bare bones cli + _ = cli::CuddleCli::new(Arc::new(Mutex::new(vec![])))?.execute()?; + } + } Ok(()) } diff --git a/cuddle_cli/src/model.rs b/cuddle_cli/src/model.rs index 020d8a4..49c7fa0 100644 --- a/cuddle_cli/src/model.rs +++ b/cuddle_cli/src/model.rs @@ -30,5 +30,6 @@ pub enum CuddleScript { #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct CuddlePlan { pub base: CuddleBase, + pub vars: Option>, pub scripts: Option>, } diff --git a/schemas/base.json b/schemas/base.json index 7f84ceb..b865d2f 100644 --- a/schemas/base.json +++ b/schemas/base.json @@ -59,14 +59,7 @@ "title": "your collection of variables to be available to cuddle", "patternProperties": { "^.*$": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object" - } - ] + "type": "string" } }, "additionalProperties": false