Adds `weevil doctor` to check development environment health. Reports status of Java, FTC SDK, Android SDK, ADB, and Gradle. Provides clear next steps based on system state.
267 lines
8.4 KiB
Rust
267 lines
8.4 KiB
Rust
use anyhow::Result;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
use colored::*;
|
|
|
|
use crate::sdk::SdkConfig;
|
|
|
|
#[derive(Debug)]
|
|
pub struct SystemHealth {
|
|
pub java_ok: bool,
|
|
pub java_version: Option<String>,
|
|
pub ftc_sdk_ok: bool,
|
|
pub ftc_sdk_version: Option<String>,
|
|
pub android_sdk_ok: bool,
|
|
pub adb_ok: bool,
|
|
pub adb_version: Option<String>,
|
|
pub gradle_ok: bool,
|
|
pub gradle_version: Option<String>,
|
|
}
|
|
|
|
impl SystemHealth {
|
|
pub fn is_healthy(&self) -> bool {
|
|
// Required: Java, FTC SDK, Android SDK
|
|
// Optional: ADB in PATH (can be in Android SDK), Gradle (projects have wrapper)
|
|
self.java_ok && self.ftc_sdk_ok && self.android_sdk_ok
|
|
}
|
|
}
|
|
|
|
/// Run system diagnostics and report health status
|
|
pub fn run_diagnostics() -> Result<()> {
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
println!("{}", " 🩺 Weevil Doctor - System Diagnostics".bright_cyan().bold());
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
println!();
|
|
|
|
let health = check_system_health()?;
|
|
print_diagnostics(&health);
|
|
|
|
println!();
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
|
|
if health.is_healthy() {
|
|
println!("{}", " ✓ System is healthy and ready for FTC development".bright_green().bold());
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
println!();
|
|
println!("{}", "You can now:".bright_yellow().bold());
|
|
println!(" - Create a new project: {}", "weevil new <project-name>".bright_cyan());
|
|
println!(" - Setup a cloned project: {}", "weevil setup <project-path>".bright_cyan());
|
|
} else {
|
|
println!("{}", " ⚠ Issues found - setup required".bright_yellow().bold());
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
println!();
|
|
println!("{}", "To fix issues, run:".bright_yellow().bold());
|
|
println!(" {}", "weevil setup".bright_cyan());
|
|
}
|
|
|
|
println!();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Check system health and return a report
|
|
pub fn check_system_health() -> Result<SystemHealth> {
|
|
let sdk_config = SdkConfig::new()?;
|
|
|
|
// Check Java
|
|
let (java_ok, java_version) = match check_java() {
|
|
Ok(version) => (true, Some(version)),
|
|
Err(_) => (false, None),
|
|
};
|
|
|
|
// Check FTC SDK
|
|
let (ftc_sdk_ok, ftc_sdk_version) = if sdk_config.ftc_sdk_path.exists() {
|
|
match crate::sdk::ftc::verify(&sdk_config.ftc_sdk_path) {
|
|
Ok(_) => {
|
|
let version = crate::sdk::ftc::get_version(&sdk_config.ftc_sdk_path)
|
|
.unwrap_or_else(|_| "unknown".to_string());
|
|
(true, Some(version))
|
|
}
|
|
Err(_) => (false, None),
|
|
}
|
|
} else {
|
|
(false, None)
|
|
};
|
|
|
|
// Check Android SDK
|
|
let android_sdk_ok = if sdk_config.android_sdk_path.exists() {
|
|
crate::sdk::android::verify(&sdk_config.android_sdk_path).is_ok()
|
|
} else {
|
|
false
|
|
};
|
|
|
|
// Check ADB
|
|
let (adb_ok, adb_version) = match check_adb(&sdk_config.android_sdk_path) {
|
|
Ok(version) => (true, Some(version)),
|
|
Err(_) => (false, None),
|
|
};
|
|
|
|
// Check Gradle (optional)
|
|
let (gradle_ok, gradle_version) = match check_gradle() {
|
|
Ok(version) => (true, Some(version)),
|
|
Err(_) => (false, None),
|
|
};
|
|
|
|
Ok(SystemHealth {
|
|
java_ok,
|
|
java_version,
|
|
ftc_sdk_ok,
|
|
ftc_sdk_version,
|
|
android_sdk_ok,
|
|
adb_ok,
|
|
adb_version,
|
|
gradle_ok,
|
|
gradle_version,
|
|
})
|
|
}
|
|
|
|
fn print_diagnostics(health: &SystemHealth) {
|
|
let sdk_config = SdkConfig::new().unwrap();
|
|
|
|
println!("{}", "Required Components:".bright_yellow().bold());
|
|
println!();
|
|
|
|
// Java
|
|
if health.java_ok {
|
|
println!(" {} Java JDK {}",
|
|
"✓".green(),
|
|
health.java_version.as_ref().unwrap()
|
|
);
|
|
} else {
|
|
println!(" {} Java JDK {}",
|
|
"✗".red(),
|
|
"not found".red()
|
|
);
|
|
}
|
|
|
|
// FTC SDK
|
|
if health.ftc_sdk_ok {
|
|
println!(" {} FTC SDK {} at {}",
|
|
"✓".green(),
|
|
health.ftc_sdk_version.as_ref().unwrap(),
|
|
sdk_config.ftc_sdk_path.display()
|
|
);
|
|
} else {
|
|
println!(" {} FTC SDK {} (expected at {})",
|
|
"✗".red(),
|
|
"not found".red(),
|
|
sdk_config.ftc_sdk_path.display()
|
|
);
|
|
}
|
|
|
|
// Android SDK
|
|
if health.android_sdk_ok {
|
|
println!(" {} Android SDK at {}",
|
|
"✓".green(),
|
|
sdk_config.android_sdk_path.display()
|
|
);
|
|
} else {
|
|
println!(" {} Android SDK {} (expected at {})",
|
|
"✗".red(),
|
|
"not found".red(),
|
|
sdk_config.android_sdk_path.display()
|
|
);
|
|
}
|
|
|
|
println!();
|
|
println!("{}", "Optional Components:".bright_yellow().bold());
|
|
println!();
|
|
|
|
// ADB
|
|
if health.adb_ok {
|
|
println!(" {} ADB {}",
|
|
"✓".green(),
|
|
health.adb_version.as_ref().unwrap()
|
|
);
|
|
} else {
|
|
println!(" {} ADB {}",
|
|
"⚠".yellow(),
|
|
"not in PATH (included in Android SDK)".yellow()
|
|
);
|
|
}
|
|
|
|
// Gradle
|
|
if health.gradle_ok {
|
|
println!(" {} Gradle {}",
|
|
"✓".green(),
|
|
health.gradle_version.as_ref().unwrap()
|
|
);
|
|
} else {
|
|
println!(" {} Gradle {}",
|
|
"⚠".yellow(),
|
|
"not in PATH (projects include wrapper)".yellow()
|
|
);
|
|
}
|
|
}
|
|
|
|
fn check_java() -> Result<String> {
|
|
let output = Command::new("java")
|
|
.arg("-version")
|
|
.output();
|
|
|
|
match output {
|
|
Ok(out) => {
|
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
for line in stderr.lines() {
|
|
if line.contains("version") {
|
|
if let Some(version_str) = line.split('"').nth(1) {
|
|
return Ok(version_str.to_string());
|
|
}
|
|
}
|
|
}
|
|
Ok("installed (version unknown)".to_string())
|
|
}
|
|
Err(_) => anyhow::bail!("Java JDK not found in PATH"),
|
|
}
|
|
}
|
|
|
|
fn check_adb(android_sdk_path: &Path) -> Result<String> {
|
|
// First try system PATH
|
|
let output = Command::new("adb")
|
|
.arg("version")
|
|
.output();
|
|
|
|
if let Ok(out) = output {
|
|
if out.status.success() {
|
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
for line in stdout.lines() {
|
|
if line.starts_with("Android Debug Bridge version") {
|
|
return Ok(line.replace("Android Debug Bridge version ", ""));
|
|
}
|
|
}
|
|
return Ok("installed (version unknown)".to_string());
|
|
}
|
|
}
|
|
|
|
// Try Android SDK location
|
|
let adb_path = if cfg!(target_os = "windows") {
|
|
android_sdk_path.join("platform-tools").join("adb.exe")
|
|
} else {
|
|
android_sdk_path.join("platform-tools").join("adb")
|
|
};
|
|
|
|
if adb_path.exists() {
|
|
anyhow::bail!("ADB found in Android SDK but not in PATH")
|
|
} else {
|
|
anyhow::bail!("ADB not found")
|
|
}
|
|
}
|
|
|
|
fn check_gradle() -> Result<String> {
|
|
let output = Command::new("gradle")
|
|
.arg("--version")
|
|
.output();
|
|
|
|
match output {
|
|
Ok(out) => {
|
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
for line in stdout.lines() {
|
|
if line.starts_with("Gradle") {
|
|
return Ok(line.replace("Gradle ", ""));
|
|
}
|
|
}
|
|
Ok("installed (version unknown)".to_string())
|
|
}
|
|
Err(_) => anyhow::bail!("Gradle not found in PATH"),
|
|
}
|
|
} |