mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-07 03:00:29 +00:00
chore: remove dead system dependency check infrastructure
Now that Graphviz is vendored, remove the DepSpec/probe_system_deps/ check_system_deps infrastructure from doctor.rs (empty since the vendoring), the no-op pre-flight check from install.rs, and the stale hardcoded "dot" check from demo diagnostics. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
829f2ae1d2
commit
3389f1d551
3 changed files with 2 additions and 233 deletions
|
|
@ -1,5 +1,4 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use fabro_api::types as api_types;
|
||||
|
|
@ -14,125 +13,12 @@ pub(crate) use fabro_util::check_report::{
|
|||
use fabro_util::printer::Printer;
|
||||
use fabro_util::terminal::Styles;
|
||||
use fabro_util::version::FABRO_VERSION;
|
||||
use regex::Regex;
|
||||
use semver::Version;
|
||||
use tokio::process::Command as TokioCommand;
|
||||
|
||||
use crate::args::{DoctorArgs, GlobalArgs};
|
||||
use crate::command_context::CommandContext;
|
||||
use crate::shared::print_json_pretty;
|
||||
use crate::user_config;
|
||||
|
||||
pub(crate) struct DepSpec {
|
||||
pub name: &'static str,
|
||||
command: &'static [&'static str],
|
||||
pub required: bool,
|
||||
pub min_version: Version,
|
||||
pattern: &'static LazyLock<Regex>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) enum ProbeOutcome {
|
||||
NotFound,
|
||||
Failed,
|
||||
Ok { version: Option<Version> },
|
||||
}
|
||||
|
||||
pub(crate) const DEP_SPECS: &[DepSpec] = &[];
|
||||
|
||||
fn parse_version(re: &Regex, output: &str) -> Option<Version> {
|
||||
let caps = re.captures(output)?;
|
||||
Some(Version::new(
|
||||
caps[1].parse().ok()?,
|
||||
caps[2].parse().ok()?,
|
||||
caps[3].parse().ok()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn probe_system_deps() -> Vec<ProbeOutcome> {
|
||||
let mut outcomes = Vec::with_capacity(DEP_SPECS.len());
|
||||
for spec in DEP_SPECS {
|
||||
let result = TokioCommand::new(spec.command[0])
|
||||
.args(&spec.command[1..])
|
||||
.output()
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let outcome = match result {
|
||||
None => ProbeOutcome::NotFound,
|
||||
Some(output) if !output.status.success() => ProbeOutcome::Failed,
|
||||
Some(output) => {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let version = parse_version(spec.pattern, &stdout)
|
||||
.or_else(|| parse_version(spec.pattern, &stderr));
|
||||
ProbeOutcome::Ok { version }
|
||||
}
|
||||
};
|
||||
outcomes.push(outcome);
|
||||
}
|
||||
outcomes
|
||||
}
|
||||
|
||||
fn dep_issue(name: &str, issue: &str, required: bool) -> (CheckStatus, String) {
|
||||
let severity = if required { "required" } else { "optional" };
|
||||
let status = if required {
|
||||
CheckStatus::Error
|
||||
} else {
|
||||
CheckStatus::Warning
|
||||
};
|
||||
(status, format!("{name}: {issue} ({severity})"))
|
||||
}
|
||||
|
||||
pub(crate) fn check_system_deps(specs: &[DepSpec], outcomes: &[ProbeOutcome]) -> CheckResult {
|
||||
let mut details = Vec::new();
|
||||
let mut worst_status = CheckStatus::Pass;
|
||||
|
||||
for (spec, outcome) in specs.iter().zip(outcomes) {
|
||||
let (status, text) = match outcome {
|
||||
ProbeOutcome::NotFound => dep_issue(spec.name, "not found", spec.required),
|
||||
ProbeOutcome::Failed => dep_issue(spec.name, "command failed", spec.required),
|
||||
ProbeOutcome::Ok { version: None } => {
|
||||
(CheckStatus::Pass, format!("{}: version unknown", spec.name))
|
||||
}
|
||||
ProbeOutcome::Ok {
|
||||
version: Some(version),
|
||||
} => {
|
||||
if version < &spec.min_version {
|
||||
(
|
||||
CheckStatus::Warning,
|
||||
format!("{}: {version} (minimum {})", spec.name, spec.min_version),
|
||||
)
|
||||
} else {
|
||||
(CheckStatus::Pass, format!("{}: {version}", spec.name))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
worst_status = worst_status.max(status);
|
||||
details.push(CheckDetail::new(text));
|
||||
}
|
||||
|
||||
let summary = match worst_status {
|
||||
CheckStatus::Pass => "all found".to_string(),
|
||||
CheckStatus::Warning => "some issues".to_string(),
|
||||
CheckStatus::Error => "missing required tools".to_string(),
|
||||
};
|
||||
|
||||
let remediation = match worst_status {
|
||||
CheckStatus::Pass => None,
|
||||
_ => Some("Install missing system dependencies".to_string()),
|
||||
};
|
||||
|
||||
CheckResult {
|
||||
name: "System dependencies".to_string(),
|
||||
status: worst_status,
|
||||
summary,
|
||||
details,
|
||||
remediation,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check_config(
|
||||
settings_path: Option<PathBuf>,
|
||||
legacy_paths: &[PathBuf],
|
||||
|
|
@ -678,90 +564,4 @@ mod tests {
|
|||
assert!(rendered.contains("Fabro Doctor"));
|
||||
assert!(rendered.contains("[✓] Configuration (loaded)"));
|
||||
}
|
||||
|
||||
static TEST_RE: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r"version (\d+)\.(\d+)\.(\d+)").unwrap());
|
||||
|
||||
fn spec(name: &'static str, required: bool, min_version: Version) -> DepSpec {
|
||||
DepSpec {
|
||||
name,
|
||||
command: &["echo", "unused"],
|
||||
required,
|
||||
min_version,
|
||||
pattern: &TEST_RE,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_system_deps_all_present() {
|
||||
let specs = [spec("dot", false, Version::new(2, 0, 0))];
|
||||
let outcomes = [ProbeOutcome::Ok {
|
||||
version: Some(Version::new(12, 2, 1)),
|
||||
}];
|
||||
let result = check_system_deps(&specs, &outcomes);
|
||||
assert_eq!(result.status, CheckStatus::Pass);
|
||||
assert_eq!(result.summary, "all found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_system_deps_required_missing_is_error() {
|
||||
let specs = [spec("required-tool", true, Version::new(3, 0, 0))];
|
||||
let outcomes = [ProbeOutcome::NotFound];
|
||||
let result = check_system_deps(&specs, &outcomes);
|
||||
assert_eq!(result.status, CheckStatus::Error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_system_deps_optional_missing_is_warning() {
|
||||
let specs = [spec("dot", false, Version::new(2, 0, 0))];
|
||||
let outcomes = [ProbeOutcome::NotFound];
|
||||
let result = check_system_deps(&specs, &outcomes);
|
||||
assert_eq!(result.status, CheckStatus::Warning);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_system_deps_outdated_is_warning() {
|
||||
let specs = [spec("required-tool", true, Version::new(3, 0, 0))];
|
||||
let outcomes = [ProbeOutcome::Ok {
|
||||
version: Some(Version::new(1, 1, 1)),
|
||||
}];
|
||||
let result = check_system_deps(&specs, &outcomes);
|
||||
assert_eq!(result.status, CheckStatus::Warning);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_system_deps_unparseable_success_is_pass() {
|
||||
let specs = [spec("required-tool", true, Version::new(3, 0, 0))];
|
||||
let outcomes = [ProbeOutcome::Ok { version: None }];
|
||||
let result = check_system_deps(&specs, &outcomes);
|
||||
assert_eq!(result.status, CheckStatus::Pass);
|
||||
assert!(result.details[0].text.contains("version unknown"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_system_deps_required_command_failed_is_error() {
|
||||
let specs = [spec("required-tool", true, Version::new(3, 0, 0))];
|
||||
let outcomes = [ProbeOutcome::Failed];
|
||||
let result = check_system_deps(&specs, &outcomes);
|
||||
assert_eq!(result.status, CheckStatus::Error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_system_deps_optional_command_failed_is_warning() {
|
||||
let specs = [spec("dot", false, Version::new(2, 0, 0))];
|
||||
let outcomes = [ProbeOutcome::Failed];
|
||||
let result = check_system_deps(&specs, &outcomes);
|
||||
assert_eq!(result.status, CheckStatus::Warning);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_system_deps_error_beats_warning() {
|
||||
let specs = [
|
||||
spec("required-tool", true, Version::new(3, 0, 0)),
|
||||
spec("dot", false, Version::new(2, 0, 0)),
|
||||
];
|
||||
let outcomes = [ProbeOutcome::NotFound, ProbeOutcome::NotFound];
|
||||
let result = check_system_deps(&specs, &outcomes);
|
||||
assert_eq!(result.status, CheckStatus::Error);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1304,33 +1304,8 @@ async fn run_install_inner(
|
|||
}
|
||||
}
|
||||
|
||||
// Pre-flight checks
|
||||
let facts = {
|
||||
let dep_outcomes = doctor::probe_system_deps().await;
|
||||
let dep_check = doctor::check_system_deps(doctor::DEP_SPECS, &dep_outcomes);
|
||||
|
||||
fabro_util::printerr!(
|
||||
printer,
|
||||
" {}",
|
||||
s.dim.apply_to("[Pre-flight] System dependency checks")
|
||||
);
|
||||
|
||||
if dep_check.status == doctor::CheckStatus::Error {
|
||||
fabro_util::printerr!(printer, " Missing required system dependencies:");
|
||||
for detail in &dep_check.details {
|
||||
fabro_util::printerr!(printer, " {}", detail.text);
|
||||
}
|
||||
bail!("Install missing required tools before running setup");
|
||||
}
|
||||
|
||||
for detail in &dep_check.details {
|
||||
fabro_util::printerr!(printer, " {}", detail.text);
|
||||
}
|
||||
fabro_util::printerr!(printer, "");
|
||||
|
||||
InstallFacts {
|
||||
codex_detected: detect_binary_on_path("codex").await,
|
||||
}
|
||||
let facts = InstallFacts {
|
||||
codex_detected: detect_binary_on_path("codex").await,
|
||||
};
|
||||
|
||||
// Step 1: LLM Providers
|
||||
|
|
|
|||
|
|
@ -396,12 +396,6 @@ pub(crate) async fn run_diagnostics(
|
|||
{ "name": "Brave Search", "status": "warning", "summary": "not configured", "details": [], "remediation": "Set BRAVE_SEARCH_API_KEY to enable web search" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "System",
|
||||
"checks": [
|
||||
{ "name": "dot", "status": "pass", "summary": "dot available", "details": [], "remediation": null }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Configuration",
|
||||
"checks": [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue