105 lines
3.5 KiB
Rust
105 lines
3.5 KiB
Rust
//! doctor command: render the environment report, optionally with --fix.
|
|
|
|
use super::*;
|
|
use crate::doctor::{self, Status};
|
|
use crate::events::{Event, EventKind};
|
|
use crate::output::{boxed_table, print_json, status_style};
|
|
use crate::runner::SystemRunner;
|
|
use anyhow::Result;
|
|
|
|
pub fn run(app: &App, fix: bool, watch: Option<u64>) -> Result<i32> {
|
|
if let Some(interval) = watch {
|
|
return watch_loop(app, interval.max(1));
|
|
}
|
|
if fix {
|
|
let repaired = doctor::fix(app)?;
|
|
for r in &repaired {
|
|
app.log.success(r);
|
|
}
|
|
let runner = SystemRunner::new(false, app.cli.verbose, &app.log);
|
|
for r in doctor::install_missing_tools(app, &runner)? {
|
|
app.log.success(r);
|
|
}
|
|
app.log.info("run 'am doctor' again to verify");
|
|
return Ok(0);
|
|
}
|
|
// The doctor report probes real tools, even under --dry-run.
|
|
let runner = SystemRunner::new(false, false, &app.log);
|
|
let report = doctor::run(app, &runner);
|
|
if !app.dry_run() {
|
|
app.emit(
|
|
&Event::now(EventKind::Doctor)
|
|
.with_exit_code(if report.ok() { 0 } else { 1 }),
|
|
);
|
|
}
|
|
if app.json() {
|
|
print_json(&report.checks);
|
|
return Ok(if report.ok() { 0 } else { 1 });
|
|
}
|
|
let color = app.color();
|
|
let theme = app.theme();
|
|
let mut table_rows = Vec::new();
|
|
for c in &report.checks {
|
|
let status = match c.status {
|
|
Status::Ok => status_style("ok", theme, color),
|
|
Status::Warn => status_style("warn", theme, color),
|
|
Status::Error => status_style("error", theme, color),
|
|
};
|
|
table_rows.push(vec![c.name.clone(), status, c.detail.clone()]);
|
|
}
|
|
print!(
|
|
"{}",
|
|
boxed_table(
|
|
vec!["CHECK", "STATUS", "DETAIL"],
|
|
table_rows,
|
|
theme,
|
|
color,
|
|
)
|
|
);
|
|
if report.problems() > 0 {
|
|
app.log
|
|
.warn(&format!("{} problem(s) found — 'am doctor --fix' repairs what it can", report.problems()));
|
|
} else {
|
|
app.log.success("environment looks good");
|
|
}
|
|
Ok(if report.ok() { 0 } else { 1 })
|
|
}
|
|
|
|
/// Issue #59: re-run the doctor checks every N seconds; alert (desktop
|
|
/// notification + event) on the first failure, recover silently. Clean
|
|
/// Ctrl-C via ctrlc.
|
|
fn watch_loop(app: &App, interval: u64) -> Result<i32> {
|
|
app.log
|
|
.info(&format!("watching every {interval}s (Ctrl-C to stop)"));
|
|
let mut was_ok = true;
|
|
loop {
|
|
let runner = SystemRunner::new(false, false, &app.log);
|
|
let report = doctor::run(app, &runner);
|
|
let ok = report.ok();
|
|
if !ok && was_ok {
|
|
// First failure: alert.
|
|
app.emit(
|
|
&Event::now(EventKind::Doctor)
|
|
.with_exit_code(1)
|
|
.with_reason(format!("{} problem(s) found", report.problems())),
|
|
);
|
|
app.log.error(&format!(
|
|
"{} problem(s) found — 'am doctor --fix' repairs what it can",
|
|
report.problems()
|
|
));
|
|
let _ = crate::process::desktop_notify(
|
|
"agent-manager",
|
|
&format!("doctor: {} problem(s)", report.problems()),
|
|
);
|
|
was_ok = false;
|
|
} else if ok && !was_ok {
|
|
app.log.success("environment recovered");
|
|
was_ok = true;
|
|
} else if !ok {
|
|
app.log
|
|
.warn(&format!("still {} problem(s)", report.problems()));
|
|
}
|
|
std::thread::sleep(std::time::Duration::from_secs(interval));
|
|
}
|
|
}
|