feat: with schema
Some checks failed
continuous-integration/drone/push Build is failing

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2023-07-02 00:47:14 +02:00
parent 6a82e0c10a
commit 12bd424f18
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
8 changed files with 164 additions and 23 deletions

29
Cargo.lock generated
View File

@ -1280,6 +1280,15 @@ dependencies = [
"value-bag", "value-bag",
] ]
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.5.0" version = "2.5.0"
@ -1675,9 +1684,24 @@ checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
dependencies = [ dependencies = [
"aho-corasick 1.0.2", "aho-corasick 1.0.2",
"memchr", "memchr",
"regex-syntax", "regex-syntax 0.7.2",
] ]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.7.2" version = "0.7.2"
@ -2215,7 +2239,10 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
dependencies = [ dependencies = [
"matchers",
"nu-ansi-term", "nu-ansi-term",
"once_cell",
"regex",
"serde", "serde",
"serde_json", "serde_json",
"sharded-slab", "sharded-slab",

View File

@ -24,7 +24,7 @@ clap = { version = "4.3.4", features = ["env", "string"] }
envconfig = "0.10.0" envconfig = "0.10.0"
dirs = "5.0.1" dirs = "5.0.1"
tracing = "0.1.36" tracing = "0.1.36"
tracing-subscriber = { version = "0.3.15", features = ["json"] } tracing-subscriber = { version = "0.3.15", features = ["json", "env-filter"] }
log = { version = "0.4.17", features = ["std", "kv_unstable"] } log = { version = "0.4.17", features = ["std", "kv_unstable"] }
tera = "1.17.0" tera = "1.17.0"
openssl = { version = "0.10.54", features = ["vendored"] } openssl = { version = "0.10.54", features = ["vendored"] }

View File

@ -1,5 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use clap::ArgMatches;
use crate::{ use crate::{
actions::shell::ShellAction, actions::shell::ShellAction,
model::{CuddleScript, CuddleShellScriptArg, CuddleVariable}, model::{CuddleScript, CuddleShellScriptArg, CuddleVariable},
@ -12,15 +14,30 @@ pub mod shell;
pub struct CuddleAction { pub struct CuddleAction {
pub script: CuddleScript, pub script: CuddleScript,
pub path: PathBuf, pub path: PathBuf,
pub description: Option<String>,
pub name: String, pub name: String,
} }
#[allow(dead_code)] #[allow(dead_code)]
impl CuddleAction { impl CuddleAction {
pub fn new(script: CuddleScript, path: PathBuf, name: String) -> Self { pub fn new(
Self { script, path, name } script: CuddleScript,
path: PathBuf,
name: String,
description: Option<String>,
) -> Self {
Self {
script,
path,
name,
description,
}
} }
pub fn execute(self, variables: Vec<CuddleVariable>) -> anyhow::Result<()> { pub fn execute(
self,
matches: &ArgMatches,
variables: Vec<CuddleVariable>,
) -> anyhow::Result<()> {
match self.script { match self.script {
CuddleScript::Shell(s) => { CuddleScript::Shell(s) => {
let mut arg_variables: Vec<CuddleVariable> = vec![]; let mut arg_variables: Vec<CuddleVariable> = vec![];
@ -28,15 +45,18 @@ impl CuddleAction {
for (k, v) in args { for (k, v) in args {
let var = match v { let var = match v {
CuddleShellScriptArg::Env(e) => { CuddleShellScriptArg::Env(e) => {
let env_var = match std::env::var(e.key.clone()) { let env_var = matches.get_one::<String>(&e.key).cloned().unwrap();
Ok(var) => var,
Err(e) => {
log::error!("env_variable not found: {}", k);
return Err(anyhow::anyhow!(e));
}
};
CuddleVariable::new(k.clone(), env_var) CuddleVariable::new(k.clone(), env_var)
} }
CuddleShellScriptArg::Flag(flag) => {
match matches.get_one::<String>(&flag.name) {
Some(flag_var) => {
CuddleVariable::new(k.clone(), flag_var.clone())
}
None => continue,
}
}
}; };
arg_variables.push(var); arg_variables.push(var);

View File

@ -43,6 +43,7 @@ impl CuddleCli {
match context { match context {
Some(_) => { Some(_) => {
tracing::debug!("build full cli");
cli = cli cli = cli
.process_variables() .process_variables()
.process_scripts() .process_scripts()
@ -50,6 +51,7 @@ impl CuddleCli {
.build_cli(); .build_cli();
} }
None => { None => {
tracing::debug!("build bare cli");
cli = cli.build_bare_cli(); cli = cli.build_bare_cli();
} }
} }
@ -98,8 +100,17 @@ impl CuddleCli {
for ctx in context_iter.iter() { for ctx in context_iter.iter() {
if let Some(scripts) = ctx.plan.scripts.clone() { if let Some(scripts) = ctx.plan.scripts.clone() {
for (name, script) in scripts { for (name, script) in scripts {
self.scripts match &script {
.push(CuddleAction::new(script.clone(), ctx.path.clone(), name)) CuddleScript::Shell(shell_script) => {
self.scripts.push(CuddleAction::new(
script.clone(),
ctx.path.clone(),
name,
shell_script.description.clone(),
))
}
CuddleScript::Dagger(_) => todo!(),
}
} }
} }
} }

View File

@ -1,4 +1,4 @@
use clap::{ArgMatches, Command}; use clap::{Arg, ArgMatches, Command};
use crate::cli::CuddleCli; use crate::cli::CuddleCli;
@ -9,11 +9,7 @@ pub fn build_command(root_cmd: Command, cli: CuddleCli) -> Command {
.about(execute_cmd_about) .about(execute_cmd_about)
.subcommand_required(true); .subcommand_required(true);
for script in cli.scripts.iter() { execute_cmd = execute_cmd.subcommands(&build_scripts(cli));
let action_cmd = Command::new(&script.name).about(&script.name);
execute_cmd = execute_cmd.subcommand(action_cmd);
}
root_cmd.subcommand(execute_cmd) root_cmd.subcommand(execute_cmd)
} else { } else {
@ -21,13 +17,62 @@ pub fn build_command(root_cmd: Command, cli: CuddleCli) -> Command {
} }
} }
pub fn build_scripts(cli: CuddleCli) -> Vec<Command> {
let mut cmds = Vec::new();
for script in cli.scripts.iter() {
let mut cmd = Command::new(&script.name);
if let Some(desc) = &script.description {
cmd = cmd.about(desc)
}
match &script.script {
crate::model::CuddleScript::Shell(shell_script) => {
if let Some(args) = &shell_script.args {
for (arg_name, arg) in args {
cmd = match arg {
crate::model::CuddleShellScriptArg::Env(arg_env) => cmd.arg(
Arg::new(arg_name.clone())
.env(arg_name.to_uppercase().replace(".", "_"))
.required(true),
),
crate::model::CuddleShellScriptArg::Flag(arg_flag) => {
let mut arg_val = Arg::new(arg_name.clone())
.env(arg_name.to_uppercase().replace(".", "_"))
.long(arg_name);
if let Some(true) = arg_flag.required {
arg_val = arg_val.required(true);
}
if let Some(def) = &arg_flag.default_value {
arg_val = arg_val.default_value(def);
}
cmd.arg(arg_val)
}
};
}
}
}
crate::model::CuddleScript::Dagger(_) => todo!(),
}
cmds.push(cmd)
}
cmds
}
pub fn execute_x(exe_submatch: &ArgMatches, cli: CuddleCli) -> anyhow::Result<()> { pub fn execute_x(exe_submatch: &ArgMatches, cli: CuddleCli) -> anyhow::Result<()> {
match exe_submatch.subcommand() { match exe_submatch.subcommand() {
Some((name, _action_matches)) => { Some((name, action_matches)) => {
log::trace!(action=name; "running action; name={}", name); log::trace!(action=name; "running action; name={}", name);
match cli.scripts.iter().find(|ele| ele.name == name) { match cli.scripts.iter().find(|ele| ele.name == name) {
Some(script) => { Some(script) => {
script.clone().execute(cli.variables.clone())?; script
.clone()
.execute(action_matches, cli.variables.clone())?;
Ok(()) Ok(())
} }
_ => Err(anyhow::anyhow!("could not find a match")), _ => Err(anyhow::anyhow!("could not find a match")),

View File

@ -1,5 +1,7 @@
use config::CuddleConfig; use config::CuddleConfig;
use tracing::Level; use tracing::Level;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
mod actions; mod actions;
mod cli; mod cli;
@ -20,7 +22,10 @@ fn main() -> anyhow::Result<()> {
} }
fn init_logging() -> anyhow::Result<()> { fn init_logging() -> anyhow::Result<()> {
tracing_subscriber::fmt().with_max_level(Level::INFO).init(); tracing_subscriber::registry()
.with(fmt::layer())
.with(EnvFilter::from_default_env())
.init();
Ok(()) Ok(())
} }

View File

@ -14,11 +14,21 @@ pub struct CuddleShellScriptArgEnv {
pub key: String, pub key: String,
} }
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct CuddleShellScriptArgFlag {
pub name: String,
pub description: Option<String>,
pub required: Option<bool>,
pub default_value: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)] #[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum CuddleShellScriptArg { pub enum CuddleShellScriptArg {
#[serde(alias = "env")] #[serde(alias = "env")]
Env(CuddleShellScriptArgEnv), Env(CuddleShellScriptArgEnv),
#[serde(alias = "flag")]
Flag(CuddleShellScriptArgFlag),
} }
#[derive(Debug, Clone, PartialEq, Deserialize)] #[derive(Debug, Clone, PartialEq, Deserialize)]

View File

@ -70,6 +70,29 @@
"patternProperties": { "patternProperties": {
"^.*$": { "^.*$": {
"anyOf": [ "anyOf": [
{
"type": "object",
"required": [
"type",
"name"
],
"properties": {
"type": {
"enum": [
"flag"
]
},
"name": {
"type": "string"
},
"required": {
"type": "boolean"
},
"default": {
"type": "string"
}
}
},
{ {
"type": "object", "type": "object",
"required": [ "required": [