Merge _configs/ into config/ for single configuration directory. Update all path references. Changes: - Move _configs/* to config/ - Update .gitignore for new patterns - No code references to _configs/ found Impact: -1 root directory (layout_conventions.md compliance)
531 lines
16 KiB
Plaintext
531 lines
16 KiB
Plaintext
#!/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
|
||
}
|