fix(cli): improve enter/stop error context

- Stop now takes the store lock like other mutating commands\n- Wrap enter/exec/stop errors with env input and resolved id context\n- Enter/exec now return a clearer error when the env is already running or not built
This commit is contained in:
Marco Allegretti 2026-02-25 18:51:14 +01:00
parent 8feee8048c
commit cf8ed5ba67
4 changed files with 44 additions and 9 deletions

View file

@ -13,10 +13,15 @@ pub fn run(
let _lock = StoreLock::acquire(&layout.lock_file()).map_err(|e| format!("store lock: {e}"))?; let _lock = StoreLock::acquire(&layout.lock_file()).map_err(|e| format!("store lock: {e}"))?;
let resolved = resolve_env_id_pretty(engine, env_id)?; let resolved = resolve_env_id_pretty(engine, env_id)?;
let short = resolved.get(..12).unwrap_or(&resolved);
if command.is_empty() { if command.is_empty() {
engine.enter(&resolved).map_err(|e| e.to_string())?; engine
.enter(&resolved)
.map_err(|e| format!("{e} (env '{env_id}' -> {short})"))?;
} else { } else {
engine.exec(&resolved, command).map_err(|e| e.to_string())?; engine
.exec(&resolved, command)
.map_err(|e| format!("{e} (env '{env_id}' -> {short})"))?;
} }
Ok(EXIT_SUCCESS) Ok(EXIT_SUCCESS)
} }

View file

@ -14,6 +14,9 @@ pub fn run(
let _lock = StoreLock::acquire(&layout.lock_file()).map_err(|e| format!("store lock: {e}"))?; let _lock = StoreLock::acquire(&layout.lock_file()).map_err(|e| format!("store lock: {e}"))?;
let resolved = resolve_env_id_pretty(engine, env_id)?; let resolved = resolve_env_id_pretty(engine, env_id)?;
engine.exec(&resolved, command).map_err(|e| e.to_string())?; let short = resolved.get(..12).unwrap_or(&resolved);
engine
.exec(&resolved, command)
.map_err(|e| format!("{e} (env '{env_id}' -> {short})"))?;
Ok(EXIT_SUCCESS) Ok(EXIT_SUCCESS)
} }

View file

@ -1,10 +1,17 @@
use super::{resolve_env_id_pretty, EXIT_SUCCESS}; use super::{resolve_env_id_pretty, EXIT_SUCCESS};
use karapace_core::Engine; use karapace_core::{Engine, StoreLock};
use karapace_store::StoreLayout;
use std::path::Path; use std::path::Path;
pub fn run(engine: &Engine, _store_path: &Path, env_id: &str) -> Result<u8, String> { pub fn run(engine: &Engine, store_path: &Path, env_id: &str) -> Result<u8, String> {
let layout = StoreLayout::new(store_path);
let _lock = StoreLock::acquire(&layout.lock_file()).map_err(|e| format!("store lock: {e}"))?;
let resolved = resolve_env_id_pretty(engine, env_id)?; let resolved = resolve_env_id_pretty(engine, env_id)?;
engine.stop(&resolved).map_err(|e| e.to_string())?; let short = resolved.get(..12).unwrap_or(&resolved);
println!("stopped environment {env_id}"); engine
.stop(&resolved)
.map_err(|e| format!("{e} (env '{env_id}' -> {short})"))?;
println!("stopped environment {env_id} ({short})");
Ok(EXIT_SUCCESS) Ok(EXIT_SUCCESS)
} }

View file

@ -378,7 +378,17 @@ impl Engine {
.get(env_id) .get(env_id)
.map_err(|_| CoreError::EnvNotFound(env_id.to_owned()))?; .map_err(|_| CoreError::EnvNotFound(env_id.to_owned()))?;
validate_transition(meta.state, EnvState::Running)?; if meta.state == EnvState::Running {
return Err(CoreError::Runtime(
karapace_runtime::RuntimeError::AlreadyRunning(env_id.to_owned()),
));
}
if meta.state != EnvState::Built {
return Err(CoreError::InvalidTransition {
from: meta.state.to_string(),
to: "enter requires built state".to_owned(),
});
}
let normalized = self.load_manifest(&meta.manifest_hash)?; let normalized = self.load_manifest(&meta.manifest_hash)?;
let store_str = self.store_root_str.clone(); let store_str = self.store_root_str.clone();
@ -415,7 +425,17 @@ impl Engine {
.get(env_id) .get(env_id)
.map_err(|_| CoreError::EnvNotFound(env_id.to_owned()))?; .map_err(|_| CoreError::EnvNotFound(env_id.to_owned()))?;
validate_transition(meta.state, EnvState::Running)?; if meta.state == EnvState::Running {
return Err(CoreError::Runtime(
karapace_runtime::RuntimeError::AlreadyRunning(env_id.to_owned()),
));
}
if meta.state != EnvState::Built {
return Err(CoreError::InvalidTransition {
from: meta.state.to_string(),
to: "exec requires built state".to_owned(),
});
}
let normalized = self.load_manifest(&meta.manifest_hash)?; let normalized = self.load_manifest(&meta.manifest_hash)?;
let store_str = self.store_root_str.clone(); let store_str = self.store_root_str.clone();