Add --proxy and --no-proxy global flags to control HTTP/HTTPS proxy usage for all network operations (SDK installs, FTC SDK clone/fetch, Android SDK download). Proxy resolution priority: 1. --no-proxy → go direct, ignore everything 2. --proxy <url> → use the specified proxy 3. HTTPS_PROXY / HTTP_PROXY env vars (auto-detected) 4. Nothing → go direct Key implementation details: - reqwest client is always built through ProxyConfig::client() rather than Client::new(), so --no-proxy actively suppresses env-var auto-detection instead of just being a no-op. - git2/libgit2 has its own HTTP transport that doesn't use reqwest. GitProxyGuard is an RAII guard that temporarily sets/clears the HTTPS_PROXY env vars around clone and fetch operations, then restores the previous state on drop. This avoids mutating ~/.gitconfig. - Gradle wrapper reads HTTPS_PROXY natively; no programmatic intervention needed. - All network failure paths now print offline/air-gapped installation instructions automatically, covering manual SDK installs and Gradle distribution download. Closes: v1.1.0 proxy support milestone
115 lines
3.3 KiB
Rust
115 lines
3.3 KiB
Rust
use std::path::{Path, PathBuf};
|
|
use anyhow::{Result, Context, bail};
|
|
use std::fs;
|
|
use colored::*;
|
|
|
|
pub mod android;
|
|
pub mod ftc;
|
|
pub mod gradle;
|
|
pub mod proxy;
|
|
|
|
pub struct SdkConfig {
|
|
pub ftc_sdk_path: PathBuf,
|
|
pub android_sdk_path: PathBuf,
|
|
pub cache_dir: PathBuf,
|
|
}
|
|
|
|
impl SdkConfig {
|
|
pub fn new() -> Result<Self> {
|
|
// Allow tests (or power users) to override the cache directory.
|
|
// When WEEVIL_HOME is set, we also skip the system Android SDK
|
|
// search so tests are fully isolated.
|
|
let (cache_dir, android_sdk_path) = if let Ok(weevil_home) = std::env::var("WEEVIL_HOME") {
|
|
let cache = PathBuf::from(weevil_home);
|
|
let android = cache.join("android-sdk");
|
|
(cache, android)
|
|
} else {
|
|
let home = dirs::home_dir()
|
|
.context("Could not determine home directory")?;
|
|
let cache = home.join(".weevil");
|
|
let android = Self::find_android_sdk().unwrap_or_else(|| cache.join("android-sdk"));
|
|
(cache, android)
|
|
};
|
|
|
|
fs::create_dir_all(&cache_dir)?;
|
|
|
|
Ok(Self {
|
|
ftc_sdk_path: cache_dir.join("ftc-sdk"),
|
|
android_sdk_path,
|
|
cache_dir,
|
|
})
|
|
}
|
|
|
|
pub fn with_paths(ftc_sdk: Option<&str>, android_sdk: Option<&str>) -> Result<Self> {
|
|
let mut config = Self::new()?;
|
|
|
|
if let Some(path) = ftc_sdk {
|
|
config.ftc_sdk_path = PathBuf::from(path);
|
|
}
|
|
|
|
if let Some(path) = android_sdk {
|
|
config.android_sdk_path = PathBuf::from(path);
|
|
}
|
|
|
|
Ok(config)
|
|
}
|
|
|
|
fn find_android_sdk() -> Option<PathBuf> {
|
|
// Check common locations
|
|
let home = dirs::home_dir()?;
|
|
|
|
let candidates = vec![
|
|
home.join("Android/Sdk"),
|
|
home.join(".android-sdk"),
|
|
PathBuf::from("/usr/lib/android-sdk"),
|
|
];
|
|
|
|
for candidate in candidates {
|
|
if candidate.join("platform-tools").exists() {
|
|
return Some(candidate);
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn validate(&self) -> Result<()> {
|
|
if !self.ftc_sdk_path.exists() {
|
|
bail!(
|
|
"FTC SDK not found at: {}\nRun: weevil sdk install",
|
|
self.ftc_sdk_path.display()
|
|
);
|
|
}
|
|
|
|
if !self.android_sdk_path.exists() {
|
|
bail!(
|
|
"Android SDK not found at: {}\nRun: weevil sdk install",
|
|
self.android_sdk_path.display()
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn print_status(&self) {
|
|
println!("{}", "SDK Configuration:".bright_yellow().bold());
|
|
println!();
|
|
|
|
self.print_sdk_status("FTC SDK", &self.ftc_sdk_path);
|
|
self.print_sdk_status("Android SDK", &self.android_sdk_path);
|
|
|
|
println!();
|
|
println!("{}: {}", "Cache Directory".bright_yellow(), self.cache_dir.display());
|
|
}
|
|
|
|
fn print_sdk_status(&self, name: &str, path: &Path) {
|
|
let status = if path.exists() {
|
|
format!("{} {}", "✓".green(), path.display())
|
|
} else {
|
|
format!("{} {} {}", "✗".red(), path.display(), "(not found)".red())
|
|
};
|
|
|
|
println!("{}: {}", name.bright_yellow(), status);
|
|
}
|
|
} |