From 5596f5badebb538c2d77a8d8d786508596866157 Mon Sep 17 00:00:00 2001 From: Eric Ratliff Date: Sat, 31 Jan 2026 14:17:51 -0600 Subject: [PATCH] fix: single source of truth for version across crate and tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace all hardcoded "1.1.0" version strings with env!("CARGO_PKG_VERSION") in src/, so Cargo.toml is the sole source for the built binary. Tests intentionally use a separate hardcoded constant in tests/common.rs to act as a canary — they will fail on a version bump until manually updated. - src/project/mod.rs: add WEEVIL_VERSION const, wire into Tera context, generated README, and .weevil-version marker - tests/common.rs: new file, holds EXPECTED_VERSION for all test crates - tests/{integration,project_lifecycle,unit/config_tests}.rs: pull from common instead of env! or inline literals --- Cargo.toml | 4 +-- src/lib.rs | 1 + src/main.rs | 69 +++++++++++++++++++++----------------- src/project/config.rs | 4 ++- src/project/mod.rs | 10 +++--- src/version.rs | 1 + tests/common.rs | 3 ++ tests/integration.rs | 6 +++- tests/project_lifecycle.rs | 8 +++-- tests/unit/config_tests.rs | 8 +++-- 10 files changed, 71 insertions(+), 43 deletions(-) create mode 100644 src/version.rs create mode 100644 tests/common.rs diff --git a/Cargo.toml b/Cargo.toml index 76cbe12..54f25fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "weevil" -version = "1.0.0" +version = "1.1.0-beta.1" edition = "2021" -authors = ["Eric Ratliff "] +authors = ["Eric Ratliff "] description = "FTC robotics project generator - bores into complexity, emerges with clean code" license = "MIT" diff --git a/src/lib.rs b/src/lib.rs index b82b008..9633986 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ // File: src/lib.rs // Library interface for testing +pub mod version; pub mod sdk; pub mod project; pub mod commands; diff --git a/src/main.rs b/src/main.rs index b230403..aa8fce4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use clap::{Parser, Subcommand}; use colored::*; use anyhow::Result; +use weevil::version::WEEVIL_VERSION; mod commands; mod sdk; @@ -9,9 +10,12 @@ mod templates; #[derive(Parser)] #[command(name = "weevil")] -#[command(author = "Eric Barch ")] -#[command(version = "1.0.0")] -#[command(about = "FTC robotics project generator - bores into complexity, emerges with clean code", long_about = None)] +#[command(author = "Eric Ratliff ")] +#[command(version = WEEVIL_VERSION)] +#[command( + about = "FTC robotics project generator - bores into complexity, emerges with clean code", + long_about = None +)] struct Cli { #[command(subcommand)] command: Commands, @@ -23,71 +27,71 @@ enum Commands { New { /// Name of the robot project name: String, - + /// Path to FTC SDK (optional, will auto-detect or download) #[arg(long)] ftc_sdk: Option, - + /// Path to Android SDK (optional, will auto-detect or download) #[arg(long)] android_sdk: Option, }, - + /// 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, }, - + /// 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>, }, - + /// 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, }, - + /// 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, @@ -98,10 +102,10 @@ enum Commands { enum SdkCommands { /// Install required SDKs Install, - + /// Show SDK status and locations Status, - + /// Update SDKs to latest versions Update, } @@ -110,11 +114,11 @@ fn main() -> Result<()> { // Enable colors on Windows #[cfg(windows)] colored::control::set_virtual_terminal(true).ok(); - + let cli = Cli::parse(); - + print_banner(); - + match cli.command { Commands::New { name, ftc_sdk, android_sdk } => { commands::new::create_project(&name, ftc_sdk.as_deref(), android_sdk.as_deref()) @@ -134,13 +138,11 @@ fn main() -> Result<()> { 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(), - SdkCommands::Status => commands::sdk::show_status(), - SdkCommands::Update => commands::sdk::update_sdks(), - } - } + Commands::Sdk { command } => match command { + SdkCommands::Install => commands::sdk::install_sdks(), + SdkCommands::Status => commands::sdk::show_status(), + SdkCommands::Update => commands::sdk::update_sdks(), + }, Commands::Config { path, set_sdk } => { if let Some(sdk_path) = set_sdk { commands::config::set_sdk(&path, &sdk_path) @@ -153,8 +155,13 @@ fn main() -> Result<()> { fn print_banner() { println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan()); - println!("{}", " 🪲 Weevil - FTC Project Generator v1.0.0".bright_cyan().bold()); + println!( + "{}", + format!(" 🪲 Weevil - FTC Project Generator v{}", WEEVIL_VERSION) + .bright_cyan() + .bold() + ); println!("{}", " Nexus Workshops LLC".bright_cyan()); println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan()); println!(); -} \ No newline at end of file +} diff --git a/src/project/config.rs b/src/project/config.rs index ea5038b..3b6d49c 100644 --- a/src/project/config.rs +++ b/src/project/config.rs @@ -3,6 +3,8 @@ use std::path::{Path, PathBuf}; use std::fs; use anyhow::{Result, Context, bail}; +const WEEVIL_VERSION: &str = env!("CARGO_PKG_VERSION"); + #[derive(Debug, Serialize, Deserialize)] pub struct ProjectConfig { pub project_name: String, @@ -24,7 +26,7 @@ impl ProjectConfig { Ok(Self { project_name: project_name.to_string(), - weevil_version: "1.0.0".to_string(), + weevil_version: WEEVIL_VERSION.to_string(), ftc_sdk_path, ftc_sdk_version, android_sdk_path, diff --git a/src/project/mod.rs b/src/project/mod.rs index dc4120d..983341c 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -7,6 +7,8 @@ use git2::Repository; use crate::sdk::SdkConfig; +const WEEVIL_VERSION: &str = env!("CARGO_PKG_VERSION"); + pub mod deployer; pub mod config; @@ -68,7 +70,7 @@ impl ProjectBuilder { let mut _context = TeraContext::new(); _context.insert("project_name", &self.name); _context.insert("sdk_dir", &sdk_config.ftc_sdk_path.to_string_lossy()); - _context.insert("generator_version", "1.0.0"); + _context.insert("generator_version", WEEVIL_VERSION); self.create_project_files(project_path, sdk_config)?; @@ -84,7 +86,7 @@ impl ProjectBuilder { let readme = format!( r#"# {} -FTC Robot Project generated by Weevil v1.0.0 +FTC Robot Project generated by Weevil v{} ## Quick Start ```bash @@ -111,7 +113,7 @@ deploy.bat 2. Test locally: `./gradlew test` 3. Deploy: `./deploy.sh` (or `deploy.bat` on Windows) "#, - self.name + self.name, WEEVIL_VERSION ); fs::write(project_path.join("README.md"), readme)?; @@ -120,7 +122,7 @@ deploy.bat fs::write(project_path.join(".gitignore"), gitignore)?; // Version marker - fs::write(project_path.join(".weevil-version"), "1.0.0")?; + fs::write(project_path.join(".weevil-version"), WEEVIL_VERSION)?; // build.gradle.kts - Pure Java with deployToSDK task // Escape backslashes for Windows paths in Kotlin strings diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 0000000..f21ab63 --- /dev/null +++ b/src/version.rs @@ -0,0 +1 @@ +pub const WEEVIL_VERSION: &str = env!("CARGO_PKG_VERSION"); \ No newline at end of file diff --git a/tests/common.rs b/tests/common.rs new file mode 100644 index 0000000..18c5bda --- /dev/null +++ b/tests/common.rs @@ -0,0 +1,3 @@ +// Intentionally hardcoded. When you bump the version in Cargo.toml, +// tests will fail here until you update this to match. +pub const EXPECTED_VERSION: &str = "1.1.0-beta.1"; \ No newline at end of file diff --git a/tests/integration.rs b/tests/integration.rs index 637a446..fa5059a 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -2,6 +2,10 @@ use assert_cmd::prelude::*; use predicates::prelude::*; use std::process::Command; +#[path = "common.rs"] +mod common; +use common::EXPECTED_VERSION; + #[path = "integration/environment_tests.rs"] mod environment_tests; @@ -25,7 +29,7 @@ fn test_version_command() { cmd.assert() .success() - .stdout(predicate::str::contains("1.0.0")); + .stdout(predicate::str::contains(EXPECTED_VERSION)); } #[test] diff --git a/tests/project_lifecycle.rs b/tests/project_lifecycle.rs index 1e98c75..252db6c 100644 --- a/tests/project_lifecycle.rs +++ b/tests/project_lifecycle.rs @@ -6,6 +6,10 @@ use std::fs; use weevil::project::{ProjectBuilder, ProjectConfig}; use weevil::sdk::SdkConfig; +#[path = "common.rs"] +mod common; +use common::EXPECTED_VERSION; + // Note: These tests use the actual FTC SDK if available, or skip if not // For true unit testing with mocks, we'd need to refactor to use dependency injection @@ -26,7 +30,7 @@ fn test_config_create_and_save() { assert_eq!(config.project_name, "test-robot"); assert_eq!(config.ftc_sdk_path, sdk_path); assert_eq!(config.android_sdk_path, android_sdk_path); - assert_eq!(config.weevil_version, "1.0.0"); + assert_eq!(config.weevil_version, EXPECTED_VERSION); // Save and reload let project_path = temp_dir.path().join("project"); @@ -60,7 +64,7 @@ fn test_config_toml_format() { let content = fs::read_to_string(project_path.join(".weevil.toml")).unwrap(); assert!(content.contains("project_name = \"my-robot\"")); - assert!(content.contains("weevil_version = \"1.0.0\"")); + assert!(content.contains(&format!("weevil_version = \"{}\"", EXPECTED_VERSION))); assert!(content.contains("ftc_sdk_path")); assert!(content.contains("ftc_sdk_version")); assert!(content.contains("android_sdk_path")); diff --git a/tests/unit/config_tests.rs b/tests/unit/config_tests.rs index 6072c76..e6da83b 100644 --- a/tests/unit/config_tests.rs +++ b/tests/unit/config_tests.rs @@ -6,6 +6,10 @@ use std::path::PathBuf; use tempfile::TempDir; use std::fs; +#[path = "../common.rs"] +mod common; +use common::EXPECTED_VERSION; + #[test] fn test_config_create_and_save() { let temp_dir = TempDir::new().unwrap(); @@ -15,7 +19,7 @@ fn test_config_create_and_save() { assert_eq!(config.project_name, "test-robot"); assert_eq!(config.ftc_sdk_path, sdk_path); - assert_eq!(config.weevil_version, "1.0.0"); + assert_eq!(config.weevil_version, EXPECTED_VERSION); // Save and reload config.save(temp_dir.path()).unwrap(); @@ -45,7 +49,7 @@ fn test_config_toml_format() { let content = fs::read_to_string(temp_dir.path().join(".weevil.toml")).unwrap(); assert!(content.contains("project_name = \"my-robot\"")); - assert!(content.contains("weevil_version = \"1.0.0\"")); + assert!(content.contains(&format!("weevil_version = \"{}\"", EXPECTED_VERSION))); assert!(content.contains("ftc_sdk_path")); }