#!/usr/bin/env nu # Rustelo Prerequisites Verification # Verifies that all required tools are properly installed and working def main [ --verbose (-v) # Show detailed version information --fix-path # Attempt to fix PATH issues --format (-f): string = "human" # Output format: human, json, yaml, toml ] { # Validate format parameter if $format not-in ["human", "json", "yaml", "toml"] { print $"Error: Invalid format '($format)'. Supported formats: human, json, yaml, toml" return } # Collect all system and prerequisite information let system_info = if $verbose or $format != "human" { collect_system_info } else { {} } let rust_status = collect_rust_info $verbose let node_status = collect_node_info $verbose let nushell_status = collect_nushell_info $verbose let just_status = collect_just_info $verbose let additional_tools = collect_additional_tools_info $verbose # Compile results let prerequisites = { rust: $rust_status, node: $node_status, nushell: $nushell_status, just: $just_status } let all_required_ok = ( $rust_status.installed and $node_status.installed and $nushell_status.installed and $just_status.installed ) let issues = ( [$rust_status, $node_status, $nushell_status, $just_status] | where not installed | get issue | where $it != null ) # Create comprehensive result object let result = { timestamp: (date now | format date '%Y-%m-%dT%H:%M:%S%.3fZ'), system: $system_info, prerequisites: $prerequisites, additional_tools: $additional_tools, summary: { all_prerequisites_met: $all_required_ok, total_issues: ($issues | length), issues: $issues } } # Output in requested format match $format { "human" => { output_human $result $verbose $fix_path }, "json" => { $result | to json }, "yaml" => { $result | to yaml }, "toml" => { $result | to toml } } } # Data collection functions for structured output def collect_system_info [] { let os_name = try { let uname_s = (^uname -s | str trim) match $uname_s { "Darwin" => "macOS", "Linux" => "Linux", "MINGW64_NT" => "Windows", _ => $uname_s } } catch { try { (sys host | get name) } catch { "unknown" } } let arch = try { let uname_m = (^uname -m | str trim) match $uname_m { "arm64" => "Apple Silicon (ARM64)", "x86_64" => "Intel x64", "aarch64" => "ARM64", _ => $uname_m } } catch { try { (sys host | get cpu | first | get brand) } catch { "unknown" } } let kernel = try { (^uname -r | str trim) } catch { try { (sys host | get kernel_version) } catch { "unknown" } } let detailed_os = if ($os_name == "macOS") { let version = try { (^sw_vers -productVersion | str trim) } catch { "" } let name = try { (^sw_vers -productName | str trim) } catch { "" } if ($version != "" and $name != "") { $"($name) ($version)" } else { $os_name } } else { $os_name } { os: $detailed_os, arch: $arch, kernel: $kernel, shell: ($env.SHELL? | default 'unknown') } } def collect_rust_info [verbose: bool] { if (which rustc | is-not-empty) and (which cargo | is-not-empty) { let rustc_version = try { (rustc --version | str trim) } catch { "unknown" } let cargo_version = try { (cargo --version | str trim) } catch { "unknown" } let toolchain = try { (rustup show active-toolchain | str trim) } catch { "unknown" } { installed: true, rustc_version: $rustc_version, cargo_version: $cargo_version, toolchain: $toolchain, issue: null } } else { { installed: false, rustc_version: null, cargo_version: null, toolchain: null, issue: "Rust compiler (rustc) and Cargo not found. Install from https://rustup.rs/" } } } def collect_node_info [verbose: bool] { let node_ok = (which node | is-not-empty) let pnpm_ok = (which pnpm | is-not-empty) let npm_ok = (which npm | is-not-empty) let node_version = if $node_ok { try { (node --version | str trim) } catch { null } } else { null } let pnpm_version = if $pnpm_ok { try { (pnpm --version | str trim) } catch { null } } else { null } let npm_version = if $npm_ok { try { (npm --version | str trim) } catch { null } } else { null } if $node_ok and $pnpm_ok { { installed: true, node_version: $node_version, pnpm_version: $pnpm_version, npm_version: $npm_version, issue: null } } else if $node_ok and not $pnpm_ok { { installed: false, node_version: $node_version, pnpm_version: null, npm_version: $npm_version, issue: "pnpm package manager not found. Install with: npm install -g pnpm" } } else { { installed: false, node_version: null, pnpm_version: null, npm_version: null, issue: "Node.js and pnpm not found. Install Node.js from https://nodejs.org/ then run: npm install -g pnpm" } } } def collect_nushell_info [verbose: bool] { if (which nu | is-not-empty) { let version = try { (nu --version | lines | first | str trim) } catch { "unknown" } let features = try { (nu -c "version | get features" | str join ", ") } catch { "unknown" } { installed: true, version: $version, features: $features, current_shell: ($env.SHELL? | default "unknown"), issue: null } } else { { installed: false, version: null, features: null, current_shell: ($env.SHELL? | default "unknown"), issue: "Nushell shell not found. Install from https://github.com/nushell/nushell/releases or via package manager" } } } def collect_just_info [verbose: bool] { if (which just | is-not-empty) { let version = try { (just --version | str trim) } catch { "unknown" } let justfile_exists = ("justfile" | path exists) or ("Justfile" | path exists) { installed: true, version: $version, justfile_found: $justfile_exists, issue: null } } else { { installed: false, version: null, justfile_found: false, issue: "Just command runner not found. Install with: cargo install just" } } } def collect_additional_tools_info [verbose: bool] { let tools = [ { name: "git", installed: (which git | is-not-empty), version: (if (which git | is-not-empty) { try { (git --version | str replace "git version " "") } catch { "unknown" } } else { null }) }, { name: "sqlite", installed: (which sqlite3 | is-not-empty), version: (if (which sqlite3 | is-not-empty) { try { (sqlite3 --version | split row " " | first) } catch { "unknown" } } else { null }) }, { name: "docker", installed: (which docker | is-not-empty), version: (if (which docker | is-not-empty) { try { (docker --version | str replace "Docker version " "" | split row "," | first) } catch { "unknown" } } else { null }) }, { name: "vscode", installed: ((which code | is-not-empty) or ("/Applications/Visual Studio Code.app" | path exists)), version: (if (which code | is-not-empty) { try { (code --version | lines | first) } catch { "available" } } else if ("/Applications/Visual Studio Code.app" | path exists) { "installed (app bundle)" } else { null }) }, { name: "zed", installed: ((which zed | is-not-empty) or ("/Applications/Zed.app" | path exists)), version: (if (which zed | is-not-empty) { try { (zed --version | str trim) } catch { "available" } } else if ("/Applications/Zed.app" | path exists) { "installed (app bundle)" } else { null }) }, { name: "claude-code", installed: ((which claude-code | is-not-empty) or (which claude | is-not-empty) or ("/Applications/Claude.app" | path exists)), version: (if (which claude-code | is-not-empty) { try { (claude-code --version | str trim) } catch { "available" } } else if (which claude | is-not-empty) { try { (claude --version | str trim) } catch { "available" } } else if ("/Applications/Claude.app" | path exists) { "installed (app bundle)" } else { null }) }, { name: "neovim", installed: (which nvim | is-not-empty), version: (if (which nvim | is-not-empty) { try { (nvim --version | lines | first | str replace "NVIM " "") } catch { "available" } } else { null }) }, { name: "gemini", installed: ((which gemini | is-not-empty) or ("/Applications/Gemini 2- The Duplicate Finder.app" | path exists)), version: (if (which gemini | is-not-empty) { try { (gemini --version | str trim) } catch { "available" } } else if ("/Applications/Gemini 2- The Duplicate Finder.app" | path exists) { "installed (app bundle)" } else { null }) }, { name: "codex", installed: (which codex | is-not-empty), version: (if (which codex | is-not-empty) { try { (codex --version | str trim) } catch { "available" } } else { null }) } ] $tools } def output_human [result: record, verbose: bool, fix_path: bool] { print "🔍 Verifying Rustelo Prerequisites..." print "" # Show system information if verbose if $verbose and ($result.system | is-not-empty) { print "" print "đŸ’ģ System Information:" print $" OS: ($result.system.os)" print $" Arch: ($result.system.arch)" print $" Kernel: ($result.system.kernel)" print $" Shell: ($result.system.shell)" print "" } # Show prerequisites let prereqs = $result.prerequisites if $prereqs.rust.installed { print $"✅ Rust: ($prereqs.rust.rustc_version)" if $verbose { print $" Cargo: ($prereqs.rust.cargo_version)" print $" Toolchain: ($prereqs.rust.toolchain)" } } else { print "❌ Rust: Not found" } if $prereqs.node.installed { print $"✅ Node.js: ($prereqs.node.node_version)" print $"✅ pnpm: ($prereqs.node.pnpm_version)" if $verbose and ($prereqs.node.npm_version != null) { print $" npm: ($prereqs.node.npm_version)" } } else if ($prereqs.node.node_version != null) { print $"✅ Node.js: ($prereqs.node.node_version)" print "❌ pnpm: Not found" } else { print "❌ Node.js: Not found" print "❌ pnpm: Not found" } if $prereqs.nushell.installed { print $"✅ Nushell: ($prereqs.nushell.version)" if $verbose { print $" Current shell: ($prereqs.nushell.current_shell)" print $" Features: ($prereqs.nushell.features)" } } else { print "❌ Nushell: Not found" } if $prereqs.just.installed { print $"✅ Just: ($prereqs.just.version)" if $verbose and $prereqs.just.justfile_found { print " ✅ justfile found in current directory" } else if $verbose { print " âš ī¸ No justfile found in current directory" } } else { print "❌ Just: Not found" } # Show additional tools print "" print "🔧 Additional Development Tools:" for tool in $result.additional_tools { if $tool.installed { match $tool.name { "vscode" => { print $"✅ VS Code: ($tool.version)" }, "zed" => { print $"✅ Zed Editor: ($tool.version)" }, "claude-code" => { print $"✅ Claude Code: ($tool.version)" }, "neovim" => { print $"✅ Neovim: ($tool.version)" }, "mvps" => { print $"✅ MVPS: ($tool.version)" }, _ => { print $"✅ ($tool.name | str title-case): ($tool.version)" } } } else { match $tool.name { "git" => { print "âš ī¸ Git: Not found (recommended for version control)" }, "sqlite" => { print "âš ī¸ SQLite: Not found (useful for local database development)" }, "docker" => { print "â„šī¸ Docker: Not found (optional, for containerized deployment)" }, "vscode" => { print "â„šī¸ VS Code: Not found (popular code editor)" }, "zed" => { print "â„šī¸ Zed Editor: Not found (modern collaborative editor)" }, "claude-code" => { print "â„šī¸ Claude Code: Not found (AI-powered coding assistant)" }, "neovim" => { print "â„šī¸ Neovim: Not found (vim-based editor)" }, "mvps" => { print "â„šī¸ MVPS: Not found (development tool)" } } } } print "" if $result.summary.all_prerequisites_met { print "✅ All prerequisites are properly installed!" print "" print "🚀 You're ready to create Rustelo projects:" print " rustelo new my-website" print " cd my-website" print " just dev" print "" } else { print "❌ Some prerequisites are missing or have issues:" print "" for issue in $result.summary.issues { print $" â€ĸ ($issue)" } print "" print "🔧 To fix these issues:" print " nu scripts/install-prerequisites.nu" print "" if $fix_path { attempt_path_fix } } } def check_rust [verbose: bool] { if (which rustc | is-not-empty) and (which cargo | is-not-empty) { let rustc_version = (rustc --version | str trim) let cargo_version = (cargo --version | str trim) print $"✅ Rust: ($rustc_version)" if $verbose { print $" Cargo: ($cargo_version)" print $" Toolchain: (rustup show active-toolchain | str trim)" } { ok: true, issue: null } } else { print "❌ Rust: Not found" { ok: false, issue: "Rust compiler (rustc) and Cargo not found. Install from https://rustup.rs/" } } } def check_node [verbose: bool] { let node_ok = (which node | is-not-empty) let pnpm_ok = (which pnpm | is-not-empty) if $node_ok and $pnpm_ok { let node_version = (node --version | str trim) let pnpm_version = (pnpm --version | str trim) print $"✅ Node.js: ($node_version)" print $"✅ pnpm: ($pnpm_version)" if $verbose { let npm_version = if (which npm | is-not-empty) { (npm --version | str trim) } else { "not found" } print $" npm: ($npm_version)" } { ok: true, issue: null } } else if $node_ok and not $pnpm_ok { let node_version = (node --version | str trim) print $"✅ Node.js: ($node_version)" print "❌ pnpm: Not found" { ok: false, issue: "pnpm package manager not found. Install with: npm install -g pnpm" } } else { print "❌ Node.js: Not found" print "❌ pnpm: Not found" { ok: false, issue: "Node.js and pnpm not found. Install Node.js from https://nodejs.org/ then run: npm install -g pnpm" } } } def check_nushell [verbose: bool] { if (which nu | is-not-empty) { let version = (nu --version | lines | first | str trim) print $"✅ Nushell: ($version)" if $verbose { # Check if current shell is nushell let current_shell = ($env.SHELL? | default "unknown") print $" Current shell: ($current_shell)" # Check nushell features try { let features = (nu -c "version | get features" | str join ", ") print $" Features: ($features)" } catch { print " Features: Could not determine" } } { ok: true, issue: null } } else { print "❌ Nushell: Not found" { ok: false, issue: "Nushell shell not found. Install from https://github.com/nushell/nushell/releases or via package manager" } } } def check_just [verbose: bool] { if (which just | is-not-empty) { let version = (just --version | str trim) print $"✅ Just: ($version)" if $verbose { # Check if justfile exists in current directory if ("justfile" | path exists) { print " ✅ justfile found in current directory" } else if ("Justfile" | path exists) { print " ✅ Justfile found in current directory" } else { print " âš ī¸ No justfile found in current directory" } } { ok: true, issue: null } } else { print "❌ Just: Not found" { ok: false, issue: "Just command runner not found. Install with: cargo install just" } } } def check_additional_tools [verbose: bool] { print "" print "🔧 Additional Development Tools:" # Git if (which git | is-not-empty) { let version = (git --version | str replace "git version " "") print $"✅ Git: ($version)" } else { print "âš ī¸ Git: Not found (recommended for version control)" } # SQLite (useful for local development) if (which sqlite3 | is-not-empty) { let version = (sqlite3 --version | split row " " | first) print $"✅ SQLite: ($version)" } else { print "âš ī¸ SQLite: Not found (useful for local database development)" } # Docker (for containerized deployments) if (which docker | is-not-empty) { let version = (docker --version | str replace "Docker version " "" | split row "," | first) print $"✅ Docker: ($version)" } else { print "â„šī¸ Docker: Not found (optional, for containerized deployment)" } # VS Code or other editors with Rust support if (which code | is-not-empty) { print "✅ VS Code: Available" } else if (which nvim | is-not-empty) { print "✅ Neovim: Available" } else if (which vim | is-not-empty) { print "✅ Vim: Available" } else { print "â„šī¸ Editor: Consider installing VS Code with rust-analyzer extension" } } def attempt_path_fix [] { print "" print "🔧 Attempting to fix PATH issues..." let home = $env.HOME let cargo_bin = $"($home)/.cargo/bin" let local_bin = "/usr/local/bin" # Check if cargo bin is in PATH if not ($env.PATH | split row ":" | any { |p| $p == $cargo_bin }) { print $"Adding ($cargo_bin) to PATH..." $env.PATH = ($env.PATH | split row ":" | append $cargo_bin | uniq) } # Check if local bin is in PATH if not ($env.PATH | split row ":" | any { |p| $p == $local_bin }) { print $"Adding ($local_bin) to PATH..." $env.PATH = ($env.PATH | split row ":" | append $local_bin | uniq) } # Suggest shell configuration updates print "" print "💡 To make PATH changes permanent, add these lines to your shell config:" print "" let shell_config = match ($env.SHELL? | default "") { $path if ($path | str ends-with "zsh") => "~/.zshrc", $path if ($path | str ends-with "bash") => "~/.bashrc", $path if ($path | str ends-with "fish") => "~/.config/fish/config.fish", _ => "~/.bashrc" } print $" echo 'export PATH=\"$PATH:($cargo_bin):($local_bin)\"' >> ($shell_config)" print " source ($shell_config)" print "" } # Show system information if verbose def show_system_info [] { print "" print "đŸ’ģ System Information:" # Get system info properly for different platforms let os_name = try { let uname_s = (^uname -s | str trim) match $uname_s { "Darwin" => "macOS", "Linux" => "Linux", "MINGW64_NT" => "Windows", _ => $uname_s } } catch { try { (sys host | get name) } catch { "unknown" } } let arch = try { let uname_m = (^uname -m | str trim) match $uname_m { "arm64" => "Apple Silicon (ARM64)", "x86_64" => "Intel x64", "aarch64" => "ARM64", _ => $uname_m } } catch { try { (sys host | get cpu | first | get brand) } catch { "unknown" } } let kernel = try { (^uname -r | str trim) } catch { try { (sys host | get kernel_version) } catch { "unknown" } } # Get more detailed OS info on macOS let detailed_os = if ($os_name == "macOS") { let version = try { (^sw_vers -productVersion | str trim) } catch { "" } let name = try { (^sw_vers -productName | str trim) } catch { "" } if ($version != "" and $name != "") { $"($name) ($version)" } else { $os_name } } else { $os_name } print $" OS: ($detailed_os)" print $" Arch: ($arch)" print $" Kernel: ($kernel)" print $" Shell: ($env.SHELL? | default 'unknown')" print "" }