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
186 lines
5.5 KiB
Rust
186 lines
5.5 KiB
Rust
use clap::{Parser, Subcommand};
|
|
use colored::*;
|
|
use anyhow::Result;
|
|
use weevil::version::WEEVIL_VERSION;
|
|
|
|
// Import ProxyConfig through our own `mod sdk`, not through the `weevil`
|
|
// library crate. Both re-export the same source, but Rust treats
|
|
// `weevil::sdk::proxy::ProxyConfig` and `sdk::proxy::ProxyConfig` as
|
|
// distinct types when a binary and its lib are compiled together.
|
|
// The command modules already see the local-mod version, so main must match.
|
|
|
|
mod commands;
|
|
mod sdk;
|
|
mod project;
|
|
mod templates;
|
|
|
|
use sdk::proxy::ProxyConfig;
|
|
|
|
#[derive(Parser)]
|
|
#[command(name = "weevil")]
|
|
#[command(author = "Eric Ratliff <eric@nxlearn.net>")]
|
|
#[command(version = WEEVIL_VERSION)]
|
|
#[command(
|
|
about = "FTC robotics project generator - bores into complexity, emerges with clean code",
|
|
long_about = None
|
|
)]
|
|
struct Cli {
|
|
/// Use this HTTP/HTTPS proxy for all downloads
|
|
#[arg(long, value_name = "URL", global = true)]
|
|
proxy: Option<String>,
|
|
|
|
/// Skip proxy entirely — go direct even if HTTPS_PROXY is set
|
|
#[arg(long, global = true)]
|
|
no_proxy: bool,
|
|
|
|
#[command(subcommand)]
|
|
command: Commands,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Commands {
|
|
/// Create a new FTC robot project
|
|
New {
|
|
/// Name of the robot project
|
|
name: String,
|
|
|
|
/// Path to FTC SDK (optional, will auto-detect or download)
|
|
#[arg(long)]
|
|
ftc_sdk: Option<String>,
|
|
|
|
/// Path to Android SDK (optional, will auto-detect or download)
|
|
#[arg(long)]
|
|
android_sdk: Option<String>,
|
|
},
|
|
|
|
/// Check system health and diagnose issues
|
|
Doctor,
|
|
|
|
/// Setup development environment (system or project)
|
|
Setup {
|
|
/// Path to project directory (optional - without it, sets up system)
|
|
path: Option<String>,
|
|
},
|
|
|
|
/// Remove Weevil-installed SDKs and dependencies
|
|
Uninstall {
|
|
/// Show what would be removed without actually removing anything
|
|
#[arg(long)]
|
|
dry_run: bool,
|
|
|
|
/// Remove only specific items by number (use --dry-run first to see the list)
|
|
#[arg(long, value_name = "NUM", num_args = 1..)]
|
|
only: Option<Vec<usize>>,
|
|
},
|
|
|
|
/// Upgrade an existing project to the latest generator version
|
|
Upgrade {
|
|
/// Path to the project directory
|
|
path: String,
|
|
},
|
|
|
|
/// Build and deploy project to Control Hub
|
|
Deploy {
|
|
/// Path to the project directory
|
|
path: String,
|
|
|
|
/// Force USB connection
|
|
#[arg(long)]
|
|
usb: bool,
|
|
|
|
/// Force WiFi connection
|
|
#[arg(long)]
|
|
wifi: bool,
|
|
|
|
/// Custom IP address
|
|
#[arg(short, long)]
|
|
ip: Option<String>,
|
|
},
|
|
|
|
/// Manage SDKs (FTC and Android)
|
|
Sdk {
|
|
#[command(subcommand)]
|
|
command: SdkCommands,
|
|
},
|
|
|
|
/// Show or update project configuration
|
|
Config {
|
|
/// Path to the project directory
|
|
path: String,
|
|
|
|
/// Set FTC SDK path for this project
|
|
#[arg(long, value_name = "PATH")]
|
|
set_sdk: Option<String>,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum SdkCommands {
|
|
/// Install required SDKs
|
|
Install,
|
|
|
|
/// Show SDK status and locations
|
|
Status,
|
|
|
|
/// Update SDKs to latest versions
|
|
Update,
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
// Enable colors on Windows
|
|
#[cfg(windows)]
|
|
colored::control::set_virtual_terminal(true).ok();
|
|
|
|
let cli = Cli::parse();
|
|
|
|
print_banner();
|
|
|
|
// Resolve proxy once at the top — every network-touching command uses it.
|
|
let proxy = ProxyConfig::resolve(cli.proxy.as_deref(), cli.no_proxy)?;
|
|
|
|
match cli.command {
|
|
Commands::New { name, ftc_sdk, android_sdk } => {
|
|
commands::new::create_project(&name, ftc_sdk.as_deref(), android_sdk.as_deref(), &proxy)
|
|
}
|
|
Commands::Doctor => {
|
|
commands::doctor::run_diagnostics()
|
|
}
|
|
Commands::Setup { path } => {
|
|
commands::setup::setup_environment(path.as_deref(), &proxy)
|
|
}
|
|
Commands::Uninstall { dry_run, only } => {
|
|
commands::uninstall::uninstall_dependencies(dry_run, only)
|
|
}
|
|
Commands::Upgrade { path } => {
|
|
commands::upgrade::upgrade_project(&path)
|
|
}
|
|
Commands::Deploy { path, usb, wifi, ip } => {
|
|
commands::deploy::deploy_project(&path, usb, wifi, ip.as_deref())
|
|
}
|
|
Commands::Sdk { command } => match command {
|
|
SdkCommands::Install => commands::sdk::install_sdks(&proxy),
|
|
SdkCommands::Status => commands::sdk::show_status(),
|
|
SdkCommands::Update => commands::sdk::update_sdks(&proxy),
|
|
},
|
|
Commands::Config { path, set_sdk } => {
|
|
if let Some(sdk_path) = set_sdk {
|
|
commands::config::set_sdk(&path, &sdk_path)
|
|
} else {
|
|
commands::config::show_config(&path)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn print_banner() {
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
println!(
|
|
"{}",
|
|
format!(" 🪲 Weevil - FTC Project Generator v{}", WEEVIL_VERSION)
|
|
.bright_cyan()
|
|
.bold()
|
|
);
|
|
println!("{}", " Nexus Workshops LLC".bright_cyan());
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
|
|
println!();
|
|
} |