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)
889 lines
32 KiB
Plaintext
889 lines
32 KiB
Plaintext
#!/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 ""
|
||
}
|