syntaxis/scripts/install-syntaxis.nu
Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
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)
2025-12-26 18:36:23 +00:00

889 lines
32 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env nu
# syntaxis Installer (Dynamic Binary Discovery)
#
# Dynamically discovers binary crates from workspace Cargo.toml
# and installs them with wrappers, configs, and Leptos deployment
#
# USAGE:
# install-syntaxis.nu # Show help
# install-syntaxis.nu help # Show help
# install-syntaxis.nu all # Install all binaries (keeps target/)
# install-syntaxis.nu all --rm-target # Install all (remove target/)
# install-syntaxis.nu status # Show installation status
# install-syntaxis.nu list # List available binaries
# Source shared libraries
source syntaxis-lib.nu
def print_help [] {
print ""
print "🛠️ syntaxis Binary Installer"
print ""
print "Dynamically installs binaries from workspace with configs and Leptos dashboard"
print ""
print "USAGE:"
print " install-syntaxis.nu # Show this help message"
print " install-syntaxis.nu help # Show this help message"
print " install-syntaxis.nu all # Install all binaries (keeps target/)"
print " install-syntaxis.nu all --rm-target # Install all and remove target/"
print " install-syntaxis.nu status # Show installation status"
print " install-syntaxis.nu list # List available binaries"
print ""
print "INSTALL SPECIFIC BINARIES:"
print " install-syntaxis.nu cli # Install syntaxis-cli"
print " install-syntaxis.nu tui # Install syntaxis-tui"
print " install-syntaxis.nu api # Install syntaxis-api"
print " install-syntaxis.nu client # Install dashboard-client"
print ""
print " # Or use full binary/crate names:"
print " install-syntaxis.nu syntaxis-cli # Install syntaxis-cli"
print " install-syntaxis.nu syntaxis-tui # Install syntaxis-tui"
print " install-syntaxis.nu syntaxis-api # Install syntaxis-api"
print ""
print "OPTIONS:"
print " --rm-target # Remove target/ directories after build"
print " # (default: false, keep target/)"
print ""
print "WHAT GETS INSTALLED (PER BINARY):"
print " 1. Binary crate (auto-discovered from Cargo.toml [[bin]] sections)"
print " 2. Wrapper script (for binaries that need configuration)"
print " • Injects environment variables for auto-discovery binaries"
print " • Injects default --config path"
print " 3. Target-specific config file"
print ""
print "GLOBAL RESOURCES:"
print " • Installation manifest in .syntaxis/manifest.toml"
print " • Configuration directories at ~/.config/syntaxis/"
print ""
}
def discover_binary_crates [] {
# Load workspace members from root Cargo.toml and find binaries in core/crates/*
let workspace_toml = "Cargo.toml"
if not ($workspace_toml | path exists) {
print "❌ Cargo.toml not found"
return []
}
try {
# Read root Cargo.toml to get workspace members
let workspace_content = (open $workspace_toml)
# Extract all lines with members entries
let members = (
($workspace_content | get -o workspace | get -o members)
| each { |line|
# let trimmed = ($line | str trim)
# Match entries like "core/crates/syntaxis-api",
if ($line | str starts-with 'core/crates/') {
# Extract the path: "core/crates/syntaxis-api", → core/crates/syntaxis-api
# Split by quotes and get the middle part
($line | str trim)
#let parts = ($line | split row '"')
#if ($parts | length) >= 2 {
# $parts | get 1
#}
}
}
| compact
)
# For each member in core/crates/*, extract metadata
let binaries = (
$members
| each { |member_path|
let member_toml = $"($member_path)/Cargo.toml"
if ($member_toml | path exists) {
let member_content = (open $member_toml)
# Extract package name
let package_name = ($member_content | get -o package | get -o name)
if ($package_name | is-not-empty) {
# Extract metadata from [package.metadata.syntaxis]
let metadata = ($member_content | get -o package.metadata | get -o syntaxis)
if ($metadata | is-not-empty) {
# Extract use_wrapper
let use_wrapper = ($metadata | get -o use_wrapper)
# Extract install_mode
let install_mode = ($metadata | get -o install_mode)
# Extract config_src
let config_src = ($metadata | get -o config_src)
# Extract binary name from [[bin]] section
let parts_bin = ($member_content | get -o bin)
let bin_name = if ($parts_bin | length) > 0 {
$parts_bin | first | get -o name
} else {
""
}
let binary_name = if ($bin_name | is-not-empty) {
$bin_name
} else {
$package_name
}
# Return record directly
{
crate_name: $package_name,
binary_name: $binary_name,
crate_path: $member_path,
use_wrapper: $use_wrapper,
install_mode: $install_mode,
config_src: $config_src
}
}
}
}
}
| compact
)
return $binaries
} catch { |err|
print $"❌ Failed to discover binaries: ($err)"
return []
}
}
def deploy_libs [binaries: list] {
# Copy syntaxis-lib.nu and target-specific scripts/libs to ~/.config/syntaxis/scripts/
# so wrappers can import them via NU_LIB_DIRS
# Only archives/backs up scripts for the specific binaries being installed (NOT the shared lib)
let config_scripts_dir = ($env.HOME + "/.config/syntaxis/scripts")
let config_dir = ($env.HOME + "/.config/syntaxis")
let scripts_dir = ($env.PWD + "/scripts")
try {
# Create config scripts directory
^mkdir -p $config_scripts_dir
# Copy common library WITHOUT backup (shared across all binaries)
let lib_path = ($scripts_dir + "/syntaxis-lib.nu")
if ($lib_path | path exists) {
let lib_dest = ($config_scripts_dir + "/syntaxis-lib.nu")
# Deploy without backup - syntaxis-lib.nu is shared and shouldn't be archived on every install
^cp $lib_path $lib_dest
print " ✅ Deployed syntaxis-lib.nu to config (no backup)"
} else {
print $" ⚠️ syntaxis-lib.nu not found at ($lib_path)"
}
# Extract binary names from the binaries list to determine which scripts to deploy
let binary_names = ($binaries | each { |b| $b.binary_name })
# Copy target-specific libraries and scripts only for binaries being installed
# For each binary with binary_name "syntaxis-tui", deploy: syntaxis-tui.nu and syntaxis-tui-lib.nu
# These ARE backed up to archives before overwriting
let target_files = (glob ($scripts_dir + "/syntaxis-*.nu") | compact)
if ($target_files | length) > 0 {
for file_path in $target_files {
let file_name = ($file_path | path basename)
# Skip the shared library (already handled above without backup)
if ($file_name == "syntaxis-lib.nu") {
continue
}
# Check if this script belongs to any of the binaries being installed
# binary_name already includes "syntaxis-" prefix (e.g., "syntaxis-tui")
# so we check for exact filename matches like "syntaxis-tui.nu" and "syntaxis-tui-lib.nu"
let should_deploy = ($binary_names | any { |bn|
($file_name == $"($bn).nu") or ($file_name == $"($bn)-lib.nu")
})
if $should_deploy {
let script_dest = ($config_scripts_dir + "/" + $file_name)
# Backup existing script if it exists (to parent config_dir archives)
backup_if_exists $script_dest $config_dir
^cp $file_path $script_dest
print $" ✅ Deployed ($file_name)"
}
}
} else {
print " No target-specific scripts found"
}
} catch { |err|
print $" ⚠️ Failed to deploy libs: ($err)"
}
}
def install_binary [item: record , rm_target: bool] {
let display_name = $"(ansi blue)($item.crate_name)(ansi reset)"
print $"📦 Building ($display_name) [from: ($item.crate_path) wrapper: ($item.use_wrapper) mode: ($item.install_mode)] config: ($item.config_src)] ..."
try {
if ($item.install_mode == "cargo") or ($item.install_mode | str starts-with "cargo") {
# Standard cargo install
cargo install --path $item.crate_path out+err> /dev/null
print $"✅ ($display_name): Installed successfully"
} else if ($item.install_mode == "leptos") or ($item.install_mode | str starts-with "leptos") {
# Leptos CSR build using build-dashboard.nu script
print $" 🎨 Building Leptos WASM dashboard..."
# Call build-dashboard.nu to handle the full build (CSS + WASM)
# This script must be run from core/ directory
try {
cd core
nu ../scripts/core/build-dashboard.nu build out+err> /dev/null
cd ..
print $" ✅ Leptos WASM build completed"
} catch { |err|
cd ..
print $" ❌ Leptos WASM build failed: ($err)"
return {
success: false,
crate_name: $item.crate_name,
binary_name: $item.binary_name,
crate_path: $item.crate_path,
reason: $"Leptos build failed: ($err)"
}
}
# Deploy artifacts to public folder
print $" 📦 Deploying Leptos artifacts for ($display_name)..."
let deployment_ok = (deploy_leptos_artifacts)
if not $deployment_ok {
print $" ⚠️ Leptos artifact deployment had issues (build output may not exist yet)"
# Don't fail installation - artifacts will be available after first build
}
} else {
print $"⚠️ Unknown install_mode: ($item.install_mode), skipping"
return {
success: false,
crate_name: $item.crate_name,
binary_name: $item.binary_name,
crate_path: $item.crate_path,
reason: $"Unknown install_mode: ($item.install_mode)"
}
}
if $rm_target {
let target_dir = "target"
if ($target_dir | path exists) {
rm -r $target_dir
print $" 🧹 Cleaned up build artifacts for ($display_name)"
}
}
return {
success: true,
crate_name: $item.crate_name,
binary_name: $item.binary_name,
crate_path: $item.crate_path
}
} catch { |err|
print $"❌ ($display_name): Build failed - ($err)"
return {
success: false,
crate_name: $item.crate_name,
binary_name: $item.binary_name,
crate_path: $item.crate_path,
reason: ($err | to_string)
}
}
}
def deploy_leptos_artifacts [] {
# Deploy compiled Leptos artifacts to public directory
# This is called AFTER build-dashboard.nu has generated artifacts in core/target/site/
let home = $env.HOME
let public_dir = $"($home)/.config/syntaxis/public"
let leptos_build_dir = "core/target/site"
print " 📂 Preparing artifact deployment..."
# Create public directory if doesn't exist
try {
^mkdir -p $public_dir
} catch { |err|
print $" ❌ Failed to create public directory: ($err)"
return false
}
# Check if Leptos build artifacts exist
if not ($leptos_build_dir | path exists) {
print $" ⚠️ Leptos build artifacts not found at: ($leptos_build_dir)"
print $" 📌 This is expected if build-dashboard.nu hasn't run yet"
return false
}
# Verify artifact structure before copying
print " 🔍 Verifying artifact structure..."
mut all_artifacts_present = true
if not ("($leptos_build_dir)/index.html" | path exists) {
print $" ⚠️ index.html not found"
$all_artifacts_present = false
} else {
print $" ✅ index.html verified"
}
if not ("($leptos_build_dir)/pkg" | path exists) {
print $" ⚠️ WASM artifacts (pkg/) not found"
$all_artifacts_present = false
} else {
let wasm_files = (glob $"($leptos_build_dir)/pkg/**/*.wasm" | length)
print ($" ✅ WASM artifacts verified " + ($wasm_files | into string) + " files")
}
if not ("($leptos_build_dir)/styles/app.css" | path exists) {
print $" ⚠️ CSS file (styles/app.css) not found"
$all_artifacts_present = false
} else {
let size_bytes = (stat $"($leptos_build_dir)/styles/app.css" | get size)
let size_kb = (($size_bytes / 1024) | math round --precision 2)
print ($" ✅ CSS verified " + ($size_kb | into string) + "KB")
}
if not $all_artifacts_present {
print $" ❌ Some artifacts are missing. Cannot deploy."
return false
}
# Copy Leptos artifacts to public directory
print " 📥 Copying artifacts to ($public_dir)..."
try {
# Copy all contents of leptos_build_dir to public_dir
# Using -r for recursive, and ensuring we copy the contents, not the directory itself
^cp -r $"($leptos_build_dir)/." $"($public_dir)/"
# Verify deployment
if ("($public_dir)/index.html" | path exists) {
print $" ✅ Deployed Leptos artifacts successfully"
print $" - index.html: SPA entry point"
print $" - pkg/: WASM binaries and JS bindings"
print $" - styles/app.css: Generated CSS"
return true
} else {
print $" ❌ Deployment verification failed"
return false
}
} catch { |err|
print $" ❌ Failed to deploy Leptos artifacts: ($err)"
return false
}
}
def deploy_leptos_dashboard [] {
# Build and deploy Leptos CSR dashboard
print ""
print "🚀 Building and deploying Leptos CSR dashboard..."
let home = $env.HOME
let public_dir = $"($home)/.config/syntaxis/public"
let leptos_build_dir = "core/crates/client/target/site"
# Create public directory
^mkdir -p $public_dir
print $"📂 Created public directory: ($public_dir)"
# Build Leptos CSR (if cargo-leptos is available)
try {
print " Building Leptos CSR..."
cd core/crates/dashboard-client
cargo build --release out+err> /dev/null
cd ../../..
if ($leptos_build_dir | path exists) {
# Copy compiled site to public directory
cp -r $"($leptos_build_dir)/*" $public_dir
print $" ✅ Deployed Leptos site to ($public_dir)"
} else {
print $" ⚠️ Leptos build directory not found at ($leptos_build_dir)"
print $" Ensure cargo-leptos is installed: cargo install cargo-leptos"
}
} catch { |err|
print $" ⚠️ Leptos build skipped: ($err)"
print $" You can manually build with: cargo leptos build --release"
}
}
def backup_if_exists [path: string, archive_root: string] {
# Move existing file to timestamped archive folder if it exists
# archive_root: parent directory to contain archives/YYYY-MM-DD-HH-MM/
if ($path | path exists) {
let filename = ($path | path basename)
try {
# Create archive directory with timestamp (YYYY-MM-DD-HH-MM)
let now = (date now | format date "%Y-%m-%d-%H-%M")
let archive_dir = ($archive_root + "/archives/" + $now)
# Create archive directory if it doesn't exist
^mkdir -p $archive_dir
# Move file to archive
let backup_path = ($archive_dir + "/" + $filename)
^mv $path $backup_path
print $" ⚠️ Previous version of ($filename) exists"
print $" 📦 Archived to: archives/($now)/"
print $" Previous version preserved - you can restore if needed"
} catch { |err|
print $" ⚠️ Failed to archive ($filename): ($err)"
}
}
}
def deploy_config_for_binary [crate_name: string, config_src: string] {
# Deploy configuration from config_src (relative to project root) to ~/.config/syntaxis/
# Backs up existing configs to archives/YYYY-MM-DD-HH-MM/ before installing new ones
if ($config_src | str length) == 0 {
# No config to deploy
return
}
let config_dir = ($env.HOME + "/.config/syntaxis")
# config_src is relative to project root
let src_path = $config_src
# Ensure source exists
if not ($src_path | path exists) {
print $" ⚠️ Config source not found: ($src_path)"
return
}
try {
# Create config directory if doesn't exist
^mkdir -p $config_dir
# Copy config files from source to config directory
if ($src_path | path type) == "dir" {
# For directories, backup existing files in config_dir that come from src
let files_to_deploy = (glob $"($src_path)/*" | compact)
for file_path in $files_to_deploy {
let filename = ($file_path | path basename)
let dest_path = $"($config_dir)/($filename)"
# Backup existing file if it exists
backup_if_exists $dest_path $config_dir
}
# Copy all files from source directory
^cp -r $"($src_path)/." $"($config_dir)/" out+err> /dev/null
print $" ✅ Deployed config from ($src_path)"
} else if ($src_path | path type) == "file" {
# Copy single file
let filename = ($src_path | path basename)
let dest_path = $"($config_dir)/($filename)"
# Backup existing file if it exists
backup_if_exists $dest_path $config_dir
^cp $src_path $dest_path
print $" ✅ Deployed config: ($filename)"
}
} catch { |err|
print $" ⚠️ Failed to deploy config: ($err)"
}
}
def deploy_configurations [] {
# Create config directories and default configs
let config_dir = ($env.HOME + "/.config/syntaxis")
^mkdir -p $config_dir
^mkdir -p $"($config_dir)/public"
^mkdir -p $"($config_dir)/features"
print ""
print "📋 Deploying global configuration structure..."
# Create default main config if doesn't exist
let default_main_config = $"($config_dir)/syntaxis.toml"
if not ($default_main_config | path exists) {
let config_content = "# Syntaxis Global Configuration
# Main config file for all syntaxis applications
[server]
host = \"127.0.0.1\"
port = 8080
[api]
host = \"127.0.0.1\"
port = 8080
[database]
url = \"sqlite:///($env.HOME)/.local/share/syntaxis/syntaxis.db\"
[logging]
level = \"info\"
format = \"compact\"
"
$config_content | save $default_main_config
print $" ✅ Created: syntaxis.toml"
}
print ""
print $"📂 Configuration directories created at: ($config_dir)"
}
def create_wrapper_for_binary [binary_name: string, use_wrapper: bool, config_src: string] {
# Create wrapper script for a single binary
# Backs up existing wrapper scripts to archives/YYYY-MM-DD-HH-MM/ before installing new ones
if not $use_wrapper {
return
}
let cargo_bin = ($env.HOME + "/.cargo/bin")
let config_dir = ($env.HOME + "/.config/syntaxis")
let binary_path = ($cargo_bin + "/" + $binary_name)
let real_binary_path = ($cargo_bin + "/" + $binary_name + ".real")
# Check if binary exists
if ($binary_path | path exists) {
let display_name = $"(ansi blue)($binary_name)(ansi reset)"
try {
# Rename actual binary to .real (don't backup the binary, it's rebuilt each time)
^mv $binary_path $real_binary_path
# Create bash wrapper script that calls the NuShell wrapper
# The bash script sets NU_LIB_DIRS and calls the .nu script
let bash_wrapper = $"#!/bin/bash
# Bash wrapper for ($binary_name)
# Calls the NuShell wrapper with proper library paths
export NU_LIB_DIRS=\"\$HOME/.config/syntaxis/scripts\"
exec nu \"\$HOME/.config/syntaxis/scripts/($binary_name).nu\" \"\$@\""
$bash_wrapper | save --force $binary_path
^chmod +x $binary_path
print $" ✅ Created wrapper for ($display_name)"
print $" Location: ($binary_path)"
} catch { |err|
print $" ⚠️ Failed to create wrapper for ($display_name): ($err)"
}
}
}
def needs_wrapper [has_clap: bool, has_wasm: bool, has_leptos: bool] {
# RULE: Determine if binary needs a wrapper script
# - If HAS clap → NO wrapper (user provides args via CLI)
# - If NO clap AND NO wasm AND NO leptos → WRAPPER needed (auto-discovery)
# - If has wasm or leptos → WASM/frontend, no wrapper needed
if $has_clap {
# Has Clap: user provides arguments explicitly
return false
} else if ($has_wasm or $has_leptos) {
# WASM frontend: no CLI wrapper needed
return false
} else {
# No Clap, no WASM/Leptos: needs wrapper for auto-discovery config
return true
}
}
def register_installations [installed_binaries: list] {
# Create manifest file tracking with version and integrity info
let manifest_path = ".syntaxis/manifest.toml"
^mkdir -p ".syntaxis"
print ""
print "📊 Registering installations..."
let now = (date now | format date "%Y-%m-%d %H:%M:%S")
let home = $env.HOME
let config_dir = $"($home)/.config/syntaxis"
# Build binaries section
let binaries_toml = (
$installed_binaries
| each { |binary|
let section = $"[binaries.($binary.crate_name)]
installed = true
version = \"0.1.0\"
installed_at = \"($now)\"
"
$section
}
| str join "\n"
)
# Create comprehensive manifest
let manifest_content = $"# syntaxis Installation Manifest
# Auto-generated during installation
# Use this to track versions, verify integrity, and enable rollback
# Generated: ($now)
[installation]
created_at = \"($now)\"
last_updated = \"($now)\"
installation_root = \"(pwd)\"
ecosystem_version = \"0.1.0\"
($binaries_toml)
[configurations]
config_dir = \"($config_dir)\"
public_dir = \"($config_dir)/public\"
data_dir = \"($home)/.local/share/syntaxis\"
[leptos_dashboard]
# Leptos WASM dashboard configuration
deployed_at = \"($now)\"
artifacts_location = \"($config_dir)/public\"
wasm_target = \"wasm32-unknown-unknown\"
build_tool = \"trunk\"
css_generator = \"unocss\"
api_url_config = \"auto-detect [window.SYNTAXIS_CONFIG]\"
[environment]
SYNTAXIS_CONFIG_DIR = \"($config_dir)\"
SYNTAXIS_DATA_DIR = \"($home)/.local/share/syntaxis\"
SYNTAXIS_PUBLIC_DIR = \"($config_dir)/public\"
[verification]
all_binaries_installed = true
all_configs_deployed = true
wrappers_created = true
leptos_dashboard_deployed = true
manifest_version = \"1.0\"
"
try {
$manifest_content | save --force $manifest_path
print $" ✅ Registered installations in manifest"
print $" 📝 Manifest saved to: .syntaxis/manifest.toml"
} catch {
print $" ⚠️ Failed to create manifest"
}
}
def show_status [] {
print ""
print "📊 Installation Status"
print ""
let cargo_bin = ($env.HOME + "/.cargo/bin")
let binaries = (discover_binary_crates)
if ($binaries | length) == 0 {
print "⚠️ No binary crates found"
return
}
let status_rows = (
$binaries | each { |binary|
let bin_path = ($cargo_bin + "/" + $binary.binary_name)
let status = if ($bin_path | path exists) {
"✅ Installed"
} else {
"⚠️ Not installed"
}
{
binary: $binary.binary_name,
crate: $binary.crate_name,
status: $status,
path: $binary.crate_path,
wrapper: $binary.use_wrapper,
mode: $binary.install_mode,
config: $binary.config_src
}
}
)
print ($status_rows | table -i false | ansi strip)
print ""
print "📝 Note: Add ~/.cargo/bin to your PATH if binaries show as not installed"
print $" export PATH=\"\$HOME/.cargo/bin:\$PATH\""
print ""
}
def resolve_binary_from_action [action: string, all_binaries: list] {
# Resolve binary from action, supporting:
# 1. Full binary name: "syntaxis-cli"
# 2. Short name: "cli" (extracts last component from crate_path core/crates/cli)
# 3. Crate name: "syntaxis-cli"
let matching = (
$all_binaries | where { |b|
(($b.binary_name == $action) or
($b.crate_name == $action) or
(($b.crate_path | path basename) == $action))
}
)
return $matching
}
def list_binaries [] {
print ""
print "📦 Available Binaries (Auto-discovered)"
print ""
let binaries = (discover_binary_crates)
if ($binaries | length) == 0 {
print "⚠️ No binary crates found in workspace"
return
}
print ($binaries | table -i false | ansi strip)
#$binaries | each { |binary|
# print $" ($binary.crate_name) → ($binary.crate_path)"
#}
print ""
}
def main [
action?: string # Action: "all", "status", "list", or binary name
--rm-target # Remove target directories after build (default: false)
] {
show_logo
# Show help if no arguments or help requested
if ($action == null or $action == "help") {
print_help
return
}
# Handle status command
if ($action == "status") {
show_status
return
}
# Handle list command
if ($action == "list") {
list_binaries
return
}
# Discover available binaries
let all_binaries = (discover_binary_crates)
if ($all_binaries | length) == 0 {
print "❌ No binary crates found in workspace"
return
}
# Determine which binaries to install
let binaries_to_install = if ($action == "all") {
$all_binaries
} else {
# Find matching binary by binary_name, crate_name, or path
let matching = (resolve_binary_from_action $action $all_binaries)
if ($matching | length) == 0 {
print $"❌ Unknown binary: ($action)"
print ""
print "Available binaries:"
let binaries_list = $all_binaries
$binaries_list | each { |b|
let short_name = ($b.crate_path | path basename)
let display = $" - ($short_name) \(or ($b.binary_name), or ($b.crate_name)\)"
print $display
}
return
}
$matching
}
print ""
let binaries_count = ($binaries_to_install | length)
print $"🔨 Installing ($binaries_count) binary crate\(s\)... Remove target: ($rm_target)"
print ""
# Deploy library files first, before creating wrappers
# This ensures wrappers can import the libs when needed
print "📋 Deploying library files..."
deploy_libs $binaries_to_install
print ""
# Install each binary with immediate wrapper and config setup
# Stop on first failure
let install_result = (
$binaries_to_install | reduce {|binary, acc|
if $acc.failed {
# Already failed, skip remaining binaries
$acc
} else {
let display_name = $"(ansi blue)($binary.crate_name)(ansi reset)"
print $"📦 Step 1/3: Building ($display_name)..."
let result = (install_binary $binary $rm_target)
if $result.success {
# Build succeeded - immediately set up wrapper and config
let config_dir = ($env.HOME + "/.config/syntaxis")
let cargo_bin = ($env.HOME + "/.cargo/bin")
print $"📦 Step 2/3: Setting up wrapper and configuration..."
print $" Config Path: ($config_dir)"
print $" Wrapper Path: ($cargo_bin)"
# Create wrapper if applicable
if $binary.use_wrapper {
create_wrapper_for_binary $binary.binary_name $binary.use_wrapper $binary.config_src
}
# Deploy config if applicable
if ($binary.config_src | str length) > 0 {
deploy_config_for_binary $binary.crate_name $binary.config_src
}
print $"📦 Step 3/3: Registered ($display_name)"
print ""
{
installed: ($acc.installed | append $result)
failed: false
}
} else {
# Installation failed - stop processing
print "❌ Installation failed - stopping further installations"
print ""
{
installed: $acc.installed
failed: true
}
}
}
} -f {installed: [], failed: false}
)
let installed = $install_result.installed
let installation_failed = $install_result.failed
# Deploy additional global resources only if any binaries were installed
if ($installed | length) > 0 {
# deploy_leptos_dashboard
# Deploy default configuration (may already exist per-binary)
print ""
print "📋 Deploying global configuration..."
let config_dir = ($env.HOME + "/.config/syntaxis")
^mkdir -p $config_dir
^mkdir -p $"($config_dir)/features"
print $" ✅ Ensured config directory: ($config_dir)"
register_installations $installed
}
# Summary
print ""
print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
let installed_count = ($installed | length)
let total_count = ($binaries_to_install | length)
print $"✨ Summary: ($installed_count)/($total_count) binaries installed"
if $installation_failed {
print "❌ Installation stopped due to failure"
} else if $installed_count == $total_count {
print "🎉 All binaries installed successfully!"
} else if $installed_count > 0 {
let failed = ($total_count - $installed_count)
print $"⚠️ ($failed) installation(s) failed"
} else {
print "❌ No binaries were installed"
}
print ""
if ($installed | length) > 0 {
print "Next steps:"
$installed | each { |binary|
print $" ($binary.crate_name) --help # Get help"
}
}
print ""
}