add bare cli

This commit is contained in:
Kasper Juul Hermansen 2022-08-11 02:03:19 +02:00
parent ba0ad9239d
commit 5da6b35ead
Signed by: kjuulh
GPG Key ID: 0F95C140730F2F23
6 changed files with 185 additions and 38 deletions

View File

@ -1,8 +1,8 @@
use std::{ use std::process::Command;
io::{BufRead, BufReader},
process::{Command, Stdio},
};
use crate::cli::CuddleVariable;
#[allow(dead_code)]
pub struct ShellAction { pub struct ShellAction {
path: String, path: String,
name: String, name: String,
@ -13,7 +13,7 @@ impl ShellAction {
Self { path, name } Self { path, name }
} }
pub fn execute(self) -> anyhow::Result<()> { pub fn execute(self, variables: Vec<CuddleVariable>) -> anyhow::Result<()> {
log::debug!("executing shell action: {}", self.path.clone()); log::debug!("executing shell action: {}", self.path.clone());
log::info!( log::info!(
@ -28,18 +28,12 @@ Starting running shell action: {}
let mut process = Command::new(self.path) let mut process = Command::new(self.path)
.current_dir(".") .current_dir(".")
//.stdout(Stdio::piped()) .envs(variables.iter().map(|v| {
//.stderr(Stdio::piped()) log::trace!("{:?}", v);
.spawn()?;
//{ (v.name.to_uppercase(), v.value.clone())
// let stdout = process.stdout.as_mut().unwrap(); }))
// let stdout_reader = BufReader::new(stdout); .spawn()?;
// let mut stdout_lines = stdout_reader.lines();
// while let Some(Ok(line)) = stdout_lines.next() {
// log::info!("{}", line);
// }
//}
process.wait()?; process.wait()?;

View File

@ -1,12 +1,18 @@
use std::{ use std::{
env::current_dir,
path::PathBuf, path::PathBuf,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use anyhow::anyhow; use anyhow::anyhow;
use clap::Command; use clap::Command;
use git2::Repository;
use crate::{actions, context::CuddleContext, model::CuddleScript}; use crate::{
actions,
context::{CuddleContext, CuddleTreeType},
model::CuddleScript,
};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(dead_code)] #[allow(dead_code)]
@ -21,7 +27,7 @@ impl CuddleAction {
Self { script, path, name } Self { script, path, name }
} }
pub fn execute(self) { pub fn execute(self, variables: Vec<CuddleVariable>) {
match self.script { match self.script {
CuddleScript::Shell(_s) => { CuddleScript::Shell(_s) => {
match actions::shell::ShellAction::new( match actions::shell::ShellAction::new(
@ -34,7 +40,7 @@ impl CuddleAction {
self.name self.name
), ),
) )
.execute() .execute(variables)
{ {
Ok(()) => {} Ok(()) => {}
Err(e) => { 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<GitCommit> {
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)] #[derive(Debug, Clone)]
pub struct CuddleCli<'a> { pub struct CuddleCli<'a> {
scripts: Vec<CuddleAction>, scripts: Vec<CuddleAction>,
variables: Vec<CuddleVariable>,
context: Arc<Mutex<Vec<CuddleContext>>>, context: Arc<Mutex<Vec<CuddleContext>>>,
command: Option<Command<'a>>, command: Option<Command<'a>>,
tmp_dir: Option<PathBuf>,
} }
impl<'a> CuddleCli<'a> { impl<'a> CuddleCli<'a> {
pub fn new(context: Arc<Mutex<Vec<CuddleContext>>>) -> anyhow::Result<CuddleCli<'a>> { pub fn new(context: Arc<Mutex<Vec<CuddleContext>>>) -> anyhow::Result<CuddleCli<'a>> {
let mut cli = CuddleCli { let mut cli = CuddleCli {
scripts: vec![], scripts: vec![],
variables: vec![],
context: context.clone(), context: context.clone(),
command: None, command: None,
tmp_dir: None,
}; };
cli = cli.process_scripts().build_cli(); cli = cli
.process_variables()
.process_scripts()
.process_templates()?
.build_cli();
Ok(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 { fn process_scripts(mut self) -> Self {
if let Ok(context_iter) = self.context.clone().lock() { if let Ok(context_iter) = self.context.clone().lock() {
for ctx in context_iter.iter() { for ctx in context_iter.iter() {
@ -82,25 +174,70 @@ impl<'a> CuddleCli<'a> {
self self
} }
fn process_templates(self) -> anyhow::Result<Self> {
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 { fn build_cli(mut self) -> Self {
let mut root_cmd = Command::new("cuddle") let mut root_cmd = Command::new("cuddle")
.version("1.0") .version("1.0")
.author("kjuulh <contact@kasperhermansen.com>") .author("kjuulh <contact@kasperhermansen.com>")
.about("cuddle is your domain specific organization tool. It enabled widespread sharing through repositories, as well as collaborating while maintaining speed and integrity") .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); .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() { for script in self.scripts.iter() {
let action_cmd = Command::new(script.name.clone()); 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.command = Some(root_cmd);
self self
@ -119,7 +256,7 @@ impl<'a> CuddleCli<'a> {
log::trace!(action=name; "running action"); log::trace!(action=name; "running action");
match self.scripts.iter().find(|ele| ele.name == name) { match self.scripts.iter().find(|ele| ele.name == name) {
Some(script) => { Some(script) => {
script.clone().execute(); script.clone().execute(self.variables.clone());
Ok(()) Ok(())
} }
_ => (Err(anyhow!("could not find a match"))), _ => (Err(anyhow!("could not find a match"))),

View File

@ -12,10 +12,17 @@ use crate::{
model::{CuddleBase, CuddlePlan}, model::{CuddleBase, CuddlePlan},
}; };
#[derive(Debug, Clone, PartialEq)]
pub enum CuddleTreeType {
Root,
Leaf,
}
#[derive(Debug)] #[derive(Debug)]
pub struct CuddleContext { pub struct CuddleContext {
pub plan: CuddlePlan, pub plan: CuddlePlan,
pub path: PathBuf, pub path: PathBuf,
pub node_type: CuddleTreeType,
} }
pub fn extract_cuddle(config: CuddleConfig) -> anyhow::Result<Arc<Mutex<Vec<CuddleContext>>>> { pub fn extract_cuddle(config: CuddleConfig) -> anyhow::Result<Arc<Mutex<Vec<CuddleContext>>>> {
@ -41,6 +48,7 @@ pub fn extract_cuddle(config: CuddleConfig) -> anyhow::Result<Arc<Mutex<Vec<Cudd
context.lock().unwrap().push(CuddleContext { context.lock().unwrap().push(CuddleContext {
plan: cuddle_plan.clone(), plan: cuddle_plan.clone(),
path: current_dir()?, path: current_dir()?,
node_type: CuddleTreeType::Root,
}); });
// pull parent plan and execute recursive descent // pull parent plan and execute recursive descent
@ -131,6 +139,7 @@ fn recurse_parent(path: PathBuf, context: Arc<Mutex<Vec<CuddleContext>>>) -> any
ctxs.push(CuddleContext { ctxs.push(CuddleContext {
plan: cuddle_plan.clone(), plan: cuddle_plan.clone(),
path: path.clone(), path: path.clone(),
node_type: CuddleTreeType::Leaf,
}); });
} else { } else {
return Err(anyhow::anyhow!("Could not acquire lock, aborting")); return Err(anyhow::anyhow!("Could not acquire lock, aborting"));

View File

@ -1,3 +1,8 @@
use std::{
env::current_dir,
sync::{Arc, Mutex},
};
use config::CuddleConfig; use config::CuddleConfig;
mod actions; mod actions;
@ -11,8 +16,16 @@ fn main() -> anyhow::Result<()> {
let config = CuddleConfig::from_env()?; let config = CuddleConfig::from_env()?;
let context = context::extract_cuddle(config.clone())?; match git2::Repository::open(current_dir()?) {
_ = cli::CuddleCli::new(context)?.execute()?; 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(()) Ok(())
} }

View File

@ -30,5 +30,6 @@ pub enum CuddleScript {
#[derive(Debug, Clone, PartialEq, Deserialize)] #[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct CuddlePlan { pub struct CuddlePlan {
pub base: CuddleBase, pub base: CuddleBase,
pub vars: Option<HashMap<String, String>>,
pub scripts: Option<HashMap<String, CuddleScript>>, pub scripts: Option<HashMap<String, CuddleScript>>,
} }

View File

@ -59,14 +59,7 @@
"title": "your collection of variables to be available to cuddle", "title": "your collection of variables to be available to cuddle",
"patternProperties": { "patternProperties": {
"^.*$": { "^.*$": {
"anyOf": [ "type": "string"
{
"type": "string"
},
{
"type": "object"
}
]
} }
}, },
"additionalProperties": false "additionalProperties": false