feat: enable actual actions
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
2d7a053ab0
commit
7804eaa667
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -7,7 +7,7 @@ name = "action"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cuddle-actions 0.1.0",
|
"cuddle-actions 0.2.0 (git+ssh://git@git.front.kjuulh.io/kjuulh/cuddle-v2)",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -268,11 +268,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cuddle-actions"
|
name = "cuddle-actions"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "git+ssh://git@git.front.kjuulh.io/kjuulh/cuddle-v2#fb2e4b3234a249d17aaf9bdff18825a36a132bbd"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
"pretty_assertions",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
@ -280,10 +280,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "cuddle-actions"
|
name = "cuddle-actions"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
source = "git+ssh://git@git.front.kjuulh.io/kjuulh/cuddle-v2#71cc6a0a000dbdb1b62b518cd2d955e9121627f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"pretty_assertions",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
@ -12,7 +12,7 @@ anyhow = { version = "1" }
|
|||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tracing = { version = "0.1", features = ["log"] }
|
tracing = { version = "0.1", features = ["log"] }
|
||||||
tracing-subscriber = { version = "0.3.18" }
|
tracing-subscriber = { version = "0.3.18" }
|
||||||
clap = { version = "4", features = ["derive", "env"] }
|
clap = { version = "4", features = ["derive", "env", "string"] }
|
||||||
dotenv = { version = "0.15" }
|
dotenv = { version = "0.15" }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.127"
|
serde_json = "1.0.127"
|
||||||
|
@ -3,7 +3,14 @@ use cuddle_actions::AddActionOptions;
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
cuddle_actions::CuddleActions::default()
|
cuddle_actions::CuddleActions::default()
|
||||||
.add_action("something", || Ok(()), &AddActionOptions::default())
|
.add_action(
|
||||||
|
"something",
|
||||||
|
|| {
|
||||||
|
println!("did something");
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
&AddActionOptions::default(),
|
||||||
|
)
|
||||||
.execute()?;
|
.execute()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
|
future::{self, Future},
|
||||||
|
ops::Deref,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rust_builder::RustActionsBuilder;
|
use rust_builder::RustActionsBuilder;
|
||||||
@ -16,6 +20,10 @@ pub mod rust_builder {
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::actions::CuddleActionsSchema;
|
||||||
|
|
||||||
use super::ExecutableActions;
|
use super::ExecutableActions;
|
||||||
|
|
||||||
pub struct RustActionsBuilder {
|
pub struct RustActionsBuilder {
|
||||||
@ -138,7 +146,7 @@ pub mod rust_builder {
|
|||||||
.output()
|
.output()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let actions: ExecutableActions = match serde_json::from_slice(&output.stdout) {
|
let actions: CuddleActionsSchema = match serde_json::from_slice(&output.stdout) {
|
||||||
Ok(output) => output,
|
Ok(output) => output,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let schema_output = std::str::from_utf8(&output.stdout)?;
|
let schema_output = std::str::from_utf8(&output.stdout)?;
|
||||||
@ -151,7 +159,7 @@ pub mod rust_builder {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(actions)
|
Ok(actions.to_executable(action_path)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,7 +188,7 @@ impl Actions {
|
|||||||
Ok(Some(Self { variants }))
|
Ok(Some(Self { variants }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build(&mut self) -> anyhow::Result<Vec<ExecutableActions>> {
|
pub async fn build(&mut self) -> anyhow::Result<ExecutableActions> {
|
||||||
let mut executable_actions = Vec::default();
|
let mut executable_actions = Vec::default();
|
||||||
|
|
||||||
for variant in &mut self.variants {
|
for variant in &mut self.variants {
|
||||||
@ -194,25 +202,49 @@ impl Actions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(executable_actions)
|
let mut exec_actions = ExecutableActions::default();
|
||||||
|
for mut actions in executable_actions {
|
||||||
|
exec_actions.actions.append(&mut actions.actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(exec_actions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct CuddleActionsSchema {
|
||||||
|
actions: Vec<CuddleActionSchema>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct CuddleActionSchema {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum ActionVariant {
|
pub enum ActionVariant {
|
||||||
Rust { root_path: PathBuf },
|
Rust { root_path: PathBuf },
|
||||||
Docker,
|
Docker,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize)]
|
#[derive(Default)]
|
||||||
pub struct ExecutableActions {
|
pub struct ExecutableActions {
|
||||||
actions: Vec<ExecutableAction>,
|
pub actions: Vec<ExecutableAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct ExecutableAction {
|
pub struct ExecutableAction {
|
||||||
name: String,
|
pub name: String,
|
||||||
description: String,
|
pub description: String,
|
||||||
flags: BTreeMap<String, ExecutableActionFlag>,
|
pub flags: BTreeMap<String, ExecutableActionFlag>,
|
||||||
|
call_fn: LazyResolve,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecutableAction {
|
||||||
|
pub async fn call(&self) -> anyhow::Result<()> {
|
||||||
|
// Bad hack until .call becomes stable
|
||||||
|
(self.call_fn.0)().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@ -220,3 +252,67 @@ pub enum ExecutableActionFlag {
|
|||||||
String,
|
String,
|
||||||
Bool,
|
Bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CuddleActionsSchema {
|
||||||
|
fn to_executable(self, action_path: &Path) -> anyhow::Result<ExecutableActions> {
|
||||||
|
Ok(ExecutableActions {
|
||||||
|
actions: self
|
||||||
|
.actions
|
||||||
|
.into_iter()
|
||||||
|
.map(|a| {
|
||||||
|
let name = a.name.clone();
|
||||||
|
let action_path = action_path.to_string_lossy().to_string();
|
||||||
|
|
||||||
|
ExecutableAction {
|
||||||
|
name: a.name,
|
||||||
|
description: String::new(),
|
||||||
|
flags: BTreeMap::default(),
|
||||||
|
call_fn: LazyResolve::new(Box::new(move || {
|
||||||
|
let name = name.clone();
|
||||||
|
let action_path = action_path.clone();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
tracing::debug!("calling: {}", name);
|
||||||
|
let mut cmd = tokio::process::Command::new(action_path);
|
||||||
|
cmd.args(["do", &name]);
|
||||||
|
|
||||||
|
let output = cmd.output().await?;
|
||||||
|
let stdout = std::str::from_utf8(&output.stdout)?;
|
||||||
|
for line in stdout.lines() {
|
||||||
|
println!("{}: {}", &name, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::debug!("finished call for output: {}", &name);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LazyResolve(
|
||||||
|
Arc<dyn Fn() -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>> + Send + Sync>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl LazyResolve {
|
||||||
|
pub fn new(
|
||||||
|
func: Box<
|
||||||
|
dyn Fn() -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>> + Send + Sync,
|
||||||
|
>,
|
||||||
|
) -> Self {
|
||||||
|
Self(Arc::new(func))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for LazyResolve {
|
||||||
|
type Target =
|
||||||
|
Arc<dyn Fn() -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>> + Send + Sync>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -40,8 +40,19 @@ impl Cli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_commands(&self) -> anyhow::Result<Vec<clap::Command>> {
|
async fn get_commands(&self) -> anyhow::Result<Vec<clap::Command>> {
|
||||||
|
let actions = self
|
||||||
|
.cuddle
|
||||||
|
.state
|
||||||
|
.actions
|
||||||
|
.actions
|
||||||
|
.iter()
|
||||||
|
.map(|a| clap::Command::new(&a.name))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
clap::Command::new("do").subcommand_required(true),
|
clap::Command::new("do")
|
||||||
|
.subcommand_required(true)
|
||||||
|
.subcommands(actions.as_slice()),
|
||||||
clap::Command::new("get")
|
clap::Command::new("get")
|
||||||
.about(GetCommand::description())
|
.about(GetCommand::description())
|
||||||
.arg(
|
.arg(
|
||||||
@ -67,8 +78,23 @@ impl Cli {
|
|||||||
.subcommand()
|
.subcommand()
|
||||||
.ok_or(anyhow::anyhow!("failed to find subcommand"))?
|
.ok_or(anyhow::anyhow!("failed to find subcommand"))?
|
||||||
{
|
{
|
||||||
("do", _args) => {
|
("do", args) => {
|
||||||
tracing::debug!("executing do");
|
tracing::debug!("executing do");
|
||||||
|
|
||||||
|
let (action_name, args) = args
|
||||||
|
.subcommand()
|
||||||
|
.ok_or(anyhow::anyhow!("failed to find do subcommand"))?;
|
||||||
|
|
||||||
|
let action = self
|
||||||
|
.cuddle
|
||||||
|
.state
|
||||||
|
.actions
|
||||||
|
.actions
|
||||||
|
.iter()
|
||||||
|
.find(|a| a.name == action_name)
|
||||||
|
.ok_or(anyhow::anyhow!("failed to find {}", action_name))?;
|
||||||
|
|
||||||
|
action.call().await?;
|
||||||
}
|
}
|
||||||
("get", args) => {
|
("get", args) => {
|
||||||
if !self.cuddle.has_project() {
|
if !self.cuddle.has_project() {
|
||||||
|
@ -54,7 +54,7 @@ impl Cuddle<PrepareProject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Cuddle<PreparePlan> {
|
impl Cuddle<PreparePlan> {
|
||||||
pub async fn build_state(&self) -> anyhow::Result<Cuddle<ValidatedState>> {
|
pub async fn build_state(&mut self) -> anyhow::Result<Cuddle<ValidatedState>> {
|
||||||
let mut state = if let Some(project) = &self.state.project {
|
let mut state = if let Some(project) = &self.state.project {
|
||||||
let state = state::State::new();
|
let state = state::State::new();
|
||||||
let raw_state = state.build_state(project, &self.state.plan).await?;
|
let raw_state = state.build_state(project, &self.state.plan).await?;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use validated_project::Project;
|
use validated_project::Project;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actions::Actions,
|
actions::{Actions, ExecutableActions},
|
||||||
plan::{self, ClonedPlan, PlanPathExt},
|
plan::{self, ClonedPlan, PlanPathExt},
|
||||||
project::{self, ProjectPlan},
|
project::{self, ProjectPlan},
|
||||||
schema_validator::SchemaValidator,
|
schema_validator::SchemaValidator,
|
||||||
@ -43,7 +43,7 @@ impl State {
|
|||||||
Ok(ValidatedState {
|
Ok(ValidatedState {
|
||||||
project: Some(project),
|
project: Some(project),
|
||||||
plan: None,
|
plan: None,
|
||||||
actions: LocalActions::default(),
|
actions: ExecutableActions::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,20 +58,21 @@ pub struct ValidatedState {
|
|||||||
pub project: Option<Project>,
|
pub project: Option<Project>,
|
||||||
pub plan: Option<Plan>,
|
pub plan: Option<Plan>,
|
||||||
|
|
||||||
pub actions: LocalActions,
|
pub actions: ExecutableActions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidatedState {
|
impl ValidatedState {
|
||||||
pub(crate) async fn build_actions(&mut self) -> anyhow::Result<&mut Self> {
|
pub(crate) async fn build_actions(&mut self) -> anyhow::Result<&mut Self> {
|
||||||
tracing::debug!("building actions");
|
tracing::debug!("building actions");
|
||||||
|
|
||||||
|
let mut local_actions = LocalActions::default();
|
||||||
if let Some(project) = &self.project {
|
if let Some(project) = &self.project {
|
||||||
if let Some(actions) = Actions::new(&project.root, &project.value).await? {
|
if let Some(actions) = Actions::new(&project.root, &project.value).await? {
|
||||||
self.actions.add(actions);
|
local_actions.add(actions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.actions.build().await?;
|
self.actions = local_actions.build().await?;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
@ -89,12 +90,15 @@ impl LocalActions {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build(&mut self) -> anyhow::Result<&mut Self> {
|
pub async fn build(&mut self) -> anyhow::Result<ExecutableActions> {
|
||||||
|
let mut executable_actions = ExecutableActions::default();
|
||||||
|
|
||||||
for actions in &mut self.0 {
|
for actions in &mut self.0 {
|
||||||
actions.build().await?;
|
let mut exec_actions = actions.build().await?;
|
||||||
|
executable_actions.actions.append(&mut exec_actions.actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
Ok(executable_actions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user