diff --git a/_examples/actions/write_a_readme/main b/_examples/actions/write_a_readme/main new file mode 100755 index 0000000..9ac7386 Binary files /dev/null and b/_examples/actions/write_a_readme/main differ diff --git a/crates/octopush_cli/src/commands/execute.rs b/crates/octopush_cli/src/commands/execute.rs index 7deaab8..926b760 100644 --- a/crates/octopush_cli/src/commands/execute.rs +++ b/crates/octopush_cli/src/commands/execute.rs @@ -1,7 +1,7 @@ -use std::path::{self, PathBuf}; +use std::path::PathBuf; use clap::{Arg, ArgAction, ArgMatches, Command}; -use octopush_core::schema::{self, models::Action}; +use octopush_core::schema; use octopush_infra::service_register::ServiceRegister; pub fn execute_cmd() -> Command { @@ -53,7 +53,14 @@ pub async fn execute_subcommand(args: &ArgMatches) -> eyre::Result<()> { paths.push(path); } - kk + for path in paths { + for action in actions.clone() { + service_register + .executor + .execute(path.clone(), action_path.clone(), action) + .await?; + } + } } } diff --git a/crates/octopush_core/src/builder/builder_capabilities.rs b/crates/octopush_core/src/builder/builder_capabilities.rs new file mode 100644 index 0000000..05441e4 --- /dev/null +++ b/crates/octopush_core/src/builder/builder_capabilities.rs @@ -0,0 +1,36 @@ +use std::{path::PathBuf, sync::Arc}; + +use async_trait::async_trait; + +use crate::schema::models::Action; + +use super::{ + builders::golang_bin::{GolangBinBuild, GolangBinBuildOpts}, + Builder, DynRunnableBin, +}; + +pub struct BuilderCapabilities; + +impl BuilderCapabilities { + pub fn new() -> Self { + Self {} + } +} + +#[async_trait] +impl Builder for BuilderCapabilities { + async fn build(&self, action_path: PathBuf, action: Action) -> eyre::Result { + match action { + Action::Go { entry } => { + let bin = GolangBinBuild::new() + .build(GolangBinBuildOpts { + entry, + src_path: action_path, + }) + .await?; + + Ok(Arc::new(bin)) + } + } + } +} diff --git a/crates/octopush_core/src/builder/builders/golang_bin.rs b/crates/octopush_core/src/builder/builders/golang_bin.rs new file mode 100644 index 0000000..7acab58 --- /dev/null +++ b/crates/octopush_core/src/builder/builders/golang_bin.rs @@ -0,0 +1,55 @@ +use std::path::PathBuf; + +use async_trait::async_trait; + +use crate::{ + builder::RunnableBin, + shell::{execute_shell, print_res}, +}; + +pub struct GolangBinBuildOpts { + pub entry: String, + pub src_path: PathBuf, +} + +pub struct GolangBinBuild; + +impl GolangBinBuild { + pub fn new() -> Self { + Self {} + } + + pub async fn build(&self, opts: GolangBinBuildOpts) -> eyre::Result { + tracing::trace!( + src = opts.src_path.to_string_lossy().to_string(), + entry = opts.entry, + "build golang_bin" + ); + + let res = execute_shell( + format!("go build {}", opts.entry), + Some(opts.src_path.clone()), + ) + .await?; + print_res("golang_bin".into(), res); + + Ok(GolangBin::new(opts.src_path)) + } +} + +pub struct GolangBin { + path: PathBuf, +} + +impl GolangBin { + fn new(path: PathBuf) -> Self { + Self { path } + } +} + +#[async_trait] +impl RunnableBin for GolangBin { + async fn run(&self) -> eyre::Result<()> { + todo!("not implemented") + } +} diff --git a/crates/octopush_core/src/builder/builders/mod.rs b/crates/octopush_core/src/builder/builders/mod.rs new file mode 100644 index 0000000..9b0dbd0 --- /dev/null +++ b/crates/octopush_core/src/builder/builders/mod.rs @@ -0,0 +1 @@ +pub mod golang_bin; diff --git a/crates/octopush_core/src/builder/mod.rs b/crates/octopush_core/src/builder/mod.rs new file mode 100644 index 0000000..e3e7931 --- /dev/null +++ b/crates/octopush_core/src/builder/mod.rs @@ -0,0 +1,22 @@ +pub mod builder_capabilities; +mod builders; + +use std::{path::PathBuf, sync::Arc}; + +use async_trait::async_trait; + +use crate::schema::models::Action; + +#[async_trait] +pub trait RunnableBin { + async fn run(&self) -> eyre::Result<()>; +} + +pub type DynRunnableBin = Arc; + +#[async_trait] +pub trait Builder { + async fn build(&self, action_path: PathBuf, action: Action) -> eyre::Result; +} + +pub type DynBuilder = Arc; diff --git a/crates/octopush_core/src/executor/default_executor.rs b/crates/octopush_core/src/executor/default_executor.rs index f88d47e..a23bce1 100644 --- a/crates/octopush_core/src/executor/default_executor.rs +++ b/crates/octopush_core/src/executor/default_executor.rs @@ -1,16 +1,43 @@ +use std::path::PathBuf; + use async_trait::async_trait; -use crate::schema::models::Action; +use crate::{builder::DynBuilder, schema::models::Action}; -use super::executor::Executor; +use super::{ + executor::Executor, + executors::golang::{GolangExecutor, GolangExecutorOpts}, +}; -pub struct DefaultExecutor; +pub struct DefaultExecutor { + builder: DynBuilder, +} + +impl DefaultExecutor { + pub fn new(builder: DynBuilder) -> Self { + Self { builder } + } +} #[async_trait] impl Executor for DefaultExecutor { - async fn execute(&self, action: Action) -> eyre::Result<()> { + async fn execute( + &self, + victim_path: PathBuf, + action_path: PathBuf, + action: Action, + ) -> eyre::Result<()> { + let bin = self.builder.build(action_path, action.clone()).await?; match action { - Action::Go { entry } => todo!(), + Action::Go { entry } => { + GolangExecutor::new() + .execute(GolangExecutorOpts { + bin, + entry, + src_path: victim_path, + }) + .await? + } } Ok(()) diff --git a/crates/octopush_core/src/executor/executor.rs b/crates/octopush_core/src/executor/executor.rs index 64c685d..d723c08 100644 --- a/crates/octopush_core/src/executor/executor.rs +++ b/crates/octopush_core/src/executor/executor.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use async_trait::async_trait; @@ -6,7 +6,12 @@ use crate::schema::models::Action; #[async_trait] pub trait Executor { - async fn execute(&self, action: Action) -> eyre::Result<()>; + async fn execute( + &self, + victim_path: PathBuf, + action_path: PathBuf, + action: Action, + ) -> eyre::Result<()>; } pub type DynExecutor = Arc; diff --git a/crates/octopush_core/src/executor/executors/golang.rs b/crates/octopush_core/src/executor/executors/golang.rs index e69de29..d9b8b2a 100644 --- a/crates/octopush_core/src/executor/executors/golang.rs +++ b/crates/octopush_core/src/executor/executors/golang.rs @@ -0,0 +1,21 @@ +use std::path::PathBuf; + +use crate::builder::DynRunnableBin; + +pub struct GolangExecutorOpts { + pub bin: DynRunnableBin, + pub entry: String, + pub src_path: PathBuf, +} + +pub struct GolangExecutor; + +impl GolangExecutor { + pub fn new() -> Self { + Self {} + } + + pub async fn execute(&self, opts: GolangExecutorOpts) -> eyre::Result<()> { + Ok(()) + } +} diff --git a/crates/octopush_core/src/lib.rs b/crates/octopush_core/src/lib.rs index 0bc06ab..a51286f 100644 --- a/crates/octopush_core/src/lib.rs +++ b/crates/octopush_core/src/lib.rs @@ -1,4 +1,6 @@ -pub mod git; -pub mod storage; -pub mod schema; +pub mod builder; pub mod executor; +pub mod git; +pub mod schema; +mod shell; +pub mod storage; diff --git a/crates/octopush_core/src/shell/mod.rs b/crates/octopush_core/src/shell/mod.rs new file mode 100644 index 0000000..5159f3f --- /dev/null +++ b/crates/octopush_core/src/shell/mod.rs @@ -0,0 +1,57 @@ +use std::{path::PathBuf, process::Stdio}; + +use eyre::Context; +use tokio::io::{AsyncBufReadExt, BufReader}; + +pub async fn execute_shell(cmd: String, path: Option) -> eyre::Result> { + let mut command = tokio::process::Command::new("sh"); + let command = command.arg("-c"); + + let command = if let Some(path) = path { + command.current_dir(path) + } else { + command + }; + + let command = command.arg(format!("{}", cmd)); + + let command = command.stdout(Stdio::piped()); + + let mut child = command.spawn()?; + + let stdout = child + .stdout + .take() + .ok_or(eyre::anyhow!("could not take stdout of command"))?; + + let mut reader = BufReader::new(stdout).lines(); + + tokio::spawn(async move { + let status = child + .wait() + .await + .context(eyre::anyhow!("child process encountered an error")) + .unwrap(); + + if !status.success() { + tracing::error!( + cmd, + status = status.to_string(), + "child program encountered an error" + ); + } + }); + + let mut lines: Vec = Vec::new(); + while let Some(line) = reader.next_line().await? { + lines.push(line) + } + + Ok(lines) +} + +pub fn print_res(scope: String, res: Vec) { + for r in res { + tracing::debug!("{}: {}", scope, r); + } +} diff --git a/crates/octopush_infra/src/service_register.rs b/crates/octopush_infra/src/service_register.rs index 32f338b..7037e73 100644 --- a/crates/octopush_infra/src/service_register.rs +++ b/crates/octopush_infra/src/service_register.rs @@ -1,6 +1,8 @@ use std::sync::Arc; use octopush_core::{ + builder::{builder_capabilities::BuilderCapabilities, DynBuilder}, + executor::{default_executor::DefaultExecutor, executor::DynExecutor}, git::{github::GitHubGitProvider, DynGitProvider}, schema::parser::{DefaultSchemaParser, DynSchemaParser}, storage::{local::LocalStorageEngine, DynStorageEngine}, @@ -10,6 +12,8 @@ pub struct ServiceRegister { pub storage_engine: DynStorageEngine, pub git_provider: DynGitProvider, pub schema_parser: DynSchemaParser, + pub builder: DynBuilder, + pub executor: DynExecutor, } impl ServiceRegister { @@ -17,11 +21,15 @@ impl ServiceRegister { let storage_engine = Arc::new(LocalStorageEngine::new("/tmp/octopush".into())); let git_provider = Arc::new(GitHubGitProvider::new(storage_engine.clone())); let schema_parser = Arc::new(DefaultSchemaParser::new()); + let builder = Arc::new(BuilderCapabilities::new()); + let executor = Arc::new(DefaultExecutor::new(builder.clone())); Self { storage_engine, git_provider, schema_parser, + builder, + executor, } }