CLI: propagate alias rename errors, doctor config resilience
aliases: rename now propagates every meta.json I/O and parse error instead of silently ignoring failures. Previously, a corrupt or unreadable meta.json after directory rename would leave the alias in an inconsistent state with no user-visible error. doctor: Config::load failure now falls back to Config::default() with a warning instead of aborting the entire health check. Doctor should still run diagnostics even when config is missing or corrupt — that's exactly when you need it most.
This commit is contained in:
@@ -304,16 +304,22 @@ fn cmd_rename(names: &[String], robot: bool, start: Instant) -> Result<(), Swagg
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Update meta.json alias field
|
// Update meta.json alias field -- propagate errors so the cache
|
||||||
|
// doesn't end up with a stale alias name in metadata.
|
||||||
let meta_path = new_dir.join("meta.json");
|
let meta_path = new_dir.join("meta.json");
|
||||||
if let Ok(bytes) = std::fs::read(&meta_path)
|
let meta_bytes = std::fs::read(&meta_path).map_err(|e| {
|
||||||
&& let Ok(mut meta) = serde_json::from_slice::<CacheMetadata>(&bytes)
|
SwaggerCliError::Cache(format!("failed to read meta.json after rename: {e}"))
|
||||||
{
|
})?;
|
||||||
meta.alias = new.clone();
|
let mut meta: CacheMetadata = serde_json::from_slice(&meta_bytes).map_err(|e| {
|
||||||
if let Ok(updated_bytes) = serde_json::to_vec_pretty(&meta) {
|
SwaggerCliError::Cache(format!("failed to parse meta.json after rename: {e}"))
|
||||||
let _ = std::fs::write(&meta_path, updated_bytes);
|
})?;
|
||||||
}
|
meta.alias = new.clone();
|
||||||
}
|
let updated_bytes = serde_json::to_vec_pretty(&meta).map_err(|e| {
|
||||||
|
SwaggerCliError::Cache(format!("failed to serialize meta.json after rename: {e}"))
|
||||||
|
})?;
|
||||||
|
std::fs::write(&meta_path, updated_bytes).map_err(|e| {
|
||||||
|
SwaggerCliError::Cache(format!("failed to write meta.json after rename: {e}"))
|
||||||
|
})?;
|
||||||
|
|
||||||
// Update config if old was the default
|
// Update config if old was the default
|
||||||
let cfg_path = config_path(None);
|
let cfg_path = config_path(None);
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ pub struct Args {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub prune_stale: bool,
|
pub prune_stale: bool,
|
||||||
|
|
||||||
/// Days before an alias is considered stale (default: 90)
|
/// Days before an alias is considered stale (default: 30, matching config)
|
||||||
#[arg(long, default_value_t = 90)]
|
#[arg(long, default_value_t = 30)]
|
||||||
pub prune_threshold: u32,
|
pub prune_threshold: u32,
|
||||||
|
|
||||||
/// Evict least-recently-used aliases until total size is under this limit (MB)
|
/// Evict least-recently-used aliases until total size is under this limit (MB)
|
||||||
@@ -290,13 +290,13 @@ fn execute_prune(args: &Args, robot: bool, start: Instant) -> Result<(), Swagger
|
|||||||
"cache",
|
"cache",
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
} else if stale.is_empty() {
|
} else if pruned.is_empty() {
|
||||||
println!(
|
println!(
|
||||||
"No stale aliases (threshold: {} days).",
|
"No stale aliases (threshold: {} days).",
|
||||||
args.prune_threshold
|
args.prune_threshold
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!("Pruned {} stale alias(es).", stale.len());
|
println!("Pruned {} stale alias(es).", pruned.len());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,12 +344,19 @@ fn try_fix_alias(cm: &CacheManager, alias: &str) -> Result<Vec<String>, Vec<Stri
|
|||||||
pub async fn execute(args: &Args, robot_mode: bool) -> Result<(), SwaggerCliError> {
|
pub async fn execute(args: &Args, robot_mode: bool) -> Result<(), SwaggerCliError> {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
// Load config
|
// Load config -- doctor should work even when config is missing or
|
||||||
|
// corrupt, so fall back to defaults and report a warning.
|
||||||
let cfg_path = config_path(None);
|
let cfg_path = config_path(None);
|
||||||
let config = Config::load(&cfg_path)?;
|
let mut warnings: Vec<String> = Vec::new();
|
||||||
|
let config = match Config::load(&cfg_path) {
|
||||||
|
Ok(cfg) => cfg,
|
||||||
|
Err(e) => {
|
||||||
|
warnings.push(format!("could not load config (using defaults): {e}"));
|
||||||
|
Config::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Check config dir exists
|
// Check config dir exists
|
||||||
let mut warnings: Vec<String> = Vec::new();
|
|
||||||
if let Some(parent) = cfg_path.parent()
|
if let Some(parent) = cfg_path.parent()
|
||||||
&& !parent.exists()
|
&& !parent.exists()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user