without autoescape
This commit is contained in:
@@ -18,3 +18,4 @@ tracing = "0.1.36"
|
||||
tracing-subscriber = { version = "0.3.15", features = ["json"] }
|
||||
log = { version = "0.4.17", features = ["std", "kv_unstable"] }
|
||||
openssl = {version = "0.10", features = ["vendored"]}
|
||||
tera = "1.17.0"
|
||||
|
@@ -1 +1,78 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{
|
||||
actions::shell::ShellAction,
|
||||
model::{CuddleScript, CuddleShellScriptArg, CuddleVariable},
|
||||
};
|
||||
|
||||
pub mod shell;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct CuddleAction {
|
||||
pub script: CuddleScript,
|
||||
pub path: PathBuf,
|
||||
pub name: String,
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
impl CuddleAction {
|
||||
pub fn new(script: CuddleScript, path: PathBuf, name: String) -> Self {
|
||||
Self { script, path, name }
|
||||
}
|
||||
|
||||
pub fn execute(self, variables: Vec<CuddleVariable>) -> anyhow::Result<()> {
|
||||
match self.script {
|
||||
CuddleScript::Shell(s) => {
|
||||
let mut arg_variables: Vec<CuddleVariable> = vec![];
|
||||
if let Some(args) = s.args {
|
||||
for (k, v) in args {
|
||||
let var = match v {
|
||||
CuddleShellScriptArg::Env(e) => {
|
||||
let env_var = match std::env::var(e.key.clone()) {
|
||||
Ok(var) => var,
|
||||
Err(e) => {
|
||||
log::error!("env_variable not found: {}", k);
|
||||
return Err(anyhow::anyhow!(e));
|
||||
}
|
||||
};
|
||||
CuddleVariable::new(k.clone(), env_var)
|
||||
}
|
||||
};
|
||||
|
||||
arg_variables.push(var);
|
||||
}
|
||||
} else {
|
||||
arg_variables = vec![]
|
||||
};
|
||||
|
||||
let mut vars = variables.clone();
|
||||
vars.append(&mut arg_variables);
|
||||
|
||||
log::trace!("preparing to run action");
|
||||
|
||||
match ShellAction::new(
|
||||
self.name.clone(),
|
||||
format!(
|
||||
"{}/scripts/{}.sh",
|
||||
self.path
|
||||
.to_str()
|
||||
.expect("action doesn't have a name, this should never happen"),
|
||||
self.name
|
||||
),
|
||||
)
|
||||
.execute(vars)
|
||||
{
|
||||
Ok(()) => {
|
||||
log::trace!("finished running action");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
CuddleScript::Dagger(_d) => Err(anyhow::anyhow!("not implemented yet!")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use std::{env::current_dir, path::PathBuf, process::Command};
|
||||
|
||||
use crate::cli::CuddleVariable;
|
||||
use crate::model::CuddleVariable;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ShellAction {
|
||||
|
@@ -1,314 +0,0 @@
|
||||
use std::{
|
||||
env::{self, current_dir},
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use clap::Command;
|
||||
use git2::Repository;
|
||||
|
||||
use crate::{
|
||||
actions,
|
||||
context::{CuddleContext, CuddleTreeType},
|
||||
model::{CuddleScript, CuddleShellScriptArg},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
struct CuddleAction {
|
||||
script: CuddleScript,
|
||||
path: PathBuf,
|
||||
name: String,
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
impl CuddleAction {
|
||||
pub fn new(script: CuddleScript, path: PathBuf, name: String) -> Self {
|
||||
Self { script, path, name }
|
||||
}
|
||||
|
||||
pub fn execute(self, variables: Vec<CuddleVariable>) -> anyhow::Result<()> {
|
||||
match self.script {
|
||||
CuddleScript::Shell(s) => {
|
||||
let mut arg_variables: Vec<CuddleVariable> = vec![];
|
||||
if let Some(args) = s.args {
|
||||
for (k, v) in args {
|
||||
let var = match v {
|
||||
CuddleShellScriptArg::Env(e) => {
|
||||
let env_var = match env::var(e.key.clone()) {
|
||||
Ok(var) => var,
|
||||
Err(e) => {
|
||||
log::error!("env_variable not found: {}", k);
|
||||
return Err(anyhow::anyhow!(e));
|
||||
}
|
||||
};
|
||||
CuddleVariable::new(k.clone(), env_var)
|
||||
}
|
||||
};
|
||||
|
||||
arg_variables.push(var);
|
||||
}
|
||||
} else {
|
||||
arg_variables = vec![]
|
||||
};
|
||||
|
||||
let mut vars = variables.clone();
|
||||
vars.append(&mut arg_variables);
|
||||
|
||||
log::trace!("preparing to run action");
|
||||
|
||||
match actions::shell::ShellAction::new(
|
||||
self.name.clone(),
|
||||
format!(
|
||||
"{}/scripts/{}.sh",
|
||||
self.path
|
||||
.to_str()
|
||||
.expect("action doesn't have a name, this should never happen"),
|
||||
self.name
|
||||
),
|
||||
)
|
||||
.execute(vars)
|
||||
{
|
||||
Ok(()) => {
|
||||
log::trace!("finished running action");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
CuddleScript::Dagger(_d) => Err(anyhow::anyhow!("not implemented yet!")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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_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() {
|
||||
if let Some(scripts) = ctx.plan.scripts.clone() {
|
||||
for (name, script) in scripts {
|
||||
self.scripts
|
||||
.push(CuddleAction::new(script.clone(), ctx.path.clone(), name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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());
|
||||
|
||||
// TODO: Some way to add an about for clap, requires conversion from String -> &str
|
||||
|
||||
execute_cmd = execute_cmd.subcommand(action_cmd);
|
||||
}
|
||||
|
||||
root_cmd = root_cmd.subcommand(execute_cmd);
|
||||
}
|
||||
|
||||
self.command = Some(root_cmd);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn execute(self) -> anyhow::Result<Self> {
|
||||
if let Some(mut cli) = self.command.clone() {
|
||||
let matches = cli.clone().get_matches();
|
||||
|
||||
let res = match matches.subcommand() {
|
||||
Some(("x", exe_submatch)) => {
|
||||
log::trace!("executing x");
|
||||
|
||||
match exe_submatch.subcommand() {
|
||||
Some((name, _action_matches)) => {
|
||||
log::trace!(action=name; "running action; name={}", name);
|
||||
match self.scripts.iter().find(|ele| ele.name == name) {
|
||||
Some(script) => {
|
||||
script.clone().execute(self.variables.clone())?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(anyhow!("could not find a match")),
|
||||
}
|
||||
}
|
||||
_ => Err(anyhow!("could not find a match")),
|
||||
}
|
||||
}
|
||||
_ => Err(anyhow!("could not find a match")),
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
let _ = cli.print_long_help();
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
182
cuddle_cli/src/cli/mod.rs
Normal file
182
cuddle_cli/src/cli/mod.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
mod subcommands;
|
||||
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use clap::Command;
|
||||
|
||||
use crate::{
|
||||
actions::CuddleAction,
|
||||
context::{CuddleContext, CuddleTreeType},
|
||||
model::*,
|
||||
util::git::GitCommit,
|
||||
};
|
||||
|
||||
use self::subcommands::render_template::RenderTemplateCommand;
|
||||
|
||||
#[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_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() {
|
||||
if let Some(scripts) = ctx.plan.scripts.clone() {
|
||||
for (name, script) in scripts {
|
||||
self.scripts
|
||||
.push(CuddleAction::new(script.clone(), ctx.path.clone(), name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.arg_required_else_help(true)
|
||||
.propagate_version(true);
|
||||
|
||||
root_cmd = subcommands::x::build_command(root_cmd, self.clone());
|
||||
root_cmd = subcommands::render_template::build_command(root_cmd);
|
||||
|
||||
self.command = Some(root_cmd);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn execute(self) -> anyhow::Result<Self> {
|
||||
if let Some(mut cli) = self.command.clone() {
|
||||
let matches = cli.clone().get_matches();
|
||||
|
||||
let res = match matches.subcommand() {
|
||||
Some(("x", exe_submatch)) => subcommands::x::execute_x(exe_submatch, self.clone()),
|
||||
Some(("render_template", sub_matches)) => {
|
||||
RenderTemplateCommand::from_matches(sub_matches, self.clone())
|
||||
.and_then(|cmd| cmd.execute())?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(anyhow::anyhow!("could not find a match")),
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
let _ = cli.print_long_help();
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
2
cuddle_cli/src/cli/subcommands/mod.rs
Normal file
2
cuddle_cli/src/cli/subcommands/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod render_template;
|
||||
pub mod x;
|
150
cuddle_cli/src/cli/subcommands/render_template.rs
Normal file
150
cuddle_cli/src/cli/subcommands/render_template.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
|
||||
use crate::{cli::CuddleCli, model::CuddleVariable};
|
||||
|
||||
pub fn build_command<'a>(root_cmd: Command<'a>) -> Command<'a> {
|
||||
root_cmd.subcommand(
|
||||
Command::new("render_template")
|
||||
.about("renders a jinja compatible template")
|
||||
.args(&[
|
||||
Arg::new("template-file")
|
||||
.alias("template")
|
||||
.short('t')
|
||||
.long("template-file")
|
||||
.required(true)
|
||||
.action(clap::ArgAction::Set).long_help("template-file is the input file path of the .tmpl file (or inferred) that you would like to render"),
|
||||
Arg::new("destination")
|
||||
.alias("dest")
|
||||
.short('d')
|
||||
.long("destination")
|
||||
.required(true)
|
||||
.action(clap::ArgAction::Set)
|
||||
.long_help("destination is the output path of the template once done, but default .tmpl is stripped and the normal file extension is used. this can be overwritten if a file path is entered instead. I.e. (/some/file/name.txt)"),
|
||||
Arg::new("extra-var")
|
||||
.long("extra-var")
|
||||
.required(false)
|
||||
.action(clap::ArgAction::Set),
|
||||
]))
|
||||
}
|
||||
|
||||
pub struct RenderTemplateCommand {
|
||||
variables: Vec<CuddleVariable>,
|
||||
template_file: PathBuf,
|
||||
destination: PathBuf,
|
||||
}
|
||||
|
||||
impl RenderTemplateCommand {
|
||||
pub fn from_matches(matches: &ArgMatches, cli: CuddleCli) -> anyhow::Result<Self> {
|
||||
let template_file = matches
|
||||
.get_one::<String>("template-file")
|
||||
.ok_or(anyhow::anyhow!("template-file was not found"))
|
||||
.and_then(get_path_buf_and_check_exists)?;
|
||||
|
||||
let destination = matches
|
||||
.get_one::<String>("destination")
|
||||
.ok_or(anyhow::anyhow!("destination was not found"))
|
||||
.and_then(get_path_buf_and_check_dir_exists)
|
||||
.and_then(RenderTemplateCommand::transform_extension)?;
|
||||
|
||||
let mut extra_vars: Vec<CuddleVariable> =
|
||||
if let Some(extra_vars) = matches.get_many::<String>("extra-var") {
|
||||
let mut vars = Vec::with_capacity(extra_vars.len());
|
||||
for var in extra_vars.into_iter() {
|
||||
let parts: Vec<&str> = var.split("=").collect();
|
||||
if parts.len() != 2 {
|
||||
return Err(anyhow::anyhow!("extra-var: is not set correctly: {}", var));
|
||||
}
|
||||
|
||||
vars.push(CuddleVariable::new(parts[0].into(), parts[1].into()));
|
||||
}
|
||||
vars
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
extra_vars.append(&mut cli.variables.clone());
|
||||
|
||||
Ok(Self {
|
||||
variables: extra_vars,
|
||||
template_file,
|
||||
destination,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn execute(self) -> anyhow::Result<()> {
|
||||
// Prepare context
|
||||
let mut context = tera::Context::new();
|
||||
for var in self.variables {
|
||||
context.insert(
|
||||
var.name.to_lowercase().replace(" ", "_").replace("-", "_"),
|
||||
&var.value,
|
||||
)
|
||||
}
|
||||
|
||||
// Load source template
|
||||
let source = std::fs::read_to_string(self.template_file)?;
|
||||
|
||||
let output = tera::Tera::one_off(source.as_str(), &context, false)?;
|
||||
|
||||
// Put template in final destination
|
||||
std::fs::write(&self.destination, output)?;
|
||||
|
||||
log::info!(
|
||||
"finished writing template to: {}",
|
||||
&self.destination.to_string_lossy()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transform_extension(template_path: PathBuf) -> anyhow::Result<PathBuf> {
|
||||
if template_path.is_file() {
|
||||
let ext = template_path.extension().ok_or(anyhow::anyhow!(
|
||||
"destination path does not have an extension"
|
||||
))?;
|
||||
if ext.to_string_lossy().ends_with("tmpl") {
|
||||
let template_dest = template_path
|
||||
.to_str()
|
||||
.and_then(|s| s.strip_suffix(".tmpl"))
|
||||
.ok_or(anyhow::anyhow!("string does not end in .tmpl"))?;
|
||||
|
||||
return PathBuf::from_str(template_dest).map_err(|e| anyhow::anyhow!(e));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(template_path)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_path_buf_and_check_exists(raw_path: &String) -> anyhow::Result<PathBuf> {
|
||||
match PathBuf::from_str(&raw_path) {
|
||||
Ok(pb) => {
|
||||
if pb.exists() {
|
||||
Ok(pb)
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"path: {}, could not be found",
|
||||
pb.to_string_lossy()
|
||||
))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(anyhow::anyhow!(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_path_buf_and_check_dir_exists(raw_path: &String) -> anyhow::Result<PathBuf> {
|
||||
match PathBuf::from_str(&raw_path) {
|
||||
Ok(pb) => {
|
||||
if pb.is_dir() && pb.exists() {
|
||||
Ok(pb)
|
||||
} else if pb.is_file() {
|
||||
Ok(pb)
|
||||
} else {
|
||||
Ok(pb)
|
||||
}
|
||||
}
|
||||
Err(e) => Err(anyhow::anyhow!(e)),
|
||||
}
|
||||
}
|
37
cuddle_cli/src/cli/subcommands/x.rs
Normal file
37
cuddle_cli/src/cli/subcommands/x.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use clap::{ArgMatches, Command};
|
||||
|
||||
use crate::cli::CuddleCli;
|
||||
|
||||
pub fn build_command<'a>(root_cmd: Command<'a>, cli: CuddleCli<'a>) -> Command<'a> {
|
||||
if cli.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 cli.scripts.iter() {
|
||||
let action_cmd = Command::new(script.name.clone());
|
||||
|
||||
// TODO: Some way to add an about for clap, requires conversion from String -> &str
|
||||
|
||||
execute_cmd = execute_cmd.subcommand(action_cmd);
|
||||
}
|
||||
|
||||
root_cmd.subcommand(execute_cmd)
|
||||
} else {
|
||||
root_cmd
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_x(exe_submatch: &ArgMatches, cli: CuddleCli) -> anyhow::Result<()> {
|
||||
match exe_submatch.subcommand() {
|
||||
Some((name, _action_matches)) => {
|
||||
log::trace!(action=name; "running action; name={}", name);
|
||||
match cli.scripts.iter().find(|ele| ele.name == name) {
|
||||
Some(script) => {
|
||||
script.clone().execute(cli.variables.clone())?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(anyhow::anyhow!("could not find a match")),
|
||||
}
|
||||
}
|
||||
_ => Err(anyhow::anyhow!("could not find a match")),
|
||||
}
|
||||
}
|
@@ -3,6 +3,7 @@ use envconfig::Envconfig;
|
||||
pub enum CuddleFetchPolicy {
|
||||
Always,
|
||||
Once,
|
||||
Never,
|
||||
}
|
||||
|
||||
#[derive(Envconfig, Clone)]
|
||||
@@ -20,6 +21,7 @@ impl CuddleConfig {
|
||||
match self.fetch_policy.clone().to_lowercase().as_str() {
|
||||
"always" => Ok(CuddleFetchPolicy::Always),
|
||||
"once" => Ok(CuddleFetchPolicy::Once),
|
||||
"never" => Ok(CuddleFetchPolicy::Never),
|
||||
_ => Err(anyhow::anyhow!("could not parse fetch policy")),
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,3 @@
|
||||
use std::{
|
||||
env::current_dir,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use config::CuddleConfig;
|
||||
use tracing::Level;
|
||||
|
||||
@@ -11,23 +6,15 @@ mod cli;
|
||||
mod config;
|
||||
mod context;
|
||||
mod model;
|
||||
mod util;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
init_logging()?;
|
||||
|
||||
let config = CuddleConfig::from_env()?;
|
||||
|
||||
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
|
||||
log::info!("was not opened in a repo with git, only showing bare-bones options");
|
||||
_ = cli::CuddleCli::new(Arc::new(Mutex::new(vec![])))?.execute()?;
|
||||
}
|
||||
}
|
||||
let context = context::extract_cuddle(config.clone())?;
|
||||
_ = cli::CuddleCli::new(context)?.execute()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -35,7 +22,7 @@ fn main() -> anyhow::Result<()> {
|
||||
fn init_logging() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.pretty()
|
||||
.with_max_level(Level::DEBUG)
|
||||
.with_max_level(Level::INFO)
|
||||
.init();
|
||||
|
||||
Ok(())
|
||||
|
@@ -46,3 +46,16 @@ pub struct CuddlePlan {
|
||||
pub vars: Option<HashMap<String, String>>,
|
||||
pub scripts: Option<HashMap<String, CuddleScript>>,
|
||||
}
|
||||
|
||||
#[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 }
|
||||
}
|
||||
}
|
||||
|
33
cuddle_cli/src/util/git.rs
Normal file
33
cuddle_cli/src/util/git.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::env::current_dir;
|
||||
|
||||
use git2::Repository;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GitCommit {
|
||||
pub commit_sha: String,
|
||||
}
|
||||
|
||||
impl GitCommit {
|
||||
pub 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(),
|
||||
})
|
||||
}
|
||||
}
|
1
cuddle_cli/src/util/mod.rs
Normal file
1
cuddle_cli/src/util/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod git;
|
Reference in New Issue
Block a user