diff --git a/README.md b/README.md index c8c0771f..2ec28baa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # ๐Ÿš€ Syncable IaC CLI +> Automatically generate optimized Docker, Kubernetes, and cloud infrastructure configurations by analyzing your codebase. > Automatically generate optimized Docker, Kubernetes, and cloud infrastructure configurations by analyzing your codebase. [![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=for-the-badge&logo=rust&logoColor=white)](https://www.rust-lang.org/) @@ -8,12 +9,20 @@ **Syncable IaC CLI** analyzes your project and automatically generates production-ready infrastructure configurations. Supporting **260+ technologies** across 5 major language ecosystems, it understands your stack and creates optimized IaC files tailored to your specific needs. +## โšก Quick Start +[![Crates.io Downloads](https://img.shields.io/crates/d/syncable-cli)](https://crates.io/crates/syncable-cli) + +**Syncable IaC CLI** analyzes your project and automatically generates production-ready infrastructure configurations. Supporting **260+ technologies** across 5 major language ecosystems, it understands your stack and creates optimized IaC files tailored to your specific needs. + ## โšก Quick Start ```bash # Install +# Install cargo install syncable-cli +# Analyze any project + # Analyze any project sync-ctl analyze /path/to/your/project @@ -22,9 +31,12 @@ sync-ctl vulnerabilities # Run security analysis sync-ctl security + +# Force update check (clears cache) +sync-ctl --clear-update-cache analyze . ``` -That's it! The CLI will detect your languages, frameworks, dependencies, and provide detailed insights about your project structure. +That's it! The CLI will detect your languages, frameworks, dependencies, and provide detailed insights about your project structure. The tool includes smart update notifications to keep you on the latest version. ## ๐ŸŽฏ What It Does @@ -76,6 +88,12 @@ $ sync-ctl analyze ./my-express-app - **Service mapping** - Traces dependencies between containers - **Network topology** - Visualizes service communication +### ๐Ÿ”„ Smart Update System +- **Intelligent caching** - Checks every 2 hours when no update available +- **Immediate notifications** - Shows updates instantly when available +- **Clear instructions** - Provides multiple update methods with step-by-step guidance +- **Zero-maintenance** - Automatically keeps you informed of new releases + ## ๐Ÿ› ๏ธ Installation ### Via Cargo (Recommended) @@ -92,6 +110,19 @@ cargo install --path . ## ๐Ÿ“– Usage Guide +### Basic Commands +cargo install syncable-cli +``` + +### From Source +```bash +git clone https://github.com/syncable-dev/syncable-cli.git +cd syncable-cli +cargo install --path . +``` + +## ๐Ÿ“– Usage Guide + ### Basic Commands ```bash @@ -118,8 +149,33 @@ Choose the output format that works best for you: - **Summary** - Brief overview for CI/CD - **JSON** - Machine-readable format +### Advanced Configuration +# Analyze with different display formats +sync-ctl analyze # Matrix view (default) +sync-ctl analyze --display detailed # Detailed view +sync-ctl analyze --json # JSON output + +# Security & vulnerability checks +sync-ctl security # Comprehensive security analysis +sync-ctl vulnerabilities # Dependency vulnerability scan + +# Dependency analysis +sync-ctl dependencies --licenses # Show license information +sync-ctl dependencies --vulnerabilities # Check for known CVEs +``` + +### Display Modes + +Choose the output format that works best for you: + +- **Matrix** (default) - Compact dashboard view +- **Detailed** - Comprehensive vertical layout +- **Summary** - Brief overview for CI/CD +- **JSON** - Machine-readable format + ### Advanced Configuration +Create `.syncable.toml` in your project root: Create `.syncable.toml` in your project root: ```toml @@ -172,27 +228,78 @@ check_secrets = true - CI/CD pipeline generation - Real-time monitoring setup +[security] +fail_on_high_severity = true +check_secrets = true +``` + +## ๐ŸŒŸ Technology Coverage + +
+View Supported Technologies (260+) + +### By Language + +- **JavaScript/TypeScript** (46) - React, Vue, Angular, Next.js, Express, Nest.js, and more +- **Python** (76) - Django, Flask, FastAPI, NumPy, TensorFlow, PyTorch, and more +- **Java/JVM** (98) - Spring Boot, Micronaut, Hibernate, Kafka, Elasticsearch, and more +- **Go** (21) - Gin, Echo, Fiber, gRPC, Kubernetes client, and more +- **Rust** (20) - Actix-web, Axum, Rocket, Tokio, SeaORM, and more + +### Package Managers +- npm, yarn, pnpm, bun (JavaScript) +- pip, poetry, pipenv, conda (Python) +- Maven, Gradle (Java) +- Cargo (Rust) +- Go modules (Go) + +
+ +## ๐Ÿš€ Roadmap + +### โœ… Phase 1: Analysis Engine (Complete) +- Project analysis and technology detection +- Vulnerability scanning +- Basic security analysis + +### ๐Ÿ”„ Phase 2: AI-Powered Generation (In Progress) +- Smart Dockerfile generation +- Intelligent Docker Compose creation +- Cloud-optimized configurations + +### ๐Ÿ“… Future Phases +- Kubernetes manifests & Helm charts +- Terraform modules for AWS/GCP/Azure +- CI/CD pipeline generation +- Real-time monitoring setup + ## ๐Ÿค Contributing +We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. ```bash # Run tests cargo test +# Check code quality +cargo clippy # Check code quality cargo clippy # Format code cargo fmt ``` +``` ## ๐Ÿ“„ License +MIT License - see [LICENSE](LICENSE) for details. MIT License - see [LICENSE](LICENSE) for details. ## ๐Ÿ™ Acknowledgments +Built with Rust ๐Ÿฆ€ and powered by the open-source community. Built with Rust ๐Ÿฆ€ and powered by the open-source community. --- @@ -200,3 +307,6 @@ Built with Rust ๐Ÿฆ€ and powered by the open-source community. **Need help?** Check our [documentation](https://github.com/syncable-dev/syncable-cli/wiki) or [open an issue](https://github.com/syncable-dev/syncable-cli/issues). [![Star on GitHub](https://img.shields.io/github/stars/syncable-dev/syncable-cli?style=social)](https://github.com/syncable-dev/syncable-cli) +**Need help?** Check our [documentation](https://github.com/syncable-dev/syncable-cli/wiki) or [open an issue](https://github.com/syncable-dev/syncable-cli/issues). + +[![Star on GitHub](https://img.shields.io/github/stars/syncable-dev/syncable-cli?style=social)](https://github.com/syncable-dev/syncable-cli) diff --git a/src/analyzer/display.rs b/src/analyzer/display.rs index 18b6cb42..7c2e0ddb 100644 --- a/src/analyzer/display.rs +++ b/src/analyzer/display.rs @@ -48,7 +48,7 @@ impl ContentLine { } /// Box drawer that pre-calculates optimal dimensions -struct BoxDrawer { +pub struct BoxDrawer { title: String, lines: Vec, min_width: usize, @@ -56,7 +56,7 @@ struct BoxDrawer { } impl BoxDrawer { - fn new(title: &str) -> Self { + pub fn new(title: &str) -> Self { Self { title: title.to_string(), lines: Vec::new(), @@ -65,15 +65,15 @@ impl BoxDrawer { } } - fn add_line(&mut self, label: &str, value: &str, label_colored: bool) { + pub fn add_line(&mut self, label: &str, value: &str, label_colored: bool) { self.lines.push(ContentLine::new(label, value, label_colored)); } - fn add_value_only(&mut self, value: &str) { + pub fn add_value_only(&mut self, value: &str) { self.lines.push(ContentLine::new("", value, false)); } - fn add_separator(&mut self) { + pub fn add_separator(&mut self) { self.lines.push(ContentLine::separator()); } @@ -130,7 +130,7 @@ impl BoxDrawer { } /// Draw the complete box - fn draw(&self) -> String { + pub fn draw(&self) -> String { let box_width = self.calculate_optimal_width(); let content_width = box_width - 4; // Available space for content diff --git a/src/main.rs b/src/main.rs index 9dee05be..aa6c9ce3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use syncable_cli::{ config, generator, }; -use syncable_cli::analyzer::display::{display_analysis, DisplayMode}; +use syncable_cli::analyzer::display::{display_analysis, DisplayMode, BoxDrawer}; use std::process; use std::collections::HashMap; use std::fs; @@ -33,7 +33,7 @@ async fn run() -> syncable_cli::Result<()> { println!("โœ… Update cache cleared. Checking for updates now..."); } - check_for_update(); + check_for_update().await; // Initialize logging cli.init_logging(); @@ -115,9 +115,10 @@ async fn run() -> syncable_cli::Result<()> { } fn clear_update_cache() { - let cache_file = cache_dir() + let cache_dir_path = cache_dir() .unwrap_or_else(|| PathBuf::from(".")) - .join("syncable-cli/last_update_check"); + .join("syncable-cli"); + let cache_file = cache_dir_path.join("version_cache.json"); if cache_file.exists() { match fs::remove_file(&cache_file) { @@ -130,26 +131,61 @@ fn clear_update_cache() { eprintln!("โš ๏ธ Failed to remove update cache: {}", e); } } + } else { + if std::env::var("SYNC_CTL_DEBUG").is_ok() { + eprintln!("๐Ÿ—‘๏ธ No update cache file found at: {}", cache_file.display()); + } } } -fn check_for_update() { - let cache_file = cache_dir() +async fn check_for_update() { + let cache_dir_path = cache_dir() .unwrap_or_else(|| PathBuf::from(".")) - .join("syncable-cli/last_update_check"); + .join("syncable-cli"); + let cache_file = cache_dir_path.join("version_cache.json"); let now = SystemTime::now(); - // Only check once per day - if let Ok(metadata) = fs::metadata(&cache_file) { + // Smart cache system: only cache when no update is available + // Check every 2 hours when no update was found, immediately when an update might be available + let should_check = if let Ok(metadata) = fs::metadata(&cache_file) { if let Ok(modified) = metadata.modified() { - if now.duration_since(modified).unwrap_or(Duration::ZERO) < Duration::from_secs(60 * 60 * 24) { - // Debug logging to understand cache behavior - if std::env::var("SYNC_CTL_DEBUG").is_ok() { - eprintln!("๐Ÿ” Update check skipped - checked within last 24 hours"); + let cache_duration = now.duration_since(modified).unwrap_or(Duration::ZERO); + + // Read cached data to determine cache strategy + if let Ok(cache_content) = fs::read_to_string(&cache_file) { + if let Ok(cache_data) = serde_json::from_str::(&cache_content) { + let cached_latest = cache_data["latest_version"].as_str().unwrap_or(""); + let current = env!("CARGO_PKG_VERSION"); + + // If cached version is newer than current, check immediately + if !cached_latest.is_empty() && is_version_newer(current, cached_latest) { + if std::env::var("SYNC_CTL_DEBUG").is_ok() { + eprintln!("๐Ÿ” Update available in cache, showing immediately"); + } + show_update_notification(current, cached_latest); + return; + } + + // If no update in cache, check every 2 hours + cache_duration >= Duration::from_secs(60 * 60 * 2) + } else { + true // Invalid cache, check now } - return; + } else { + true // Can't read cache, check now } + } else { + true // Can't get modified time, check now } + } else { + true // No cache file, check now + }; + + if !should_check { + if std::env::var("SYNC_CTL_DEBUG").is_ok() { + eprintln!("๐Ÿ” Update check skipped - checked recently and no update available"); + } + return; } // Debug logging @@ -158,16 +194,17 @@ fn check_for_update() { } // Query GitHub releases API - let client = reqwest::blocking::Client::builder() + let client = reqwest::Client::builder() .user_agent(format!("syncable-cli/{}", env!("CARGO_PKG_VERSION"))) - .timeout(std::time::Duration::from_secs(5)) // Add timeout + .timeout(std::time::Duration::from_secs(5)) .build(); match client { Ok(client) => { let result = client .get("https://api.github.com/repos/syncable-dev/syncable-cli/releases/latest") - .send(); + .send() + .await; match result { Ok(response) => { @@ -178,7 +215,7 @@ fn check_for_update() { return; } - match response.json::() { + match response.json::().await { Ok(json) => { let latest = json["tag_name"].as_str().unwrap_or("") .trim_start_matches('v'); // Remove 'v' prefix if present @@ -188,15 +225,20 @@ fn check_for_update() { eprintln!("๐Ÿ“ฆ Current version: {}, Latest version: {}", current, latest); } - // Parse and compare versions properly - if latest != "" && latest != current { - // Only show update message if latest is actually newer - if is_version_newer(current, latest) { - println!( - "\x1b[33m๐Ÿ”” A new version of sync-ctl is available: {} (current: {})\nRun `cargo install syncable-cli` or download from https://github.com/syncable-dev/syncable-cli/releases/tag/v{}\x1b[0m", - latest, current, latest - ); - } + // Update cache with latest version info + let cache_data = serde_json::json!({ + "latest_version": latest, + "current_version": current, + "checked_at": now.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(), + "update_available": is_version_newer(current, latest) + }); + + let _ = fs::create_dir_all(&cache_dir_path); + let _ = fs::write(&cache_file, serde_json::to_string_pretty(&cache_data).unwrap_or_default()); + + // Show update notification if newer version is available + if !latest.is_empty() && latest != current && is_version_newer(current, latest) { + show_update_notification(current, latest); } } Err(e) => { @@ -219,10 +261,49 @@ fn check_for_update() { } } } +} - // Update cache file - let _ = fs::create_dir_all(cache_file.parent().unwrap()); - let _ = fs::write(&cache_file, ""); +fn show_update_notification(current: &str, latest: &str) { + use colored::*; + + let mut box_drawer = BoxDrawer::new(&"UPDATE AVAILABLE".bright_red().bold().to_string()); + + // Version info line with prominent colors + let version_info = format!("New version: {} | Current: {}", + latest.bright_green().bold(), + current.bright_red()); + box_drawer.add_value_only(&version_info); + + // Empty line for spacing + box_drawer.add_value_only(""); + + // Instructions header with emphasis + box_drawer.add_value_only(&"To update, run one of these commands:".bright_cyan().bold().to_string()); + box_drawer.add_value_only(""); + + // Recommended method - highlighted as primary option + box_drawer.add_line(&"RECOMMENDED".bright_green().bold().to_string(), &"(via Cargo)".green().to_string(), false); + let cargo_cmd = "cargo install syncable-cli".bright_white().on_blue().bold().to_string(); + box_drawer.add_value_only(&format!(" {}", cargo_cmd)); + box_drawer.add_value_only(""); + + // Alternative method - neutral coloring + box_drawer.add_line(&"ALTERNATIVE".yellow().bold().to_string(), &"(direct download)".yellow().to_string(), false); + let github_url = format!(" Visit: {}", + format!("github.com/syncable-dev/syncable-cli/releases/v{}", latest).bright_blue().underline()); + box_drawer.add_value_only(&github_url); + box_drawer.add_value_only(""); + + // Install script method - secondary option + box_drawer.add_line(&"SCRIPT".magenta().bold().to_string(), &"(automated installer)".magenta().to_string(), false); + let script_cmd = "curl -sSL install.syncable.dev | sh".bright_white().on_magenta().bold().to_string(); + box_drawer.add_value_only(&format!(" {}", script_cmd)); + + // Add a helpful note + box_drawer.add_value_only(""); + box_drawer.add_value_only(&"Tip: The Cargo method is fastest for existing Rust users".dimmed().italic().to_string()); + + println!("\n{}", box_drawer.draw()); } // Helper function to compare semantic versions diff --git a/test_update_check.sh b/test_update_check.sh index f793d035..ed082ddf 100755 --- a/test_update_check.sh +++ b/test_update_check.sh @@ -1,20 +1,42 @@ #!/bin/bash -echo "๐Ÿงช Testing Syncable CLI Update Check" -echo "====================================" +echo "๐Ÿงช Testing Syncable CLI Smart Update Check" +echo "===========================================" # Test 1: Clear cache and check with debug echo -e "\n๐Ÿ“‹ Test 1: Clear cache and check with debug mode" -SYNC_CTL_DEBUG=1 cargo run -- --clear-update-cache analyze . 2>&1 | grep -E "(Checking for updates|Current version|Latest version|Update check skipped)" +SYNC_CTL_DEBUG=1 cargo run -- --clear-update-cache analyze . 2>&1 | grep -E "(Checking for updates|Current version|Latest version|Update check skipped|Update available in cache)" -# Test 2: Check if cache works -echo -e "\n๐Ÿ“‹ Test 2: Second run should use cache" +# Test 2: Check if intelligent cache works +echo -e "\n๐Ÿ“‹ Test 2: Second run should use smart cache (2-hour window)" sleep 1 -SYNC_CTL_DEBUG=1 cargo run -- analyze . 2>&1 | grep -E "(Update check skipped|Checking for updates)" +SYNC_CTL_DEBUG=1 cargo run -- analyze . 2>&1 | grep -E "(Update check skipped|Checking for updates|Update available in cache)" -# Test 3: Force check again -echo -e "\n๐Ÿ“‹ Test 3: Force check with --clear-update-cache" -SYNC_CTL_DEBUG=1 cargo run -- --clear-update-cache analyze . 2>&1 | grep -E "(Update cache cleared|Checking for updates)" +# Test 3: Show cache contents +echo -e "\n๐Ÿ“‹ Test 3: Examining cache contents" +if [[ "$OSTYPE" == "darwin"* ]]; then + CACHE_FILE="$HOME/Library/Caches/syncable-cli/version_cache.json" +else + CACHE_FILE="$HOME/.cache/syncable-cli/version_cache.json" +fi + +if [ -f "$CACHE_FILE" ]; then + echo "Cache file found at: $CACHE_FILE" + echo "Cache contents:" + cat "$CACHE_FILE" | jq . 2>/dev/null || cat "$CACHE_FILE" +else + echo "No cache file found at: $CACHE_FILE" +fi + +# Test 4: Force check again +echo -e "\n๐Ÿ“‹ Test 4: Force check with --clear-update-cache" +SYNC_CTL_DEBUG=1 cargo run -- --clear-update-cache analyze . 2>&1 | grep -E "(Update cache cleared|Checking for updates|Removed update cache)" echo -e "\nโœ… Test complete!" -echo "To test with a real update notification, the GitHub release needs to have a newer version than 0.4.1" \ No newline at end of file +echo "Smart update system features:" +echo " โ€ข Checks every 2 hours when no update available" +echo " โ€ข Shows update immediately if cached version is newer" +echo " โ€ข Stores detailed version info in JSON cache" +echo " โ€ข Enhanced notification with clear update instructions" +echo " โ€ข Multiple update methods (Cargo, direct download, install script)" +echo " โ€ข To test with a real update notification, the GitHub release needs to have a newer version than 0.5.0" \ No newline at end of file