feat: scripts can run from plan

This commit is contained in:
Kasper Juul Hermansen 2025-02-28 22:08:56 +01:00
parent 2fadde0f8a
commit b0261de87e
Signed by: kjuulh
SSH Key Fingerprint: SHA256:RjXh0p7U6opxnfd3ga/Y9TCo18FYlHFdSpRIV72S/QM
3 changed files with 105 additions and 14 deletions

View File

@ -19,6 +19,24 @@ impl Run {
} }
} }
if let Some(plan) = &ctx.plan {
if let Some(scripts) = &plan.scripts {
let existing_cmds = run_cmd
.get_subcommands()
.map(|s| s.get_name().to_string())
.collect::<Vec<_>>();
for name in scripts.items.keys() {
if existing_cmds.contains(name) {
continue;
}
let cmd = clap::Command::new(name.to_string());
run_cmd = run_cmd.subcommand(cmd);
}
}
}
root.subcommand(run_cmd) root.subcommand(run_cmd)
} }
@ -31,14 +49,27 @@ impl Run {
anyhow::bail!("failed to find a matching run command") anyhow::bail!("failed to find a matching run command")
}; };
let scripts_ctx = ctx.project.scripts.as_ref().expect("to find scripts"); if let Some(scripts_ctx) = &ctx.project.scripts {
let Some(script_ctx) = scripts_ctx.items.get(name) else { if let Some(script_ctx) = scripts_ctx.items.get(name) {
anyhow::bail!("failed to find script: {}", name); ScriptExecutor::new(project_path.into(), ctx.clone())
}; .run(script_ctx, name)
.await?;
ScriptExecutor::new(project_path.into(), ctx.clone()) return Ok(());
.run(script_ctx, name) }
.await?; }
if let Some(plan) = &ctx.plan {
if let Some(scripts_ctx) = &plan.scripts {
if let Some(script_ctx) = scripts_ctx.items.get(name) {
ScriptExecutor::new(project_path.into(), ctx.clone())
.run(script_ctx, name)
.await?;
return Ok(());
}
}
}
Ok(()) Ok(())
} }

View File

@ -18,10 +18,44 @@ impl ScriptExecutor {
} }
pub async fn run(&self, script_ctx: &Script, name: &str) -> anyhow::Result<()> { pub async fn run(&self, script_ctx: &Script, name: &str) -> anyhow::Result<()> {
match script_ctx { if self.run_project(script_ctx, name).await? {
Script::Shell {} => ShellExecutor::from(self).execute(name).await?, return Ok(());
}
if self.run_plan(script_ctx, name).await? {
return Ok(());
} }
Ok(()) Ok(())
} }
async fn run_project(&self, script_ctx: &Script, name: &str) -> anyhow::Result<bool> {
match script_ctx {
Script::Shell {} => {
if matches!(
ShellExecutor::from(self).execute(name).await?,
shell::ScriptStatus::Found
) {
Ok(true)
} else {
Ok(false)
}
}
}
}
async fn run_plan(&self, script_ctx: &Script, name: &str) -> anyhow::Result<bool> {
match script_ctx {
Script::Shell {} => {
if matches!(
ShellExecutor::from_plan(self).execute(name).await?,
shell::ScriptStatus::Found
) {
Ok(true)
} else {
Ok(false)
}
}
}
}
} }

View File

@ -1,4 +1,4 @@
use std::process::Stdio; use std::{path::PathBuf, process::Stdio};
use anyhow::Context; use anyhow::Context;
@ -6,15 +6,26 @@ use super::ScriptExecutor;
pub struct ShellExecutor { pub struct ShellExecutor {
root: ScriptExecutor, root: ScriptExecutor,
ty: ShellType,
}
pub enum ScriptStatus {
Found,
NotFound,
}
enum ShellType {
Plan,
Project,
} }
impl ShellExecutor { impl ShellExecutor {
pub async fn execute(&self, name: &str) -> anyhow::Result<()> { pub async fn execute(&self, name: &str) -> anyhow::Result<ScriptStatus> {
let path = &self.root.project_path; let path = &self.get_path();
let script_path = path.join("scripts").join(format!("{name}.sh")); let script_path = path.join("scripts").join(format!("{name}.sh"));
if !script_path.exists() { if !script_path.exists() {
anyhow::bail!("script was not found at: {}", script_path.display()); return Ok(ScriptStatus::NotFound);
} }
let mut cmd = tokio::process::Command::new(&script_path); let mut cmd = tokio::process::Command::new(&script_path);
@ -37,7 +48,21 @@ impl ShellExecutor {
) )
} }
Ok(()) Ok(ScriptStatus::Found)
}
fn get_path(&self) -> PathBuf {
match self.ty {
ShellType::Plan => self.root.project_path.join(".forest").join("plan"),
ShellType::Project => self.root.project_path.clone(),
}
}
pub fn from_plan(value: &ScriptExecutor) -> Self {
Self {
root: value.clone(),
ty: ShellType::Plan,
}
} }
} }
@ -45,6 +70,7 @@ impl From<&ScriptExecutor> for ShellExecutor {
fn from(value: &ScriptExecutor) -> Self { fn from(value: &ScriptExecutor) -> Self {
Self { Self {
root: value.clone(), root: value.clone(),
ty: ShellType::Project,
} }
} }
} }