feat: Improve service manager testing and error handling
- Add comprehensive testing instructions to README. - Improve error handling in examples to prevent crashes. - Enhance launchctl error handling for production safety. - Improve zinit error handling for production safety. - Remove obsolete plan_to_fix.md file. - Update Rhai integration tests for improved robustness. - Improve service manager creation on Linux with systemd fallback.
This commit is contained in:
@@ -6,10 +6,25 @@ use std::path::PathBuf;
|
||||
use tokio::process::Command;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
// Shared runtime for async operations
|
||||
static ASYNC_RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
||||
Runtime::new().expect("Failed to create async runtime for LaunchctlServiceManager")
|
||||
});
|
||||
// Shared runtime for async operations - production-safe initialization
|
||||
static ASYNC_RUNTIME: Lazy<Option<Runtime>> = Lazy::new(|| Runtime::new().ok());
|
||||
|
||||
/// Get the async runtime, creating a temporary one if the static runtime failed
|
||||
fn get_runtime() -> Result<Runtime, ServiceManagerError> {
|
||||
// Try to use the static runtime first
|
||||
if let Some(_runtime) = ASYNC_RUNTIME.as_ref() {
|
||||
// We can't return a reference to the static runtime because we need ownership
|
||||
// for block_on, so we create a new one. This is a reasonable trade-off for safety.
|
||||
Runtime::new().map_err(|e| {
|
||||
ServiceManagerError::Other(format!("Failed to create async runtime: {}", e))
|
||||
})
|
||||
} else {
|
||||
// Static runtime failed, try to create a new one
|
||||
Runtime::new().map_err(|e| {
|
||||
ServiceManagerError::Other(format!("Failed to create async runtime: {}", e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LaunchctlServiceManager {
|
||||
@@ -221,8 +236,9 @@ impl ServiceManager for LaunchctlServiceManager {
|
||||
}
|
||||
|
||||
fn start(&self, config: &ServiceConfig) -> Result<(), ServiceManagerError> {
|
||||
// Use the shared runtime for async operations
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
// Use production-safe runtime for async operations
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
let label = self.get_service_label(&config.name);
|
||||
|
||||
// Check if service is already loaded
|
||||
@@ -249,7 +265,8 @@ impl ServiceManager for LaunchctlServiceManager {
|
||||
}
|
||||
|
||||
fn start_existing(&self, service_name: &str) -> Result<(), ServiceManagerError> {
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
let label = self.get_service_label(service_name);
|
||||
let plist_path = self.get_plist_path(service_name);
|
||||
|
||||
@@ -300,8 +317,9 @@ impl ServiceManager for LaunchctlServiceManager {
|
||||
// First start the service
|
||||
self.start(config)?;
|
||||
|
||||
// Then wait for confirmation using the shared runtime
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
// Then wait for confirmation using production-safe runtime
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
self.wait_for_service_status(&config.name, timeout_secs)
|
||||
.await
|
||||
})
|
||||
@@ -315,15 +333,17 @@ impl ServiceManager for LaunchctlServiceManager {
|
||||
// First start the existing service
|
||||
self.start_existing(service_name)?;
|
||||
|
||||
// Then wait for confirmation using the shared runtime
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
// Then wait for confirmation using production-safe runtime
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
self.wait_for_service_status(service_name, timeout_secs)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
fn stop(&self, service_name: &str) -> Result<(), ServiceManagerError> {
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
let _label = self.get_service_label(service_name);
|
||||
let plist_path = self.get_plist_path(service_name);
|
||||
|
||||
@@ -359,7 +379,8 @@ impl ServiceManager for LaunchctlServiceManager {
|
||||
}
|
||||
|
||||
fn status(&self, service_name: &str) -> Result<ServiceStatus, ServiceManagerError> {
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
let label = self.get_service_label(service_name);
|
||||
let plist_path = self.get_plist_path(service_name);
|
||||
|
||||
@@ -397,7 +418,8 @@ impl ServiceManager for LaunchctlServiceManager {
|
||||
service_name: &str,
|
||||
lines: Option<usize>,
|
||||
) -> Result<String, ServiceManagerError> {
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
let log_path = self.get_log_path(service_name);
|
||||
|
||||
if !log_path.exists() {
|
||||
@@ -421,7 +443,8 @@ impl ServiceManager for LaunchctlServiceManager {
|
||||
}
|
||||
|
||||
fn list(&self) -> Result<Vec<String>, ServiceManagerError> {
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
let list_output = self.run_launchctl(&["list"]).await?;
|
||||
|
||||
let services: Vec<String> = list_output
|
||||
@@ -456,8 +479,9 @@ impl ServiceManager for LaunchctlServiceManager {
|
||||
);
|
||||
}
|
||||
|
||||
// Remove the plist file using the shared runtime
|
||||
ASYNC_RUNTIME.block_on(async {
|
||||
// Remove the plist file using production-safe runtime
|
||||
let runtime = get_runtime()?;
|
||||
runtime.block_on(async {
|
||||
let plist_path = self.get_plist_path(service_name);
|
||||
if plist_path.exists() {
|
||||
tokio::fs::remove_file(&plist_path).await?;
|
||||
|
Reference in New Issue
Block a user