feat: Weevil v1.0.0-beta1 - FTC Project Generator
Cross-platform tool for generating clean, testable FTC robot projects without editing the SDK installation. Features: - Standalone project generation with proper separation from SDK - Per-project SDK configuration via .weevil.toml - Local unit testing support (no robot required) - Cross-platform build/deploy scripts (Linux/macOS/Windows) - Project upgrade system preserving user code - Configuration management commands - Comprehensive test suite (11 passing tests) - Zero-warning builds Architecture: - Pure Rust implementation with embedded Gradle wrapper - Projects use deployToSDK task to copy code to FTC SDK TeamCode - Git-ready projects with automatic initialization - USB and WiFi deployment with auto-detection Commands: - weevil new <name> - Create new project - weevil upgrade <path> - Update project infrastructure - weevil config <path> - View/modify project configuration - weevil sdk status/install/update - Manage SDKs Addresses the core problem: FTC's SDK structure forces students to edit framework internals instead of separating concerns like industry standard practices. Weevil enables proper software engineering workflows for robotics education.
This commit is contained in:
119
src/commands/upgrade.rs
Normal file
119
src/commands/upgrade.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use anyhow::{Result, bail};
|
||||
use colored::*;
|
||||
use std::path::PathBuf;
|
||||
use std::fs;
|
||||
|
||||
pub fn upgrade_project(path: &str) -> Result<()> {
|
||||
let project_path = PathBuf::from(path);
|
||||
|
||||
// Verify it's a weevil project (check for old .weevil-version or new .weevil.toml)
|
||||
let has_old_version = project_path.join(".weevil-version").exists();
|
||||
let has_config = project_path.join(".weevil.toml").exists();
|
||||
|
||||
if !has_old_version && !has_config {
|
||||
bail!("Not a weevil project: {} (missing .weevil-version or .weevil.toml)", path);
|
||||
}
|
||||
|
||||
println!("{}", format!("Upgrading project: {}", path).bright_yellow());
|
||||
println!();
|
||||
|
||||
// Get SDK config
|
||||
let sdk_config = crate::sdk::SdkConfig::new()?;
|
||||
|
||||
// Load or create project config
|
||||
let project_config = if has_config {
|
||||
println!("Found existing .weevil.toml");
|
||||
crate::project::ProjectConfig::load(&project_path)?
|
||||
} else {
|
||||
println!("Creating .weevil.toml (migrating from old format)");
|
||||
let project_name = project_path.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("unknown");
|
||||
crate::project::ProjectConfig::new(project_name, sdk_config.ftc_sdk_path.clone())?
|
||||
};
|
||||
|
||||
println!("Current SDK: {}", project_config.ftc_sdk_path.display());
|
||||
println!("SDK Version: {}", project_config.ftc_sdk_version);
|
||||
println!();
|
||||
|
||||
// Files that are safe to overwrite (infrastructure, not user code)
|
||||
let safe_to_overwrite = vec![
|
||||
"build.gradle.kts",
|
||||
"settings.gradle.kts",
|
||||
"build.sh",
|
||||
"build.bat",
|
||||
"deploy.sh",
|
||||
"deploy.bat",
|
||||
"gradlew",
|
||||
"gradlew.bat",
|
||||
"gradle/wrapper/gradle-wrapper.properties",
|
||||
"gradle/wrapper/gradle-wrapper.jar",
|
||||
".gitignore",
|
||||
];
|
||||
|
||||
println!("{}", "Updating infrastructure files...".bright_yellow());
|
||||
|
||||
// Create a modified SDK config that uses the project's configured FTC SDK
|
||||
let project_sdk_config = crate::sdk::SdkConfig {
|
||||
ftc_sdk_path: project_config.ftc_sdk_path.clone(),
|
||||
android_sdk_path: sdk_config.android_sdk_path.clone(),
|
||||
cache_dir: sdk_config.cache_dir.clone(),
|
||||
};
|
||||
|
||||
// Regenerate safe files using the project's configured SDK
|
||||
let builder = crate::project::ProjectBuilder::new(
|
||||
&project_config.project_name,
|
||||
&project_sdk_config
|
||||
)?;
|
||||
|
||||
// Temporarily create files in a temp location
|
||||
let temp_dir = tempfile::tempdir()?;
|
||||
builder.create(temp_dir.path(), &project_sdk_config)?;
|
||||
|
||||
// Copy only the safe files
|
||||
for file in safe_to_overwrite {
|
||||
let src = temp_dir.path().join(file);
|
||||
let dst = project_path.join(file);
|
||||
|
||||
if src.exists() {
|
||||
if let Some(parent) = dst.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
fs::copy(&src, &dst)?;
|
||||
println!(" {} {}", "✓".green(), file);
|
||||
|
||||
// Make executable if needed
|
||||
#[cfg(unix)]
|
||||
if file.ends_with(".sh") || file == "gradlew" {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = fs::metadata(&dst)?.permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(&dst, perms)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save/update the config
|
||||
project_config.save(&project_path)?;
|
||||
println!(" {} {}", "✓".green(), ".weevil.toml");
|
||||
|
||||
// Remove old version marker if it exists
|
||||
let old_version_file = project_path.join(".weevil-version");
|
||||
if old_version_file.exists() {
|
||||
fs::remove_file(old_version_file)?;
|
||||
println!(" {} {}", "✓".green(), "Removed old .weevil-version");
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("{}", "═══════════════════════════════════════════════════════════".bright_green());
|
||||
println!("{}", " ✓ Project Upgraded!".bright_green().bold());
|
||||
println!("{}", "═══════════════════════════════════════════════════════════".bright_green());
|
||||
println!();
|
||||
println!("{}", "Your code in src/ was preserved.".green());
|
||||
println!("{}", "Build scripts and gradle configuration updated.".green());
|
||||
println!();
|
||||
println!("Test it: ./gradlew test");
|
||||
println!();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user