syntaxis/scripts/provisioning/install-with-presets.nu

531 lines
16 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env nu
# install-with-presets.nu - Local syntaxis installer with preset reference
#
# This script handles LOCAL installation and setup only.
# For remote/distributed deployments, use project-provisioning:
# https://github.com/Akasha/project-provisioning
#
# This script:
# - Installs syntaxis binaries locally
# - Configures local services (optional)
# - Shows preset templates (reference only)
#
# Architecture:
# syntaxis: Binary distribution + LOCAL installation
# project-provisioning: Remote infrastructure + multi-host orchestration
#
# USAGE:
# nu scripts/provisioning/install-with-presets.nu --preset local
# nu scripts/provisioning/install-with-presets.nu --list-presets
# nu scripts/provisioning/install-with-presets.nu help
def main [
--preset: string = "local"
--verbose: bool = false
--list-presets: bool = false
...args
] {
# Handle help and list commands
let first_arg = ($args | first)
if ($first_arg == "help") or ($first_arg == "--help") or ($first_arg == "-h") {
print_help
return
}
if $list_presets or ($first_arg == "list-presets") {
list_available_presets
return
}
print_header
# Only allow LOCAL presets that make sense for this machine
let allowed_local_presets = ["minimal", "local", "dev"]
if not ($preset in $allowed_local_presets) {
print "❌ Error: preset '$preset' is not supported for local installation"
print ""
print "For remote deployments (staging, production, kubernetes),"
print "use project-provisioning:"
print " https://github.com/Akasha/project-provisioning"
print ""
print "Allowed LOCAL presets:"
$allowed_local_presets | each { |p| print $" • ($p)" }
return
}
print_divider
print $"Selected preset: (ansi cyan)($preset)(ansi reset)"
# Load preset configuration (for reference)
let install_config = load_preset_reference $preset
if ($install_config == null) {
print $"❌ Unknown preset: ($preset)"
return
}
# Show preset information
print_preset_info $preset $install_config
# Pre-flight checks (minimal)
print_divider
print "✓ Running pre-flight checks..."
let checks_passed = (run_preflight_checks_local $preset)
if not $checks_passed {
print "⚠️ Some checks failed - proceed anyway? (y/n)"
# For automation, continue anyway
}
# Check provctl availability
print_divider
print "🔍 Checking environment..."
let provctl_info = (detect_provctl_availability)
let provctl_available = ($provctl_info.available)
if $verbose {
print $" provctl available: ($provctl_available)"
if $provctl_available {
print $" provctl path: ($provctl_info.path)"
print $" provctl version: ($provctl_info.version)"
}
}
# Determine provctl usage
let use_provctl = match $provctl {
"required" => {
if not $provctl_available {
print "❌ provctl is required but not available"
print "Install from: https://github.com/Akasha/provctl"
return
}
true
}
"disabled" => false
"auto" => {
if $provctl_available and ($preset_config.provctl_recommended? ?? false) {
true
} else {
false
}
}
_ => false
}
if $use_provctl {
print "✓ provctl will be used for service management"
} else {
print " Service management: manual setup (generate scripts)"
}
# Installation phase
print_divider
print "📦 Installing syntaxis binaries..."
# Determine which binaries to install based on preset
let binaries_to_install = (get_binaries_for_preset $effective_preset $preset_config)
if ($binaries_to_install | length) == 0 {
print " No binaries to install for this preset"
} else {
# Call the original installer
print $"Installing ($binaries_to_install | length) binary(ies)..."
try {
# Run original installer for binaries
nu scripts/install-syntaxis.nu all 2>/dev/null
print "✓ Binaries installed"
} catch { |err|
print $"⚠️ Binary installation had issues: ($err)"
}
}
# Service setup phase
let services_config = ($preset_config.services?)
if ($services_config != null) {
print_divider
print "🚀 Setting up services..."
setup_services $effective_preset $services_config $use_provctl $provctl_info
}
# Database configuration
print_divider
print "🗄️ Configuring database..."
let db_backend = ($preset_config.database_backend? ?? "sqlite")
setup_database $db_backend $effective_preset
# Create manifest
print_divider
print "📋 Creating installation manifest..."
create_installation_manifest $effective_preset $preset_config $use_provctl
# Summary and next steps
print_divider
print_installation_summary $effective_preset $preset_config
}
# Load configuration from file
def load_config_from_file [config_path: string] {
if not ($config_path | path exists) {
print $"⚠️ Configuration not found: ($config_path)"
return null
}
try {
open $config_path
} catch { |err|
print $"❌ Failed to load configuration: ($err)"
return null
}
}
# Generate installation config output
def generate_installation_config [preset: string, config: record]: nothing -> string {
let output = $"# Generated installation configuration for preset: ($preset)
[installation]
preset = \"($preset)\"
interactive = false
[database]
backend = \"($config.database_backend?)\"
[services]
"
# Add services
let services_str = (
if ($config.services? != null) {
($config.services | to toml)
} else {
""
}
)
$output + $services_str
}
# Detect provctl availability
def detect_provctl_availability []: nothing -> record {
try {
nu scripts/provisioning/detect-provctl.nu
} catch {
{
available: false,
path: null,
version: null,
commands: [],
backends: []
}
}
}
# Select preset interactively
def select_preset_interactive [config: record]: nothing -> string {
let available_presets = ($config | get preset | keys)
if ($available_presets | length) == 0 {
return "local"
}
print ""
print "Available installation presets:"
print ""
for (idx, preset) in ($available_presets | enumerate) {
let preset_info = ($config.preset | get $preset)
let name = ($preset_info.name? ?? $preset)
let desc = ($preset_info.description? ?? "")
print $" ($idx + 1)) ($name)"
if ($desc != "") {
print $" ($desc)"
}
print ""
}
# Get user input
let choice = (input "Select preset (1-$(($available_presets | length)): ")
let choice_num = (try { $choice | into int } catch { 1 })
if ($choice_num > 0) and ($choice_num <= ($available_presets | length)) {
$available_presets | get ($choice_num - 1)
} else {
"local"
}
}
# Get binaries for preset
def get_binaries_for_preset [preset: string, config: record]: nothing -> list {
["syntaxis-cli", "syntaxis-tui"] # Core binaries always installed
# API and others conditional on preset
}
# Setup database
def setup_database [backend: string, preset: string] {
print $"Database backend: (ansi cyan)($backend)(ansi reset)"
match $backend {
"sqlite" => {
let db_path = "~/.local/share/core/workspace.db"
print $" Location: ($db_path)"
print $" Status: File-based (automatic)"
}
"surrealdb" => {
print $" Mode: Check configs/database-surrealdb.toml"
print $" Status: Requires configuration"
}
_ => {
print $" Unknown backend: ($backend)"
}
}
}
# Setup services
def setup_services [preset: string, services: record, use_provctl: bool, provctl_info: record] {
print $"Services for preset: (ansi cyan)($preset)(ansi reset)"
let service_list = []
# Iterate through services
try {
$services | transpose | each { |item|
let service_name = $item.key
let service_config = $item.value
if ($service_config.enabled? ?? false) {
print $" ✓ ($service_name)"
if ($service_config.auto_start? ?? false) {
print $" └─ auto-start: enabled"
}
}
}
} catch {
print " (no services configured)"
}
if $use_provctl {
print ""
print "📌 Service deployment with provctl:"
print " Run: nu scripts/provisioning/provctl-services.nu deploy $preset"
}
}
# Create installation manifest
def create_installation_manifest [preset: string, config: record, used_provctl: bool] {
let manifest_path = ".syntaxis/manifest-installation.toml"
let timestamp = (date now | format date "%Y-%m-%d %H:%M:%S")
let manifest = $"# Installation Manifest
[installation]
preset = \"($preset)\"
timestamp = \"($timestamp)\"
provctl_used = ($used_provctl)
config_location = \"configs/installation.toml\"
[preset_info]
name = \"($config.name?)\"
description = \"($config.description?)\"
[database]
backend = \"($config.database_backend?)\"
[notes]
# To view services: nu scripts/provisioning/provctl-services.nu list $preset
# To manage services: nu scripts/provisioning/provctl-services.nu deploy $preset
"
try {
^mkdir -p .syntaxis
echo $manifest | save $manifest_path
print $"✓ Manifest saved: ($manifest_path)"
} catch { |err|
print $"⚠️ Failed to create manifest: ($err)"
}
}
# Print header
def print_header [] {
print ""
print "┌────────────────────────────────────────────────┐"
print "│ syntaxis Enhanced Installer with provctl │"
print "│ Smart presets + Service Management │"
print "└────────────────────────────────────────────────┘"
print ""
}
# Print divider
def print_divider [] {
print "─────────────────────────────────────────────────"
}
# Print preset info
def print_preset_info [name: string, config: record] {
print $"Preset: (ansi cyan)($name)(ansi reset)"
print $" Name: ($config.name?)"
print $" Description: ($config.description?)"
print $" Database: ($config.database_backend?)"
}
# Print installation summary
def print_installation_summary [preset: string, config: record] {
print "Installation complete!"
print ""
print "Next steps:"
match $preset {
"local" => {
print " 1. Try the CLI: syntaxis-cli init my-project"
print " 2. Use TUI: syntaxis-tui"
print ""
print "To upgrade later: nu scripts/provisioning/install-with-presets.nu --preset dev"
}
"dev" => {
print " 1. Start services: provctl start surrealdb && provctl start nats"
print " 2. Or use: docker-compose up"
print " 3. Open dashboard: http://localhost:3000"
print " 4. Use API: curl http://localhost:3000/health"
}
"staging" => {
print " 1. Start Docker services: docker-compose -f docker/docker-compose.yml up"
print " 2. Verify health: curl http://localhost:3000/health"
print " 3. Open dashboard: http://localhost:3000"
print " 4. Monitor: http://localhost:9090"
}
"production" => {
print " 1. Review Kubernetes manifests: ls kubernetes/"
print " 2. Create namespace: kubectl create namespace syntaxis"
print " 3. Deploy: kubectl apply -f kubernetes/"
print " 4. Monitor: kubectl logs -n syntaxis deployment/syntaxis-api"
}
_ => {
print " Installation complete. Run 'syntaxis-cli --help' for usage."
}
}
print ""
print "Documentation: docs/INSTALLATION_CONTEXTS.md"
print ""
}
# Print help message
def print_help [] {
print ""
print "syntaxis Enhanced Installer with Presets and provctl Integration"
print ""
print "USAGE:"
print " nu scripts/provisioning/install-with-presets.nu [OPTIONS]"
print ""
print "OPTIONS:"
print " --preset <PRESET> Installation preset (local, minimal, dev, staging, production, custom)"
print " --interactive Interactive mode - ask questions"
print " --config <FILE> Load configuration from file"
print " --generate-config Generate config file and exit"
print " --provctl <MODE> provctl mode: auto (default), required, disabled"
print " --verbose Verbose output"
print " --list-presets List all available presets"
print " -h, --help Show this help message"
print ""
print "EXAMPLES:"
print " # Simple local installation"
print " nu scripts/provisioning/install-with-presets.nu"
print ""
print " # Development with services"
print " nu scripts/provisioning/install-with-presets.nu --preset dev"
print ""
print " # Interactive setup"
print " nu scripts/provisioning/install-with-presets.nu --interactive"
print ""
print " # Generate configuration"
print " nu scripts/provisioning/install-with-presets.nu --preset staging --generate-config"
print ""
print " # Install from configuration file"
print " nu scripts/provisioning/install-with-presets.nu --config my-install.toml"
print ""
print "DOCUMENTATION:"
print " docs/INSTALLATION_CONTEXTS.md - Detailed guide for each preset"
print " CLAUDE.md - Installation commands reference"
print ""
}
# List all available presets
def list_available_presets [] {
let config = (
try {
open "configs/installation.toml"
} catch {
return
}
)
let presets = ($config | get preset | keys | sort)
print ""
print "Available Installation Presets:"
print ""
for preset in $presets {
let preset_config = ($config.preset | get $preset)
let name = ($preset_config.name? ?? $preset)
let desc = ($preset_config.description? ?? "No description")
let db = ($preset_config.database_backend? ?? "unknown")
print $" (ansi cyan)→(ansi reset) ($preset)"
print $" Name: ($name)"
print $" Description: ($desc)"
print $" Database: ($db)"
print ""
}
}
# Run pre-flight checks
def run_preflight_checks [preset: string]: nothing -> bool {
mut all_passed = true
# Check if Rust is installed
if (try { which cargo } catch { null }) == null {
print " ⚠️ Rust/Cargo not found (required for building)"
$all_passed = false
} else {
print " ✓ Rust/Cargo detected"
}
# Check if NuShell is available
if (try { which nu } catch { null }) == null {
print " ❌ NuShell required but not found"
return false
} else {
print " ✓ NuShell detected"
}
# Check if configs exist
if not ("configs/installation.toml" | path exists) {
print " ❌ configs/installation.toml not found"
return false
} else {
print " ✓ Installation config found"
}
# Preset-specific checks
match $preset {
"dev" | "staging" => {
if (try { which surreal } catch { null }) == null {
print " ⚠️ SurrealDB CLI not found (can install with: cargo install surrealdb-cli)"
# Not fatal, warn but continue
} else {
print " ✓ SurrealDB detected"
}
}
"staging" | "production" => {
if (try { which docker } catch { null }) == null {
print " ⚠️ Docker not found (required for this preset)"
$all_passed = false
} else {
print " ✓ Docker detected"
}
}
_ => {}
}
$all_passed
}