5 Commits

Author SHA1 Message Date
Eric Ratliff
6b6ba058b7 chore: Release v1.0.0 - First stable release
Update version references from 1.0.0-rc2 to 1.0.0 across documentation.

This marks the first production-ready stable release of Weevil with complete
cross-platform support, robust Windows deployment, and comprehensive project
management features.

Changes:
- Update README.md current version to 1.0.0
- Remove "Next Release" section from ROADMAP.md (now tracking post-1.0 features)

Weevil is now stable and ready for FTC teams to use in production. 🎉
2026-01-26 19:39:11 -06:00
Eric Ratliff
8add733514 Updated email to eric@nxlearn.net 2026-01-26 19:18:26 -06:00
Eric Ratliff
8f12a0a09d Updated versioning information to align to RC2 2026-01-26 19:16:02 -06:00
Eric Ratliff
655a213113 docs: Add project roadmap for v1.0.0 and beyond
Add ROADMAP.md documenting planned features and improvements for the
initial stable release and future versions.

Roadmap includes:
- Pre-1.0.0: Critical fixes for Windows deployment and documentation
- v1.0.0 features: Enhanced project templates, improved error handling
- Post-1.0.0: Advanced features like custom templates, project analytics,
  multi-robot support, and CI/CD integration

This provides transparency for users and contributors about the project's
direction and planned capabilities.
2026-01-26 18:43:44 -06:00
Eric Ratliff
64826e2ce2 fix: Complete Windows deployment pipeline
Fixes critical bugs in Windows APK deployment workflow including APK path
resolution, adb integration, and config file parsing.

Changes:
- Fix APK search to look for TeamCode-debug.apk instead of *app-debug.apk
- Strip both single and double quotes from batch file path parsing
- Add android_sdk_path to project configuration (.weevil.toml)
- Resolve adb.exe from Android SDK platform-tools directory
- Check adb install exit code and report deployment failures correctly
- Add migration support for old .weevil.toml files missing android_sdk_path
- Update all tests to use new ProjectConfig::new() signature

The deployment workflow now properly finds the generated APK, locates adb,
and reports success/failure accurately on Windows.
2026-01-26 18:42:06 -06:00
7 changed files with 73 additions and 25 deletions

17
CHANGELOG.md Normal file
View File

@@ -0,0 +1,17 @@
# Changelog
## [1.0.0] - 2026-01-27
First stable release! 🎉
### Added
- Complete Windows deployment support
- Android SDK path in project configuration
- Robust cross-platform build and deployment scripts
- Project upgrade command with config migration
- Comprehensive test suite
### Fixed
- Windows APK discovery and deployment
- Batch file path parsing (quote handling)
- ADB integration and error reporting

View File

@@ -511,7 +511,7 @@ Built with frustration at unnecessarily complex robotics frameworks, and hope th
## Project Status ## Project Status
**Current Version:** 1.0.0-rc1 **Current Version:** 1.0.0
**What Works:** **What Works:**
- ✅ Project generation - ✅ Project generation
@@ -532,7 +532,7 @@ Built with frustration at unnecessarily complex robotics frameworks, and hope th
**Questions? Issues? Suggestions?** **Questions? Issues? Suggestions?**
📧 Email: [eric@nxws.dev](mailto:eric@nxws.dev) 📧 Email: [eric@nxlearn.net](mailto:eric@nxlearn.net)
🐛 Issues: Open an issue on the repository 🐛 Issues: Open an issue on the repository
Building better tools so you can build better robots. 🤖 Building better tools so you can build better robots. 🤖

View File

@@ -2,9 +2,6 @@
This document outlines the planned feature development for Weevil across multiple versions. Features are subject to change based on user feedback, technical constraints, and market needs. This document outlines the planned feature development for Weevil across multiple versions. Features are subject to change based on user feedback, technical constraints, and market needs.
**Current Version:** 1.0.0-rc1
**Next Release:** 1.1.0 (Target: TBD)
--- ---
## Version 1.1.0 - Core Stability & Team Adoption ## Version 1.1.0 - Core Stability & Team Adoption

View File

@@ -32,7 +32,7 @@ pub fn upgrade_project(path: &str) -> Result<()> {
let project_name = project_path.file_name() let project_name = project_path.file_name()
.and_then(|n| n.to_str()) .and_then(|n| n.to_str())
.unwrap_or("unknown"); .unwrap_or("unknown");
crate::project::ProjectConfig::new(project_name, sdk_config.ftc_sdk_path.clone())? crate::project::ProjectConfig::new(project_name, sdk_config.ftc_sdk_path.clone(), sdk_config.android_sdk_path.clone())?
}; };
println!("Current SDK: {}", project_config.ftc_sdk_path.display()); println!("Current SDK: {}", project_config.ftc_sdk_path.display());

View File

@@ -9,10 +9,16 @@ pub struct ProjectConfig {
pub weevil_version: String, pub weevil_version: String,
pub ftc_sdk_path: PathBuf, pub ftc_sdk_path: PathBuf,
pub ftc_sdk_version: String, pub ftc_sdk_version: String,
#[serde(default = "default_android_sdk_path")]
pub android_sdk_path: PathBuf,
}
fn default_android_sdk_path() -> PathBuf {
PathBuf::new()
} }
impl ProjectConfig { impl ProjectConfig {
pub fn new(project_name: &str, ftc_sdk_path: PathBuf) -> Result<Self> { pub fn new(project_name: &str, ftc_sdk_path: PathBuf, android_sdk_path: PathBuf) -> Result<Self> {
let ftc_sdk_version = crate::sdk::ftc::get_version(&ftc_sdk_path) let ftc_sdk_version = crate::sdk::ftc::get_version(&ftc_sdk_path)
.unwrap_or_else(|_| "unknown".to_string()); .unwrap_or_else(|_| "unknown".to_string());
@@ -21,6 +27,7 @@ impl ProjectConfig {
weevil_version: "1.0.0".to_string(), weevil_version: "1.0.0".to_string(),
ftc_sdk_path, ftc_sdk_path,
ftc_sdk_version, ftc_sdk_version,
android_sdk_path,
}) })
} }
@@ -34,9 +41,15 @@ impl ProjectConfig {
let contents = fs::read_to_string(&config_path) let contents = fs::read_to_string(&config_path)
.context("Failed to read .weevil.toml")?; .context("Failed to read .weevil.toml")?;
let config: ProjectConfig = toml::from_str(&contents) let mut config: ProjectConfig = toml::from_str(&contents)
.context("Failed to parse .weevil.toml")?; .context("Failed to parse .weevil.toml")?;
// Migrate old configs that don't have android_sdk_path
if config.android_sdk_path.as_os_str().is_empty() {
let sdk_config = crate::sdk::SdkConfig::new()?;
config.android_sdk_path = sdk_config.android_sdk_path;
}
Ok(config) Ok(config)
} }
@@ -77,6 +90,7 @@ impl ProjectConfig {
println!(); println!();
println!("{:.<20} {}", "FTC SDK Path", self.ftc_sdk_path.display().to_string().bright_white()); println!("{:.<20} {}", "FTC SDK Path", self.ftc_sdk_path.display().to_string().bright_white());
println!("{:.<20} {}", "FTC SDK Version", self.ftc_sdk_version.bright_white()); println!("{:.<20} {}", "FTC SDK Version", self.ftc_sdk_version.bright_white());
println!("{:.<20} {}", "Android SDK Path", self.android_sdk_path.display().to_string().bright_white());
println!(); println!();
} }
} }

View File

@@ -77,7 +77,7 @@ impl ProjectBuilder {
fn create_project_files(&self, project_path: &Path, sdk_config: &SdkConfig) -> Result<()> { fn create_project_files(&self, project_path: &Path, sdk_config: &SdkConfig) -> Result<()> {
// Create .weevil.toml config // Create .weevil.toml config
let project_config = ProjectConfig::new(&self.name, sdk_config.ftc_sdk_path.clone())?; let project_config = ProjectConfig::new(&self.name, sdk_config.ftc_sdk_path.clone(), sdk_config.android_sdk_path.clone())?;
project_config.save(project_path)?; project_config.save(project_path)?;
// README.md // README.md
@@ -334,47 +334,62 @@ echo "✓ Deployed!"
let deploy_bat = r#"@echo off let deploy_bat = r#"@echo off
setlocal enabledelayedexpansion setlocal enabledelayedexpansion
REM Read SDK path from config REM Read SDK paths from config
for /f "tokens=2 delims==" %%a in ('findstr /c:"ftc_sdk_path" .weevil.toml') do ( for /f "tokens=2 delims==" %%a in ('findstr /c:"ftc_sdk_path" .weevil.toml') do set SDK_DIR=%%a
set SDK_DIR=%%a for /f "tokens=2 delims==" %%a in ('findstr /c:"android_sdk_path" .weevil.toml') do set ANDROID_SDK=%%a
)
REM Strip all quotes (both single and double) REM Strip all quotes (both single and double)
set SDK_DIR=%SDK_DIR:"=% set SDK_DIR=%SDK_DIR:"=%
set SDK_DIR=%SDK_DIR:'=% set SDK_DIR=%SDK_DIR:'=%
set SDK_DIR=%SDK_DIR: =% set SDK_DIR=%SDK_DIR: =%
set ANDROID_SDK=%ANDROID_SDK:"=%
set ANDROID_SDK=%ANDROID_SDK:'=%
set ANDROID_SDK=%ANDROID_SDK: =%
if not defined SDK_DIR ( if not defined SDK_DIR (
echo Error: Could not read FTC SDK path from .weevil.toml echo Error: Could not read FTC SDK path from .weevil.toml
exit /b 1 exit /b 1
) )
if not defined ANDROID_SDK (
echo Error: Could not read Android SDK path from .weevil.toml
exit /b 1
)
REM Set ADB path
set ADB_PATH=%ANDROID_SDK%\platform-tools\adb.exe
echo Building APK... echo Building APK...
call gradlew.bat buildApk call gradlew.bat buildApk
echo. echo.
echo Deploying to Control Hub... echo Deploying to Control Hub...
echo.
echo DEBUG: SDK_DIR = %SDK_DIR%
echo DEBUG: Searching for: %SDK_DIR%\TeamCode-debug.apk
echo.
REM Find APK - look for TeamCode-debug.apk REM Find APK - look for TeamCode-debug.apk
for /f "delims=" %%i in ('dir /s /b "%SDK_DIR%\TeamCode-debug.apk" 2^>nul') do set APK=%%i for /f "delims=" %%i in ('dir /s /b "%SDK_DIR%\TeamCode-debug.apk" 2^>nul') do set APK=%%i
if not defined APK ( if not defined APK (
echo Error: APK not found echo Error: APK not found
echo.
echo DEBUG: Tried searching in: %SDK_DIR%
echo DEBUG: Let's see what APKs exist:
dir /s /b "%SDK_DIR%\*.apk" 2>nul
echo.
exit /b 1 exit /b 1
) )
echo Found APK: %APK% echo Found APK: %APK%
REM Check for adb
if not exist "%ADB_PATH%" (
echo Error: adb not found at %ADB_PATH%
echo Run: weevil sdk install
exit /b 1
)
echo Installing: %APK% echo Installing: %APK%
adb install -r "%APK%" "%ADB_PATH%" install -r "%APK%"
if errorlevel 1 (
echo.
echo Deployment failed!
exit /b 1
)
echo. echo.
echo Deployed! echo Deployed!

View File

@@ -13,6 +13,7 @@ use weevil::sdk::SdkConfig;
fn test_config_create_and_save() { fn test_config_create_and_save() {
let temp_dir = TempDir::new().unwrap(); let temp_dir = TempDir::new().unwrap();
let sdk_path = temp_dir.path().join("mock-sdk"); let sdk_path = temp_dir.path().join("mock-sdk");
let android_sdk_path = temp_dir.path().join("android-sdk");
// Create minimal SDK structure // Create minimal SDK structure
fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap(); fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap();
@@ -20,10 +21,11 @@ fn test_config_create_and_save() {
fs::write(sdk_path.join("build.gradle"), "// test").unwrap(); fs::write(sdk_path.join("build.gradle"), "// test").unwrap();
fs::write(sdk_path.join(".version"), "v10.1.1").unwrap(); fs::write(sdk_path.join(".version"), "v10.1.1").unwrap();
let config = ProjectConfig::new("test-robot", sdk_path.clone()).unwrap(); let config = ProjectConfig::new("test-robot", sdk_path.clone(), android_sdk_path.clone()).unwrap();
assert_eq!(config.project_name, "test-robot"); assert_eq!(config.project_name, "test-robot");
assert_eq!(config.ftc_sdk_path, sdk_path); 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, "1.0.0");
// Save and reload // Save and reload
@@ -34,12 +36,14 @@ fn test_config_create_and_save() {
let loaded = ProjectConfig::load(&project_path).unwrap(); let loaded = ProjectConfig::load(&project_path).unwrap();
assert_eq!(loaded.project_name, config.project_name); assert_eq!(loaded.project_name, config.project_name);
assert_eq!(loaded.ftc_sdk_path, config.ftc_sdk_path); assert_eq!(loaded.ftc_sdk_path, config.ftc_sdk_path);
assert_eq!(loaded.android_sdk_path, config.android_sdk_path);
} }
#[test] #[test]
fn test_config_toml_format() { fn test_config_toml_format() {
let temp_dir = TempDir::new().unwrap(); let temp_dir = TempDir::new().unwrap();
let sdk_path = temp_dir.path().join("sdk"); let sdk_path = temp_dir.path().join("sdk");
let android_sdk_path = temp_dir.path().join("android-sdk");
// Create minimal SDK // Create minimal SDK
fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap(); fs::create_dir_all(sdk_path.join("TeamCode/src/main/java")).unwrap();
@@ -47,7 +51,7 @@ fn test_config_toml_format() {
fs::write(sdk_path.join("build.gradle"), "// test").unwrap(); fs::write(sdk_path.join("build.gradle"), "// test").unwrap();
fs::write(sdk_path.join(".version"), "v10.1.1").unwrap(); fs::write(sdk_path.join(".version"), "v10.1.1").unwrap();
let config = ProjectConfig::new("my-robot", sdk_path).unwrap(); let config = ProjectConfig::new("my-robot", sdk_path, android_sdk_path).unwrap();
let project_path = temp_dir.path().join("project"); let project_path = temp_dir.path().join("project");
fs::create_dir_all(&project_path).unwrap(); fs::create_dir_all(&project_path).unwrap();
@@ -59,6 +63,7 @@ fn test_config_toml_format() {
assert!(content.contains("weevil_version = \"1.0.0\"")); assert!(content.contains("weevil_version = \"1.0.0\""));
assert!(content.contains("ftc_sdk_path")); assert!(content.contains("ftc_sdk_path"));
assert!(content.contains("ftc_sdk_version")); assert!(content.contains("ftc_sdk_version"));
assert!(content.contains("android_sdk_path"));
} }
#[test] #[test]