feat: with further rust build actions
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
@@ -17,3 +17,7 @@ uuid.workspace = true
|
||||
|
||||
toml = "0.8.19"
|
||||
fs_extra = "1.3.0"
|
||||
dirs = "5.0.1"
|
||||
walkdir = "2.5.0"
|
||||
sha256 = "1.5.0"
|
||||
blake3 = "1.5.4"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "rust"
|
||||
name = "action"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@@ -2,8 +2,6 @@ use cuddle_actions::AddActionOptions;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
println!("Hello, world!");
|
||||
|
||||
cuddle_actions::CuddleActions::default()
|
||||
.add_action("something", || Ok(()), &AddActionOptions::default())
|
||||
.execute()?;
|
||||
|
@@ -4,13 +4,17 @@ use std::{
|
||||
};
|
||||
|
||||
use rust_builder::RustActionsBuilder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::state::validated_project::Value;
|
||||
|
||||
pub mod builder;
|
||||
|
||||
pub mod rust_builder {
|
||||
use std::path::PathBuf;
|
||||
use std::{
|
||||
env::temp_dir,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use super::ExecutableActions;
|
||||
|
||||
@@ -23,8 +27,131 @@ pub mod rust_builder {
|
||||
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> {
|
||||
Ok(ExecutableActions::default())
|
||||
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 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(entry.path().to_string_lossy().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)
|
||||
.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: ExecutableActions = 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
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(actions)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,17 +203,19 @@ pub enum ActionVariant {
|
||||
Docker,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct ExecutableActions {
|
||||
actions: Vec<ExecutableAction>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ExecutableAction {
|
||||
name: String,
|
||||
description: String,
|
||||
flags: BTreeMap<String, ExecutableActionFlag>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ExecutableActionFlag {
|
||||
String,
|
||||
Bool,
|
||||
|
Reference in New Issue
Block a user