provisioning/scripts/install-typedialog.sh
2026-01-08 21:22:57 +00:00

641 lines
19 KiB
Bash
Executable File
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 bash
# TypeDialog Installation Script
# Installs TypeDialog (main + backends + tools) for interactive configuration and provisioning
#
# TypeDialog Suite Components:
# BACKENDS (interactive forms):
# - typedialog CLI backend (step-by-step prompts)
# - typedialog-tui Terminal UI backend (3-panel interface)
# - typedialog-web Web backend (browser-based interface)
# TOOLS (provisioning):
# - typedialog-ag Type-safe AI agents for automation
# - typedialog-ai AI configuration assistant (HTTP server backend)
# - typedialog-prov-gen Provisioning structure generator
#
# Usage:
# ./install-typedialog.sh # Interactive mode
# ./install-typedialog.sh --version 0.1.0 # Specify version
# ./install-typedialog.sh --components all # Install all (default)
# ./install-typedialog.sh --components backends # Only: cli,tui,web
# ./install-typedialog.sh --components tools # Only: ag,ai,prov-gen
# ./install-typedialog.sh --components cli,tui,ag # Custom selection
# ./install-typedialog.sh --install-dir /usr/local/bin # Custom directory
# ./install-typedialog.sh --help # Show help
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
VERSIONS_FILE="${PROJECT_ROOT}/core/versions.ncl"
# All available components
BACKENDS=("cli" "tui" "web")
TOOLS=("ag" "ai" "prov-gen")
ALL_COMPONENTS=("${BACKENDS[@]}" "${TOOLS[@]}")
# Default values
TYPEDIALOG_VERSION=""
INSTALL_DIR=""
INSTALL_COMPONENTS="all"
FORCE=false
VERBOSE=false
SKIP_VALIDATION=false
INTERACTIVE=false
# ============================================================================
# Helper Functions
# ============================================================================
print_header() {
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}"
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}" >&2
}
debug() {
if [[ "$VERBOSE" == "true" ]]; then
echo -e "${BLUE}🔍 $1${NC}"
fi
}
# ============================================================================
# Platform Detection
# ============================================================================
detect_os() {
local os=""
case "$(uname -s)" in
Darwin*) os="macos" ;;
Linux*) os="linux" ;;
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
*) print_error "Unsupported OS: $(uname -s)"; return 1 ;;
esac
echo "$os"
}
detect_arch() {
local arch=""
case "$(uname -m)" in
x86_64|amd64) arch="x86_64" ;;
aarch64|arm64) arch="aarch64" ;;
*) print_error "Unsupported architecture: $(uname -m)"; return 1 ;;
esac
echo "$arch"
}
get_binary_name() {
local component=$1
local os=$2
# Map component name to binary name
local binary="typedialog"
case "$component" in
tui) binary="typedialog-tui" ;;
web) binary="typedialog-web" ;;
ag) binary="typedialog-ag" ;;
ai) binary="typedialog-ai" ;;
prov-gen) binary="typedialog-prov-gen" ;;
*) binary="typedialog" ;;
esac
case "$os" in
macos|linux)
echo "${binary}"
;;
windows)
echo "${binary}.exe"
;;
*)
echo "${binary}"
;;
esac
}
# ============================================================================
# Version Management
# ============================================================================
extract_version_from_ncl() {
local tool=$1
if [[ ! -f "$VERSIONS_FILE" ]]; then
debug "versions.ncl not found at $VERSIONS_FILE"
return 1
fi
# Extract version for tool using grep
local version_line=$(grep -A 2 "name = \"$tool\"" "$VERSIONS_FILE" | grep "current =" | head -1)
if [[ -z "$version_line" ]]; then
return 1
fi
# Extract version string (format: current = "X.Y.Z",)
local version=$(echo "$version_line" | sed 's/.*current = "\([^"]*\)".*/\1/')
if [[ -z "$version" || "$version" == "$version_line" ]]; then
return 1
fi
echo "$version"
}
# ============================================================================
# Installation Functions
# ============================================================================
ensure_install_dir() {
local dir=$1
if [[ ! -d "$dir" ]]; then
print_info "Creating installation directory: $dir"
mkdir -p "$dir" || {
print_error "Failed to create directory: $dir"
return 1
}
fi
# Verify directory is writable
if [[ ! -w "$dir" ]]; then
print_error "Installation directory is not writable: $dir"
return 1
fi
}
download_binary() {
local binary=$1
local version=$2
local os=$3
local arch=$4
local output_path=$5
# GitHub release URL pattern
local github_repo="typedialog/typedialog"
local release_url="https://github.com/${github_repo}/releases/download/v${version}"
# Determine binary name
local binary_name=$(get_binary_name "$binary" "$os")
local download_url="${release_url}/${binary_name}-${os}-${arch}"
print_info "Downloading: ${binary} (${version}) for ${os}/${arch}"
debug "URL: $download_url"
# Download with curl
if ! curl -fsSL --progress-bar "$download_url" -o "$output_path"; then
print_error "Failed to download from: $download_url"
return 1
fi
# Verify downloaded file
if [[ ! -f "$output_path" ]]; then
print_error "Downloaded file not found: $output_path"
return 1
fi
# Check file size (should not be tiny)
local file_size=$(stat -f%z "$output_path" 2>/dev/null || stat -c%s "$output_path" 2>/dev/null || echo 0)
if [[ $file_size -lt 1000000 ]]; then # Less than 1MB = likely error
print_warning "Downloaded file seems too small ($file_size bytes), may be error page"
return 1
fi
print_success "Downloaded: $output_path"
}
install_binary() {
local source=$1
local dest=$2
local binary_name=$3
# Copy binary
if ! cp "$source" "$dest"; then
print_error "Failed to copy binary from $source to $dest"
return 1
fi
# Make executable
if ! chmod +x "$dest"; then
print_error "Failed to make binary executable: $dest"
return 1
fi
print_success "Installed: $dest"
}
validate_installation() {
local binary=$1
local expected_version=$2
# Check if binary exists in PATH
if ! command -v "$binary" &> /dev/null; then
print_error "Binary not found in PATH: $binary"
return 1
fi
# Get installed version
local installed_version=$("$binary" --version 2>&1 | head -1 | sed 's/.*\s\([0-9.]*\)$/\1/' || echo "")
if [[ -z "$installed_version" ]]; then
print_warning "Could not determine installed version of $binary"
if [[ "$SKIP_VALIDATION" == "false" ]]; then
return 1
fi
return 0
fi
# Check version match
if [[ "$installed_version" != "$expected_version" ]]; then
print_warning "Version mismatch: expected $expected_version, got $installed_version"
if [[ "$FORCE" != "true" ]]; then
return 1
fi
fi
print_success "Validated: $binary ($installed_version)"
}
# ============================================================================
# Main Installation Flow
# ============================================================================
install_component() {
local component=$1
local version=$2
local install_dir=$3
local os=$4
local arch=$5
local component_type=${6:-""} # "backend" or "tool" for logging
local binary_name=$(get_binary_name "$component" "$os")
local display_name="typedialog"
if [[ "$component" != "cli" ]]; then
display_name="typedialog-${component}"
fi
print_info "Installing: ${display_name}${component_type:+ ($component_type)}"
# Create temp directory for downloads
local temp_dir=$(mktemp -d)
trap "rm -rf $temp_dir" EXIT
# Download component binary
local temp_binary="${temp_dir}/${binary_name}"
download_binary "$component" "$version" "$os" "$arch" "$temp_binary" || {
print_warning "Failed to download ${display_name} (may not be available for this version/platform)"
return 0 # Don't fail if component unavailable
}
# Install component binary
install_binary "$temp_binary" "${install_dir}/${binary_name}" "${display_name}" || {
print_warning "Failed to install ${display_name}"
return 0 # Don't fail if installation fails
}
return 0
}
install_components() {
local components=$1
local version=$2
local install_dir=$3
local os=$4
local arch=$5
if [[ -z "$components" || "$components" == "none" ]]; then
print_info "No components specified for installation"
return 0
fi
# Expand "all" and "backends"/"tools" aliases
local expanded_components=""
if [[ "$components" == "all" ]]; then
expanded_components="${ALL_COMPONENTS[*]}"
elif [[ "$components" == "backends" ]]; then
expanded_components="${BACKENDS[*]}"
elif [[ "$components" == "tools" ]]; then
expanded_components="${TOOLS[*]}"
else
# Assume comma-separated list
expanded_components=$(echo "$components" | sed 's/,/ /g')
fi
print_header "Installing TypeDialog Components"
local installed_count=0
local failed_count=0
for component in $expanded_components; do
component=$(echo "$component" | xargs) # trim whitespace
if [[ -z "$component" ]]; then
continue
fi
# Validate component name
if [[ ! " ${ALL_COMPONENTS[*]} " =~ " ${component} " ]]; then
print_warning "Unknown component: $component"
continue
fi
# Determine component type for logging
local ctype=""
if [[ " ${BACKENDS[*]} " =~ " ${component} " ]]; then
ctype="backend"
elif [[ " ${TOOLS[*]} " =~ " ${component} " ]]; then
ctype="tool"
fi
if install_component "$component" "$version" "$install_dir" "$os" "$arch" "$ctype"; then
((installed_count++))
else
((failed_count++))
fi
done
echo ""
print_success "Installation completed: ${installed_count} component(s) installed"
if [[ $failed_count -gt 0 ]]; then
print_warning "${failed_count} component(s) failed"
fi
}
validate_all() {
local install_dir=$1
print_header "Validating Installation"
# Validate main binary
validate_installation "typedialog" "$TYPEDIALOG_VERSION" || return 1
# Validate installed backends
for backend in tui web; do
if command -v "typedialog-${backend}" &> /dev/null; then
validate_installation "typedialog-${backend}" "$TYPEDIALOG_VERSION" || true
fi
done
return 0
}
# ============================================================================
# Help and Configuration
# ============================================================================
show_help() {
cat <<EOF
${BLUE}TypeDialog Installation Script${NC}
Complete installation of TypeDialog suite: backends (CLI/TUI/Web) + tools (AG/AI/ProvGen)
Usage: $(basename "$0") [OPTIONS]
OPTIONS:
-v, --version VERSION TypeDialog version to install (default: from versions.ncl)
-c, --components COMPONENTS Components to install (see COMPONENTS below)
-d, --install-dir DIR Installation directory (default: ~/.local/bin)
-i, --interactive Interactive mode (select components)
--force Force installation even on version mismatch
--skip-validation Skip validation checks after installation
--verbose Enable verbose output
-h, --help Show this help message
COMPONENTS:
Aliases (install multiple):
all All 6 components (backends + tools) - DEFAULT
backends All backends: cli, tui, web
tools All tools: ag, ai, prov-gen
Individual:
BACKENDS (interactive forms):
cli CLI backend (step-by-step prompts)
tui Terminal UI backend (3-panel interface)
web Web backend (browser-based)
TOOLS (provisioning):
ag AI agents for automation & transpilation
ai AI configuration assistant (HTTP server)
prov-gen Provisioning structure generator
Examples:
all typedialog + typedialog-tui + typedialog-web + typedialog-ag + typedialog-ai + typedialog-prov-gen
backends typedialog + typedialog-tui + typedialog-web
cli,tui,ag typedialog + typedialog-tui + typedialog-ag (custom selection)
EXAMPLES:
# Interactive mode (select what to install)
$(basename "$0") --interactive
# Install all components with defaults
$(basename "$0") --components all
# Install only backends
$(basename "$0") --components backends
# Install only tools
$(basename "$0") --components tools
# Custom selection
$(basename "$0") --components cli,tui,prov-gen
# Install specific version to system directory
$(basename "$0") --version 0.1.0 --components all --install-dir /usr/local/bin
# Verbose installation with validation
$(basename "$0") --verbose --components all
ENVIRONMENT VARIABLES:
TYPEDIALOG_INSTALL_DIR Override default installation directory
TYPEDIALOG_VERSION Override version detection
EOF
}
# ============================================================================
# Parse Arguments
# ============================================================================
prompt_for_components() {
echo ""
print_info "Select what to install:"
echo ""
echo " 1) All components (backends + tools) - RECOMMENDED"
echo " 2) Backends only (CLI, TUI, Web)"
echo " 3) Tools only (AG, AI, ProvGen)"
echo " 4) Custom selection"
echo " 5) Cancel"
echo ""
read -rp "Choice [1-5]: " choice
case "$choice" in
1) INSTALL_COMPONENTS="all" ;;
2) INSTALL_COMPONENTS="backends" ;;
3) INSTALL_COMPONENTS="tools" ;;
4)
echo ""
echo "Available components:"
echo " Backends: cli, tui, web"
echo " Tools: ag, ai, prov-gen"
echo ""
read -rp "Enter comma-separated list (e.g., cli,tui,ag): " custom
INSTALL_COMPONENTS="$custom"
;;
5)
print_info "Installation cancelled"
exit 0
;;
*)
print_error "Invalid choice"
exit 1
;;
esac
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
-v|--version)
TYPEDIALOG_VERSION="$2"
shift 2
;;
-c|--components)
INSTALL_COMPONENTS="$2"
shift 2
;;
-d|--install-dir)
INSTALL_DIR="$2"
shift 2
;;
-i|--interactive)
INTERACTIVE=true
shift
;;
--force)
FORCE=true
shift
;;
--skip-validation)
SKIP_VALIDATION=true
shift
;;
--verbose)
VERBOSE=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
print_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
}
# ============================================================================
# Main Entry Point
# ============================================================================
main() {
parse_args "$@"
print_header "TypeDialog Installation Suite"
# Interactive mode
if [[ "$INTERACTIVE" == "true" ]]; then
prompt_for_components
fi
# Detect platform
local os=$(detect_os) || exit 1
local arch=$(detect_arch) || exit 1
print_info "Platform: $os/$arch"
# Determine version
if [[ -z "$TYPEDIALOG_VERSION" ]]; then
TYPEDIALOG_VERSION=$(extract_version_from_ncl "typedialog")
if [[ -z "$TYPEDIALOG_VERSION" ]]; then
print_error "Could not determine TypeDialog version"
print_info "Please specify with: --version X.Y.Z"
exit 1
fi
fi
print_info "Version: $TYPEDIALOG_VERSION"
# Determine install directory
if [[ -z "$INSTALL_DIR" ]]; then
INSTALL_DIR="${TYPEDIALOG_INSTALL_DIR:-$HOME/.local/bin}"
fi
print_info "Install directory: $INSTALL_DIR"
# Display what will be installed
print_info "Components to install: $INSTALL_COMPONENTS"
echo ""
# Add install directory to PATH if not already there
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
export PATH="${INSTALL_DIR}:$PATH"
debug "Added $INSTALL_DIR to PATH"
fi
# Ensure install directory exists
ensure_install_dir "$INSTALL_DIR" || exit 1
# Install selected components
install_components "$INSTALL_COMPONENTS" "$TYPEDIALOG_VERSION" "$INSTALL_DIR" "$os" "$arch" || true
# Validate installation
if [[ "$SKIP_VALIDATION" != "true" ]]; then
validate_all "$INSTALL_DIR" || {
print_warning "Validation failed, but TypeDialog may still be functional"
}
fi
echo ""
print_success "Installation complete!"
echo ""
print_info "Next steps:"
print_info " 1. Verify: typedialog --version && typedialog-tui --version && typedialog-prov-gen --version"
print_info " 2. Setup: provisioning/scripts/setup-platform-config.sh"
print_info " 3. Test forms: typedialog form <config.toml>"
print_info " 4. Generate configs: typedialog-prov-gen generate --help"
print_info " 5. Use AI assistant: typedialog-ai serve"
echo ""
print_info "Documentation:"
print_info " - provisioning/.typedialog/platform/README.md"
print_info " - provisioning/.typedialog/provisioning/form.toml"
echo ""
return 0
}
# Run main function
main "$@"