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::{
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<CuddleVariable>) -> 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()?;

View File

@ -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<CuddleVariable>) {
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<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)]
pub struct CuddleCli<'a> {
scripts: Vec<CuddleAction>,
variables: Vec<CuddleVariable>,
context: Arc<Mutex<Vec<CuddleContext>>>,
command: Option<Command<'a>>,
tmp_dir: Option<PathBuf>,
}
impl<'a> CuddleCli<'a> {
pub fn new(context: Arc<Mutex<Vec<CuddleContext>>>) -> anyhow::Result<CuddleCli<'a>> {
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<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 {
let mut root_cmd = Command::new("cuddle")
.version("1.0")
.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")
.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"))),

View File

@ -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<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 {
plan: cuddle_plan.clone(),
path: current_dir()?,
node_type: CuddleTreeType::Root,
});
// 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 {
plan: cuddle_plan.clone(),
path: path.clone(),
node_type: CuddleTreeType::Leaf,
});
} else {
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;
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(())
}

View File

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

View File

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