feat: cleanup

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-10-26 22:29:56 +02:00
parent 6729f6e794
commit c94f6b72b8
Signed by: kjuulh
GPG Key ID: D85D7535F18F35FA
3 changed files with 0 additions and 235 deletions

View File

@ -1 +0,0 @@

View File

@ -1,233 +0,0 @@
use std::{
collections::BTreeMap,
env::temp_dir,
path::{Path, PathBuf},
};
use serde::Deserialize;
use super::{ExecutableAction, ExecutableActions, LazyResolve};
pub struct RustActionsBuilder {
root_path: PathBuf,
}
impl RustActionsBuilder {
pub fn new(root_path: PathBuf) -> Self {
Self { root_path }
}
fn actions_path(&self) -> Option<PathBuf> {
let actions_path = self.root_path.join("actions/rust");
if !actions_path.exists() {
return None;
}
Some(actions_path)
}
fn global_registry(&self) -> anyhow::Result<Option<PathBuf>> {
if let Some(dir) = dirs::cache_dir().map(|c| c.join("sh.cuddle/registry/actions/rust")) {
if !dir.exists() {
std::fs::create_dir_all(&dir)?;
}
Ok(Some(dir))
} else {
Ok(None)
}
}
pub async fn build(&self) -> anyhow::Result<ExecutableActions> {
tracing::debug!("building rust action: {}", self.root_path.display());
let Some(path) = self.actions_path() else {
anyhow::bail!(
"action was not found: {}",
self.root_path.display().to_string()
);
};
let actions_registry = self
.global_registry()?
.ok_or(anyhow::anyhow!("failed to find global registry"))?;
let staging_id = uuid::Uuid::new_v4();
let actions_temp = temp_dir()
.join("cuddle/actions")
.join(staging_id.to_string());
std::fs::create_dir_all(&actions_temp)?;
let actions_temp: TempGuard = actions_temp.into();
let mut hasher = blake3::Hasher::new();
tracing::debug!("moving file into: {}", actions_temp.display());
for entry in walkdir::WalkDir::new(&path) {
let entry = entry?;
let full_path = entry.path();
let rel_path = full_path
.strip_prefix(path.canonicalize()?.to_string_lossy().to_string())?
.to_string_lossy();
if rel_path.contains("target/")
|| rel_path.contains(".cuddle/")
|| rel_path.contains(".git/")
{
continue;
}
let metadata = entry.metadata()?;
if metadata.is_file() {
let temp_file_path = actions_temp.join(rel_path.to_string());
if let Some(temp_parent) = temp_file_path.parent() {
std::fs::create_dir_all(temp_parent)?;
}
std::fs::copy(entry.path(), temp_file_path)?;
hasher.update(rel_path.as_bytes());
let file_bytes = tokio::fs::read(entry.path()).await?;
hasher.update(&file_bytes);
}
}
let digest = hasher.finalize().to_hex().to_string();
let action_index = actions_registry.join(digest);
if action_index.exists() {
tracing::debug!("action already exists in: {}", action_index.display());
return self.get_actions(&action_index.join("action")).await;
}
std::fs::create_dir_all(&action_index)?;
tracing::debug!("building rust code: {}", actions_temp.display());
let mut cmd = tokio::process::Command::new("cargo");
let output = cmd
.args(vec!["build", "--release"])
.current_dir(actions_temp.as_path())
.output()
.await?;
if !output.status.success() {
anyhow::bail!(
"cargo build failed: {}",
std::str::from_utf8(&output.stderr)?
);
}
let temp_file_bin_path = actions_temp.join("target/release/action");
tokio::fs::copy(temp_file_bin_path, action_index.join("action")).await?;
self.get_actions(&action_index.join("action")).await
}
pub async fn get_actions(&self, action_path: &Path) -> anyhow::Result<ExecutableActions> {
tracing::debug!("querying schema: {}", action_path.display());
let output = tokio::process::Command::new(action_path.to_string_lossy().to_string())
.arg("schema")
.output()
.await?;
let actions: CuddleActionsSchema = match serde_json::from_slice(&output.stdout) {
Ok(output) => output,
Err(e) => {
let schema_output = std::str::from_utf8(&output.stdout)?;
anyhow::bail!(
"failed to query schema: {} {}",
e.to_string(),
schema_output
)
}
};
actions.to_executable(action_path)
}
}
struct TempGuard(PathBuf);
impl From<PathBuf> for TempGuard {
fn from(value: PathBuf) -> Self {
Self(value)
}
}
impl std::ops::Deref for TempGuard {
type Target = PathBuf;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Drop for TempGuard {
fn drop(&mut self) {
match std::fs::remove_dir_all(&self.0) {
Ok(_) => {
tracing::trace!("cleaned up temp dir: {}", self.0.display());
}
Err(e) => panic!("{}", e),
}
}
}
#[derive(Debug, Deserialize, Clone)]
struct CuddleActionsSchema {
actions: Vec<CuddleActionSchema>,
}
#[derive(Debug, Deserialize, Clone)]
struct CuddleActionSchema {
name: String,
}
impl CuddleActionsSchema {
fn to_executable(&self, action_path: &Path) -> anyhow::Result<ExecutableActions> {
Ok(ExecutableActions {
actions: self
.actions
.iter()
.cloned()
.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 {
if let Some(parent) = PathBuf::from(&action_path).parent() {
tokio::process::Command::new("touch")
.arg(parent)
.output()
.await?;
}
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(),
})
}
}

View File

@ -1,7 +1,6 @@
use cli::Cli;
use cuddle_state::Cuddle;
mod actions;
mod cli;
mod cuddle_state;
mod plan;