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.
[](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
+[](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).
[](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).
+
+[](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