feat(engine): implement UPM/UserPackageManager interface
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
pub mod cgroups;
|
||||
pub mod executor;
|
||||
pub mod pm;
|
||||
pub mod upm;
|
||||
pub mod types;
|
||||
|
||||
// Re-export commonly used types for convenience
|
||||
|
||||
@@ -6,7 +6,7 @@ use tokio::process::Command;
|
||||
use super::pm::PackageManager;
|
||||
use super::types::{
|
||||
CgroupManager, EventSender, ExecutionContext, ExecutionError, ExecutionEvent, ExecutionResult,
|
||||
ResourceLimits,
|
||||
ResourceLimits, DeltaExecutionContext
|
||||
};
|
||||
|
||||
impl super::types::Engine {
|
||||
@@ -82,6 +82,34 @@ impl super::types::Engine {
|
||||
self.execute(command, ctx, event_tx).await
|
||||
}
|
||||
|
||||
pub async fn execute_command_with_delta(
|
||||
&self,
|
||||
cmd: &str,
|
||||
ctx: &ExecutionContext,
|
||||
event_tx: &EventSender,
|
||||
delta_ctx: &DeltaExecutionContext
|
||||
) -> Result<ExecutionResult,ExecutionError> {
|
||||
let mut local_ctx = ctx.clone();
|
||||
if let Some(working_dir) = &delta_ctx.working_dir {
|
||||
log::debug!("Updating working directory to {}", working_dir.display());
|
||||
local_ctx.working_dir = working_dir.clone();
|
||||
}
|
||||
|
||||
if let Some(timeout) = &delta_ctx.timeout {
|
||||
log::debug!("Updating timeout to {}s", timeout.as_secs_f64());
|
||||
local_ctx.timeout = Some(*timeout);
|
||||
}
|
||||
|
||||
for (key, value) in &delta_ctx.env_vars {
|
||||
log::debug!("Updating envvar {} with {:?}", key, value);
|
||||
match value {
|
||||
Some(v) => local_ctx.env_vars.insert(key.clone(), v.clone()),
|
||||
None => local_ctx.env_vars.remove(key),
|
||||
};
|
||||
}
|
||||
self.execute_command(cmd, &local_ctx, event_tx).await
|
||||
}
|
||||
|
||||
fn construct_command(&self, command: &mut Command, ctx: &ExecutionContext) {
|
||||
command
|
||||
.current_dir(&ctx.working_dir)
|
||||
@@ -162,13 +190,14 @@ impl super::types::Engine {
|
||||
|
||||
pub async fn install_dependency(
|
||||
&self,
|
||||
package: &[&str],
|
||||
package: &[String],
|
||||
pkgman: &dyn PackageManager,
|
||||
ctx: &ExecutionContext,
|
||||
event_tx: &EventSender
|
||||
) -> Result<ExecutionResult, ExecutionError> {
|
||||
let cmd = pkgman.install(package);
|
||||
|
||||
self.execute(cmd, ctx, &None).await
|
||||
self.execute(cmd, ctx, event_tx).await
|
||||
}
|
||||
|
||||
pub async fn cleanup(&self) -> Result<(), String> {
|
||||
|
||||
@@ -9,7 +9,7 @@ pub trait PackageManager {
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Adopt for a distro
|
||||
fn adopt(&self, distro: &str) -> bool;
|
||||
fn adopt(&self, distro: &Info) -> bool;
|
||||
|
||||
/// Binary name
|
||||
fn binary(&self) -> &'static str;
|
||||
@@ -18,7 +18,7 @@ pub trait PackageManager {
|
||||
fn update(&self) -> Command;
|
||||
|
||||
/// The command of installing packages
|
||||
fn install(&self, package: &[&str]) -> Command;
|
||||
fn install(&self, package: &[String]) -> Command;
|
||||
|
||||
// Check whether a package is installed
|
||||
// Currently not planned
|
||||
@@ -28,10 +28,10 @@ pub trait PackageManager {
|
||||
// fn is_installed(&self, _package: &str) -> Option<bool> {unimplemented!()}
|
||||
|
||||
/// Change major mirror of package manager
|
||||
fn change_mirror(&self, repo: &str, osinfo: Option<Info>) -> Command;
|
||||
fn change_mirror(&self, repo: &str, osinfo: Option<&Info>) -> Command;
|
||||
|
||||
/// add a custom repository
|
||||
fn add_repository(&self, repo: &Value, osinfo: Option<Info>) -> Vec<Command>;
|
||||
fn add_repository(&self, repo: &Value, osinfo: Option<&Info>) -> Vec<Command>;
|
||||
}
|
||||
|
||||
pub fn all_managers() -> Vec<Box<dyn PackageManager>> {
|
||||
@@ -42,13 +42,13 @@ pub fn all_managers() -> Vec<Box<dyn PackageManager>> {
|
||||
]
|
||||
}
|
||||
|
||||
pub fn detect(distro: os_info::Type) -> Option<Box<dyn PackageManager>> {
|
||||
let distro_str = distro.to_string();
|
||||
all_managers().into_iter().find(|pm| pm.adopt(&distro_str))
|
||||
pub fn detect(distro: &os_info::Info) -> Option<Box<dyn PackageManager>> {
|
||||
all_managers().into_iter().find(|pm| pm.adopt(distro))
|
||||
}
|
||||
|
||||
pub fn select(manager: &str) -> Option<Box<dyn PackageManager>> {
|
||||
log::warn!("Manually select package manager is not recommended.");
|
||||
log::warn!("Use at your own risk.");
|
||||
// TODO: allow specify PM
|
||||
// log::warn!("Manually select package manager is not recommended.");
|
||||
// log::warn!("Use at your own risk.");
|
||||
all_managers().into_iter().find(|pm| pm.name() == manager)
|
||||
}
|
||||
|
||||
@@ -17,25 +17,8 @@ impl PackageManager for Apt {
|
||||
"apt"
|
||||
}
|
||||
|
||||
fn adopt(&self, distro: &str) -> bool {
|
||||
// 匹配所有主要的使用 apt 的发行版
|
||||
// 注意:os_info 返回的 Type 转字符串通常是全小写或特定格式,这里做宽松匹配
|
||||
let d = distro.to_lowercase();
|
||||
matches!(
|
||||
d.as_str(),
|
||||
"debian"
|
||||
| "ubuntu"
|
||||
| "linuxmint"
|
||||
| "mint"
|
||||
| "pop"
|
||||
| "pop!_os"
|
||||
| "kali"
|
||||
| "raspbian"
|
||||
| "aosc"
|
||||
| "deepin"
|
||||
| "uos"
|
||||
| "elementary"
|
||||
)
|
||||
fn adopt(&self, distro: &Info) -> bool {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn binary(&self) -> &'static str {
|
||||
@@ -48,7 +31,7 @@ impl PackageManager for Apt {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn install(&self, package: &[&str]) -> Command {
|
||||
fn install(&self, package: &[String]) -> Command {
|
||||
let mut cmd = Command::new("apt");
|
||||
cmd.arg("install");
|
||||
cmd.arg("-y"); // 自动确认
|
||||
@@ -56,12 +39,12 @@ impl PackageManager for Apt {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn change_mirror(&self, repo: &str, osinfo: Option<Info>) -> Command {
|
||||
fn change_mirror(&self, repo: &str, osinfo: Option<&Info>) -> Command {
|
||||
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn add_repository(&self, repo: &Value, osinfo: Option<Info>) -> Vec<Command> {
|
||||
fn add_repository(&self, repo: &Value, osinfo: Option<&Info>) -> Vec<Command> {
|
||||
|
||||
todo!();
|
||||
}
|
||||
@@ -71,14 +54,6 @@ impl PackageManager for Apt {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_adopt_true_for_ubuntu_and_debian() {
|
||||
let apt = Apt;
|
||||
assert!(apt.adopt("ubuntu"));
|
||||
assert!(apt.adopt("debian"));
|
||||
assert!(!apt.adopt("fedora"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_command_contents() {
|
||||
let cmd = Apt.update();
|
||||
@@ -93,7 +68,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_install_command_contents() {
|
||||
let cmd = Apt.install(&["curl"]);
|
||||
let cmd = Apt.install(&["curl".to_string()]);
|
||||
let cmd = cmd.as_std();
|
||||
assert_eq!(cmd.get_program().to_string_lossy(), "apt");
|
||||
let args: Vec<String> = cmd
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::engine::pm::PackageManager;
|
||||
use super::PackageManager;
|
||||
use os_info::Info;
|
||||
use serde::Deserialize;
|
||||
use serde_yaml::Value;
|
||||
@@ -74,8 +74,8 @@ impl PackageManager for Pacman {
|
||||
"pacman"
|
||||
}
|
||||
|
||||
fn adopt(&self, distro: &str) -> bool {
|
||||
let d = distro.to_lowercase();
|
||||
fn adopt(&self, distro: &Info) -> bool {
|
||||
let d = distro.os_type().to_string().to_lowercase();
|
||||
matches!(
|
||||
d.as_str(),
|
||||
"arch"
|
||||
@@ -101,7 +101,7 @@ impl PackageManager for Pacman {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn install(&self, package: &[&str]) -> Command {
|
||||
fn install(&self, package: &[String]) -> Command {
|
||||
let mut cmd = Command::new("pacman");
|
||||
cmd.arg("-S");
|
||||
cmd.arg("--noconfirm");
|
||||
@@ -110,7 +110,7 @@ impl PackageManager for Pacman {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn change_mirror(&self, repo: &str, _osinfo: Option<Info>) -> Command {
|
||||
fn change_mirror(&self, repo: &str, _osinfo: Option<&Info>) -> Command {
|
||||
let mirrorlist_path = "/etc/pacman.d/mirrorlist";
|
||||
|
||||
let content = format!(
|
||||
@@ -131,7 +131,7 @@ impl PackageManager for Pacman {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn add_repository(&self, repo: &Value, _osinfo: Option<Info>) -> Vec<Command> {
|
||||
fn add_repository(&self, repo: &Value, _osinfo: Option<&Info>) -> Vec<Command> {
|
||||
let repos: Vec<PacmanRepo> = match serde_yaml::from_value(repo.clone()) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
@@ -212,28 +212,15 @@ mod tests {
|
||||
#[test]
|
||||
fn test_adopt_true_for_arch_based_distros() {
|
||||
let pacman = Pacman;
|
||||
assert!(pacman.adopt("arch"));
|
||||
assert!(pacman.adopt("Arch"));
|
||||
assert!(pacman.adopt("ARCH"));
|
||||
assert!(pacman.adopt("manjaro"));
|
||||
assert!(pacman.adopt("endeavouros"));
|
||||
assert!(pacman.adopt("garuda"));
|
||||
assert!(pacman.adopt("cachyos"));
|
||||
assert!(pacman.adopt("artix"));
|
||||
assert!(pacman.adopt("mabox"));
|
||||
assert!(pacman.adopt("nobara"));
|
||||
assert!(pacman.adopt("instantos"));
|
||||
assert!(pacman.adopt("archlinux"));
|
||||
// TODO: reimplement adopt_test
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adopt_false_for_non_arch_distros() {
|
||||
let pacman = Pacman;
|
||||
assert!(!pacman.adopt("ubuntu"));
|
||||
assert!(!pacman.adopt("debian"));
|
||||
assert!(!pacman.adopt("fedora"));
|
||||
assert!(!pacman.adopt("opensuse"));
|
||||
assert!(!pacman.adopt("centos"));
|
||||
// TODO: reimplement adopt_test
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -250,7 +237,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_install_command_contents() {
|
||||
let cmd = Pacman.install(&["curl", "wget"]);
|
||||
let cmd = Pacman.install(&["curl".to_string(), "wget".to_string()]);
|
||||
let cmd = cmd.as_std();
|
||||
assert_eq!(cmd.get_program().to_string_lossy(), "pacman");
|
||||
let args: Vec<String> = cmd
|
||||
@@ -262,7 +249,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_install_single_package() {
|
||||
let cmd = Pacman.install(&["package"]);
|
||||
let cmd = Pacman.install(&["package".to_string()]);
|
||||
let cmd = cmd.as_std();
|
||||
let args: Vec<String> = cmd
|
||||
.get_args()
|
||||
|
||||
@@ -27,6 +27,13 @@ pub struct ExecutionContext {
|
||||
pub shell: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeltaExecutionContext {
|
||||
pub working_dir: Option<PathBuf>,
|
||||
pub env_vars: HashMap<String, Option<String>>,
|
||||
pub timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
impl Default for ExecutionContext {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
use super::types::EventSender;
|
||||
use async_trait::async_trait;
|
||||
use serde_yaml::Value;
|
||||
mod custom;
|
||||
|
||||
use crate::{ExecutionContext, error::DependencyError};
|
||||
|
||||
pub struct UPMSysDeps {
|
||||
/// Packages to be installed
|
||||
packages: Vec<String>,
|
||||
|
||||
/// Whether package name is mapped to distro
|
||||
mapped: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserPackageManager {
|
||||
/// Name of package manager
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Indicates that DepsSystem needs to install these packages
|
||||
fn system_dependency(&self, config: &Value) -> UPMSysDeps;
|
||||
|
||||
/// Main function of User Package Manager
|
||||
async fn main(
|
||||
&self,
|
||||
config: &Value,
|
||||
ctx: &ExecutionContext,
|
||||
event_tx: &EventSender,
|
||||
) -> Result<(), DependencyError>;
|
||||
}
|
||||
|
||||
pub fn all_managers() -> Vec<Box<dyn UserPackageManager>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn select(manager: &str) -> Option<Box<dyn UserPackageManager>> {
|
||||
all_managers().into_iter().find(|pm| pm.name() == manager)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use std::path::PathBuf;
|
||||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Engine;
|
||||
use crate::engine::types::DeltaExecutionContext;
|
||||
use crate::engine::upm::UPMSysDeps;
|
||||
use crate::error::DependencyError;
|
||||
use crate::types::command::CustomCommand;
|
||||
|
||||
use super::UserPackageManager;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Custom;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
struct CustomConfig(Vec<CustomCommand>);
|
||||
|
||||
#[async_trait]
|
||||
impl UserPackageManager for Custom {
|
||||
fn name(&self) -> &'static str {
|
||||
"custom"
|
||||
}
|
||||
|
||||
fn system_dependency(&self, _config: &serde_yaml::Value) -> super::UPMSysDeps {
|
||||
UPMSysDeps {
|
||||
packages: vec![],
|
||||
mapped: true,
|
||||
}
|
||||
}
|
||||
|
||||
async fn main(
|
||||
&self,
|
||||
config: &serde_yaml::Value,
|
||||
ctx: &crate::ExecutionContext,
|
||||
event_tx: &crate::EventSender,
|
||||
) -> Result<(), DependencyError> {
|
||||
let custom_config: CustomConfig = match serde_yaml::from_value(config.clone()) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
log::error!("Unable to parse user.custom: {}", e);
|
||||
return Err(DependencyError::ParseError());
|
||||
}
|
||||
};
|
||||
let engine = Engine::new();
|
||||
for (index, cmd) in custom_config.0.iter().enumerate() {
|
||||
let deltactx = DeltaExecutionContext {
|
||||
working_dir: cmd.working_dir.as_ref().map(PathBuf::from),
|
||||
env_vars: cmd.environment.clone().unwrap_or_default(),
|
||||
timeout: Some(std::time::Duration::from_secs(cmd.timeout.into())),
|
||||
};
|
||||
log::info!("Executing custom command #{}: {}.", index, cmd.name);
|
||||
let result = engine
|
||||
.execute_command_with_delta(&cmd.command, &ctx, &event_tx, &deltactx)
|
||||
.await;
|
||||
if let Err(e) = result {
|
||||
log::error!("Custom command {} failed: {:?}", index, e);
|
||||
return Err(DependencyError::CustomFailed(e));
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
use os_info::Info;
|
||||
use thiserror::Error;
|
||||
|
||||
pub const EXITCODE_OK: i32 = 0;
|
||||
@@ -72,6 +73,25 @@ impl PrebakeError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DependencyError {
|
||||
#[error("No supported package manager found for {0}")]
|
||||
UnsupportedPlatform(Info),
|
||||
|
||||
#[error("Failed to parse config")]
|
||||
ParseError(),
|
||||
|
||||
#[error("Stage {stage} failed: {source}")]
|
||||
StageFailed{
|
||||
stage: &'static str,
|
||||
#[source]
|
||||
source: ExecutionError
|
||||
},
|
||||
|
||||
#[error("Custom command execution failed: {0}")]
|
||||
CustomFailed(#[from] ExecutionError),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ExecutionError {
|
||||
#[error("Invalid command: {0}")]
|
||||
|
||||
@@ -2,6 +2,7 @@ pub mod bootstrap;
|
||||
pub mod environment;
|
||||
pub mod hook;
|
||||
pub mod depssystem;
|
||||
pub mod depsuser;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum PrebakeStage {
|
||||
|
||||
@@ -1,12 +1,98 @@
|
||||
use crate::ExecutionContext;
|
||||
use crate::engine::{Engine, EventSender, ExecutionError, ExecutionResult};
|
||||
use crate::prebake::config::PrebakeConfig;
|
||||
use std::path::PathBuf;
|
||||
use crate::engine::{Engine, EventSender, pm};
|
||||
use crate::error::DependencyError;
|
||||
use crate::prebake::config::SystemDependency;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Handles system dependency installation for the target platform.
|
||||
///
|
||||
/// This function:
|
||||
/// - Detects the operating system and package manager
|
||||
/// - Optionally changes the package manager mirror
|
||||
/// - Optionally adds custom repositories
|
||||
/// - Updates the package index
|
||||
/// - Installs configured system packages
|
||||
pub async fn depssystem(
|
||||
config: &PrebakeConfig,
|
||||
config: &HashMap<String, SystemDependency>,
|
||||
ctx: &ExecutionContext,
|
||||
event_tx: &EventSender,
|
||||
) -> Result<ExecutionResult, ExecutionError> {
|
||||
todo!();
|
||||
) -> Result<(), DependencyError> {
|
||||
let osinfo = os_info::get();
|
||||
let engine = Engine::new();
|
||||
|
||||
// Detect package manager
|
||||
let package_manager = pm::detect(&osinfo).ok_or_else(|| {
|
||||
log::error!("No supported package manager found for {}", &osinfo);
|
||||
DependencyError::UnsupportedPlatform(osinfo.clone())
|
||||
})?;
|
||||
log::info!("Detected package manager: {}", package_manager.name());
|
||||
|
||||
// Retrieve configuration
|
||||
let Some(pm_config) = config.get(package_manager.name()) else {
|
||||
log::info!(
|
||||
"No configuration found for {}, skipping system dependencies",
|
||||
package_manager.name()
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Change mirror
|
||||
if let Some(mirror) = &pm_config.mirror {
|
||||
log::info!("Changing mirror to: {}", mirror);
|
||||
let cmd = package_manager.change_mirror(mirror, Some(&osinfo));
|
||||
log::debug!("Executing: {:?}", &cmd);
|
||||
engine
|
||||
.execute(cmd, &ctx, &event_tx)
|
||||
.await
|
||||
.map_err(|e| DependencyError::StageFailed {
|
||||
stage: "MirrorChange",
|
||||
source: e,
|
||||
})?;
|
||||
}
|
||||
|
||||
// Add custom repository
|
||||
if let Some(repos) = &pm_config.repositories {
|
||||
log::info!(
|
||||
"Adding {} custom repositories",
|
||||
repos.as_sequence().map(|s| s.len()).unwrap_or(0)
|
||||
);
|
||||
let cmds = package_manager.add_repository(repos, Some(&osinfo));
|
||||
for cmd in cmds {
|
||||
log::debug!("Executing: {:?}", &cmd);
|
||||
engine.execute(cmd, &ctx, &event_tx).await.map_err(|e| {
|
||||
DependencyError::StageFailed {
|
||||
stage: "CustomRepoAdd",
|
||||
source: e,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
// Update package index
|
||||
{
|
||||
log::info!("Updating package index...");
|
||||
let cmd = package_manager.update();
|
||||
log::debug!("Executing: {:?}", &cmd);
|
||||
engine
|
||||
.execute(cmd, &ctx, &event_tx)
|
||||
.await
|
||||
.map_err(|e| DependencyError::StageFailed {
|
||||
stage: "UpdateIndex",
|
||||
source: e,
|
||||
})?;
|
||||
}
|
||||
|
||||
// Install packages
|
||||
if !pm_config.packages.is_empty() {
|
||||
log::info!("Installing {} packages", pm_config.packages.len());
|
||||
engine
|
||||
.install_dependency(&pm_config.packages, &*package_manager, &ctx, &event_tx)
|
||||
.await
|
||||
.map_err(|e| DependencyError::StageFailed {
|
||||
stage: "InstallPackage",
|
||||
source: e,
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
use serde_yaml::Value;
|
||||
|
||||
use crate::ExecutionContext;
|
||||
use crate::engine::{Engine, EventSender, ExecutionError, ExecutionResult};
|
||||
use crate::prebake::config::PrebakeConfig;
|
||||
use std::path::PathBuf;
|
||||
use crate::error::DependencyError;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub async fn depsuser(
|
||||
config: &HashMap<String, Value>,
|
||||
ctx: &ExecutionContext,
|
||||
event_tx: &EventSender,
|
||||
) -> Result<(), DependencyError> {
|
||||
for (name, cfg) in config {
|
||||
let package_manager = upm::get(name).unwrap(); // ?
|
||||
|
||||
}
|
||||
todo!();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::engine::types::DeltaExecutionContext;
|
||||
use crate::ExecutionContext;
|
||||
use crate::engine::{Engine, EventSender, ExecutionError};
|
||||
use crate::types::command::CustomCommand;
|
||||
@@ -15,23 +16,16 @@ pub async fn hook(
|
||||
}
|
||||
};
|
||||
|
||||
let engine = Engine::new();
|
||||
for (index, hook_cmd) in hook.iter().enumerate() {
|
||||
let engine = Engine::new();
|
||||
let mut hook_ctx = ctx.clone();
|
||||
|
||||
// Update working directory if provided
|
||||
if let Some(working_dir) = &hook_cmd.working_dir {
|
||||
log::debug!("Updating working directory to {}", working_dir);
|
||||
hook_ctx.working_dir = PathBuf::from(working_dir);
|
||||
}
|
||||
// Update environment variables if provided
|
||||
if let Some(environment) = &hook_cmd.environment {
|
||||
hook_ctx.env_vars.extend(environment.clone());
|
||||
}
|
||||
log::info!("Executing hook index {}.", index);
|
||||
|
||||
let deltactx = DeltaExecutionContext {
|
||||
working_dir: hook_cmd.working_dir.as_ref().map(PathBuf::from),
|
||||
env_vars: hook_cmd.environment.clone().unwrap_or_default(),
|
||||
timeout: Some(std::time::Duration::from_secs(hook_cmd.timeout.into())),
|
||||
};
|
||||
log::info!("Executing hook #{}: {}.", index, hook_cmd.name);
|
||||
let result = engine
|
||||
.execute_command(&hook_cmd.command, &hook_ctx, &event_tx)
|
||||
.execute_command_with_delta(&hook_cmd.command, &ctx, &event_tx, &deltactx)
|
||||
.await;
|
||||
if let Err(e) = result {
|
||||
log::error!("Hook {} failed: {:?}", index, e);
|
||||
|
||||
@@ -5,7 +5,7 @@ pub struct CustomCommand {
|
||||
pub name: String,
|
||||
pub command: String,
|
||||
#[serde(default)]
|
||||
pub environment: Option<HashMap<String, String>>,
|
||||
pub environment: Option<HashMap<String, Option<String>>>,
|
||||
#[serde(default)]
|
||||
pub working_dir: Option<String>,
|
||||
#[serde(default = "default_timeout")]
|
||||
|
||||
Reference in New Issue
Block a user