1178 lines
34 KiB
Plaintext
1178 lines
34 KiB
Plaintext
#!/usr/bin/env nu
|
|
|
|
# Installer creation tool - creates installation scripts for different platforms
|
|
#
|
|
# Creates:
|
|
# - Platform-specific installation scripts
|
|
# - Package manager integration (deb, rpm, msi)
|
|
# - Automated configuration setup
|
|
# - Service installation and management
|
|
# - Uninstall scripts and rollback procedures
|
|
|
|
use std log
|
|
|
|
def main [
|
|
--distribution-path: string # Path to distribution to create installer for (required)
|
|
--output-dir: string = "installers" # Output directory for installers
|
|
--installer-types: string = "shell,package" # Installer types: shell, package, gui, all
|
|
--platforms: string = "linux,macos,windows" # Target platforms
|
|
--include-services: bool = true # Include service installation
|
|
--create-uninstaller: bool = true # Create uninstall scripts
|
|
--sign-packages: bool = false # Sign installation packages
|
|
--validate-installer: bool = true # Validate generated installers
|
|
--compression: string = "gzip" # Compression method for packages
|
|
--verbose: bool = false # Enable verbose logging
|
|
] -> record {
|
|
|
|
if $distribution_path == "" {
|
|
log error "Distribution path is required"
|
|
exit 1
|
|
}
|
|
|
|
let dist_root = ($distribution_path | path expand)
|
|
|
|
if not ($dist_root | path exists) {
|
|
log error $"Distribution path does not exist: ($dist_root)"
|
|
exit 1
|
|
}
|
|
|
|
let installer_types_list = if $installer_types == "all" {
|
|
["shell", "package", "gui"]
|
|
} else {
|
|
($installer_types | split row "," | each { str trim })
|
|
}
|
|
|
|
let platforms_list = ($platforms | split row "," | each { str trim })
|
|
|
|
let installer_config = {
|
|
distribution_path: $dist_root
|
|
output_dir: ($output_dir | path expand)
|
|
installer_types: $installer_types_list
|
|
platforms: $platforms_list
|
|
include_services: $include_services
|
|
create_uninstaller: $create_uninstaller
|
|
sign_packages: $sign_packages
|
|
validate_installer: $validate_installer
|
|
compression: $compression
|
|
verbose: $verbose
|
|
}
|
|
|
|
log info $"Starting installer creation with config: ($installer_config)"
|
|
|
|
# Ensure output directory exists
|
|
mkdir ($installer_config.output_dir)
|
|
|
|
let creation_results = []
|
|
|
|
try {
|
|
# Phase 1: Analyze distribution
|
|
let analysis_result = analyze_distribution $installer_config
|
|
|
|
let creation_results = ($creation_results | append { phase: "analysis", result: $analysis_result })
|
|
|
|
if $analysis_result.status != "success" {
|
|
log error $"Distribution analysis failed: ($analysis_result.reason)"
|
|
exit 1
|
|
}
|
|
|
|
# Phase 2: Create shell installers
|
|
let shell_result = if "shell" in $installer_config.installer_types {
|
|
create_shell_installers $installer_config $analysis_result
|
|
} else {
|
|
{ status: "skipped", reason: "shell installers not requested" }
|
|
}
|
|
|
|
let creation_results = ($creation_results | append { phase: "shell", result: $shell_result })
|
|
|
|
# Phase 3: Create package installers
|
|
let package_result = if "package" in $installer_config.installer_types {
|
|
create_package_installers $installer_config $analysis_result
|
|
} else {
|
|
{ status: "skipped", reason: "package installers not requested" }
|
|
}
|
|
|
|
let creation_results = ($creation_results | append { phase: "package", result: $package_result })
|
|
|
|
# Phase 4: Create GUI installers
|
|
let gui_result = if "gui" in $installer_config.installer_types {
|
|
create_gui_installers $installer_config $analysis_result
|
|
} else {
|
|
{ status: "skipped", reason: "GUI installers not requested" }
|
|
}
|
|
|
|
let creation_results = ($creation_results | append { phase: "gui", result: $gui_result })
|
|
|
|
# Phase 5: Create uninstallers
|
|
let uninstall_result = if $installer_config.create_uninstaller {
|
|
create_uninstall_scripts $installer_config $analysis_result
|
|
} else {
|
|
{ status: "skipped", reason: "uninstaller creation disabled" }
|
|
}
|
|
|
|
let creation_results = ($creation_results | append { phase: "uninstall", result: $uninstall_result })
|
|
|
|
# Phase 6: Validate installers
|
|
let validation_result = if $installer_config.validate_installer {
|
|
validate_installers $installer_config $creation_results
|
|
} else {
|
|
{ status: "skipped", reason: "installer validation disabled" }
|
|
}
|
|
|
|
let creation_results = ($creation_results | append { phase: "validation", result: $validation_result })
|
|
|
|
let summary = {
|
|
distribution_path: $installer_config.distribution_path
|
|
output_directory: $installer_config.output_dir
|
|
installer_types: ($installer_config.installer_types | length)
|
|
platforms: ($installer_config.platforms | length)
|
|
successful_phases: ($creation_results | where {|r| $r.result.status == "success"} | length)
|
|
total_phases: ($creation_results | length)
|
|
installers_created: (count_created_installers $creation_results)
|
|
installer_config: $installer_config
|
|
phases: $creation_results
|
|
}
|
|
|
|
log info $"Installer creation completed - ($summary.installers_created) installers created"
|
|
|
|
return $summary
|
|
|
|
} catch {|err|
|
|
log error $"Installer creation failed: ($err.msg)"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Analyze the distribution to understand its structure
|
|
def analyze_distribution [installer_config: record] -> record {
|
|
log info "Analyzing distribution structure..."
|
|
|
|
let start_time = (date now)
|
|
|
|
try {
|
|
let dist_path = $installer_config.distribution_path
|
|
|
|
# Detect distribution type and structure
|
|
let dist_info = {
|
|
is_archive: (($dist_path | path type) == "file")
|
|
is_directory: (($dist_path | path type) == "dir")
|
|
name: ($dist_path | path basename)
|
|
}
|
|
|
|
# If it's an archive, we need to extract it temporarily for analysis
|
|
let analysis_path = if $dist_info.is_archive {
|
|
extract_distribution_for_analysis $dist_path $installer_config
|
|
} else {
|
|
$dist_path
|
|
}
|
|
|
|
# Analyze distribution contents
|
|
let components = analyze_distribution_components $analysis_path
|
|
|
|
# Detect version information
|
|
let version_info = detect_distribution_version $analysis_path
|
|
|
|
# Analyze installation requirements
|
|
let requirements = analyze_installation_requirements $analysis_path $components
|
|
|
|
{
|
|
status: "success"
|
|
distribution_info: $dist_info
|
|
analysis_path: $analysis_path
|
|
components: $components
|
|
version_info: $version_info
|
|
requirements: $requirements
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
|
|
} catch {|err|
|
|
{
|
|
status: "failed"
|
|
reason: $err.msg
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
}
|
|
}
|
|
|
|
# Extract distribution archive for analysis
|
|
def extract_distribution_for_analysis [dist_path: string, installer_config: record] -> string {
|
|
let temp_dir = ($installer_config.output_dir | path join "tmp" "analysis")
|
|
mkdir $temp_dir
|
|
|
|
let dist_name = ($dist_path | path basename)
|
|
|
|
if ($dist_name | str ends-with ".tar.gz") or ($dist_name | str ends-with ".tgz") {
|
|
cd $temp_dir
|
|
tar -xzf $dist_path
|
|
} else if ($dist_name | str ends-with ".zip") {
|
|
cd $temp_dir
|
|
unzip $dist_path
|
|
} else {
|
|
error $"Unsupported archive format: ($dist_name)"
|
|
}
|
|
|
|
# Find the extracted directory (usually a single top-level directory)
|
|
let extracted_contents = (ls $temp_dir)
|
|
if ($extracted_contents | length) == 1 and (($extracted_contents | get 0.type) == "dir") {
|
|
return ($extracted_contents | get 0.name)
|
|
} else {
|
|
return $temp_dir
|
|
}
|
|
}
|
|
|
|
# Analyze distribution components
|
|
def analyze_distribution_components [analysis_path: string] -> record {
|
|
let components = {
|
|
has_platform: (($analysis_path | path join "platform") | path exists)
|
|
has_core: (($analysis_path | path join "core") | path exists)
|
|
has_config: (($analysis_path | path join "config") | path exists)
|
|
has_docs: (($analysis_path | path join "docs") | path exists)
|
|
has_services: (($analysis_path | path join "services") | path exists)
|
|
}
|
|
|
|
# Find executables
|
|
let executables = if $components.has_platform {
|
|
ls ($analysis_path | path join "platform") | where type == file | get name
|
|
} else if $components.has_core and (($analysis_path | path join "core" "bin") | path exists) {
|
|
ls ($analysis_path | path join "core" "bin") | where type == file | get name
|
|
} else {
|
|
[]
|
|
}
|
|
|
|
# Find configuration files
|
|
let config_files = if $components.has_config {
|
|
find ($analysis_path | path join "config") -name "*.toml" -o -name "*.yaml" -o -name "*.json" | lines
|
|
} else {
|
|
[]
|
|
}
|
|
|
|
# Find service definitions
|
|
let service_files = if $components.has_services {
|
|
find ($analysis_path | path join "services") -name "*.service" -o -name "*.yml" -o -name "*.yaml" | lines
|
|
} else {
|
|
[]
|
|
}
|
|
|
|
let components = ($components
|
|
| insert executables $executables
|
|
| insert config_files $config_files
|
|
| insert service_files $service_files
|
|
| insert total_size (get_directory_size $analysis_path))
|
|
|
|
return $components
|
|
}
|
|
|
|
# Detect distribution version
|
|
def detect_distribution_version [analysis_path: string] -> record {
|
|
# Try to find version from metadata
|
|
let metadata_files = [
|
|
($analysis_path | path join "core-metadata.json")
|
|
($analysis_path | path join "platform-metadata.json")
|
|
($analysis_path | path join "metadata.json")
|
|
($analysis_path | path join "VERSION")
|
|
]
|
|
|
|
for metadata_file in $metadata_files {
|
|
if ($metadata_file | path exists) {
|
|
let version = try {
|
|
if ($metadata_file | str ends-with ".json") {
|
|
let data = (open $metadata_file)
|
|
return $data.version
|
|
} else {
|
|
open $metadata_file --raw | str trim
|
|
}
|
|
} catch {
|
|
"unknown"
|
|
}
|
|
|
|
if $version != "unknown" {
|
|
return { version: $version, source: ($metadata_file | path basename) }
|
|
}
|
|
}
|
|
}
|
|
|
|
# Fallback: try to extract from directory name
|
|
let dir_name = ($analysis_path | path basename)
|
|
let version_match = ($dir_name | parse --regex ".*-([0-9]+\\.[0-9]+\\.[0-9]+)")
|
|
|
|
if ($version_match | length) > 0 {
|
|
return { version: ($version_match | get 0.capture0), source: "directory_name" }
|
|
}
|
|
|
|
return { version: "unknown", source: "none" }
|
|
}
|
|
|
|
# Analyze installation requirements
|
|
def analyze_installation_requirements [analysis_path: string, components: record] -> record {
|
|
let mut requirements = {
|
|
system_user: "provisioning"
|
|
system_group: "provisioning"
|
|
install_dirs: []
|
|
config_dirs: []
|
|
data_dirs: []
|
|
log_dirs: []
|
|
service_dependencies: []
|
|
port_requirements: []
|
|
}
|
|
|
|
# Standard installation directories
|
|
if $components.has_platform or ($components.executables | length) > 0 {
|
|
$requirements.install_dirs = ($requirements.install_dirs | append "/usr/local/bin")
|
|
}
|
|
|
|
if $components.has_core {
|
|
$requirements.install_dirs = ($requirements.install_dirs | append "/usr/local/lib/provisioning")
|
|
}
|
|
|
|
if $components.has_config {
|
|
$requirements.config_dirs = ($requirements.config_dirs | append "/etc/provisioning")
|
|
}
|
|
|
|
# Data and log directories
|
|
$requirements.data_dirs = ($requirements.data_dirs | append "/var/lib/provisioning")
|
|
$requirements.log_dirs = ($requirements.log_dirs | append "/var/log/provisioning")
|
|
|
|
# Service dependencies (would analyze service files to determine)
|
|
if $components.has_services {
|
|
$requirements.service_dependencies = ($requirements.service_dependencies | append "network.target")
|
|
}
|
|
|
|
# Port requirements (would analyze configuration to determine)
|
|
$requirements.port_requirements = ($requirements.port_requirements | append { port: 8080, description: "Main API" })
|
|
|
|
return $requirements
|
|
}
|
|
|
|
# Create shell installers for different platforms
|
|
def create_shell_installers [
|
|
installer_config: record
|
|
analysis_result: record
|
|
] -> record {
|
|
log info "Creating shell installers..."
|
|
|
|
let start_time = (date now)
|
|
|
|
try {
|
|
let mut created_installers = []
|
|
|
|
for platform in $installer_config.platforms {
|
|
match $platform {
|
|
"linux" | "macos" => {
|
|
let installer_result = create_unix_shell_installer $platform $installer_config $analysis_result
|
|
|
|
if $installer_result.status == "success" {
|
|
$created_installers = ($created_installers | append $installer_result)
|
|
}
|
|
}
|
|
"windows" => {
|
|
let installer_result = create_windows_batch_installer $installer_config $analysis_result
|
|
|
|
if $installer_result.status == "success" {
|
|
$created_installers = ($created_installers | append $installer_result)
|
|
}
|
|
}
|
|
_ => {
|
|
log warning $"Unsupported platform for shell installer: ($platform)"
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
status: "success"
|
|
installers_created: ($created_installers | length)
|
|
created_installers: $created_installers
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
|
|
} catch {|err|
|
|
{
|
|
status: "failed"
|
|
reason: $err.msg
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
}
|
|
}
|
|
|
|
# Create Unix shell installer
|
|
def create_unix_shell_installer [
|
|
platform: string
|
|
installer_config: record
|
|
analysis_result: record
|
|
] -> record {
|
|
let version = $analysis_result.version_info.version
|
|
let components = $analysis_result.components
|
|
let requirements = $analysis_result.requirements
|
|
|
|
let installer_content = $"#!/bin/bash
|
|
# Provisioning System Installer
|
|
# Platform: ($platform)
|
|
# Version: ($version)
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\\033[0;31m'
|
|
GREEN='\\033[0;32m'
|
|
YELLOW='\\033[1;33m'
|
|
NC='\\033[0m' # No Color
|
|
|
|
# Configuration
|
|
INSTALL_USER=\"($requirements.system_user)\"
|
|
INSTALL_GROUP=\"($requirements.system_group)\"
|
|
SERVICE_NAME=\"provisioning\"
|
|
|
|
# Installation directories
|
|
BIN_DIR=\"/usr/local/bin\"
|
|
LIB_DIR=\"/usr/local/lib/provisioning\"
|
|
CONFIG_DIR=\"/etc/provisioning\"
|
|
DATA_DIR=\"/var/lib/provisioning\"
|
|
LOG_DIR=\"/var/log/provisioning\"
|
|
|
|
# Functions
|
|
log_info() {
|
|
echo -e \"${GREEN}[INFO]${NC} $1\"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e \"${YELLOW}[WARN]${NC} $1\"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e \"${RED}[ERROR]${NC} $1\"
|
|
}
|
|
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error \"This script must be run as root (use sudo)\"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
create_user() {
|
|
if ! id \"$INSTALL_USER\" >/dev/null 2>&1; then
|
|
log_info \"Creating user: $INSTALL_USER\"
|
|
useradd -r -s /bin/false -m -d \"$DATA_DIR\" \"$INSTALL_USER\"
|
|
usermod -a -G \"$INSTALL_GROUP\" \"$INSTALL_USER\" 2>/dev/null || true
|
|
else
|
|
log_info \"User $INSTALL_USER already exists\"
|
|
fi
|
|
}
|
|
|
|
create_directories() {
|
|
log_info \"Creating directories...\"
|
|
mkdir -p \"$BIN_DIR\" \"$LIB_DIR\" \"$CONFIG_DIR\" \"$DATA_DIR\" \"$LOG_DIR\"
|
|
|
|
# Set ownership
|
|
chown -R \"$INSTALL_USER:$INSTALL_GROUP\" \"$DATA_DIR\" \"$LOG_DIR\"
|
|
chmod 755 \"$DATA_DIR\" \"$LOG_DIR\"
|
|
}
|
|
|
|
install_binaries() {
|
|
log_info \"Installing binaries...\"
|
|
if [[ -d \"platform\" ]]; then
|
|
cp platform/* \"$BIN_DIR/\"
|
|
chmod +x \"$BIN_DIR\"/provisioning-*
|
|
elif [[ -d \"core/bin\" ]]; then
|
|
cp core/bin/* \"$BIN_DIR/\"
|
|
chmod +x \"$BIN_DIR\"/provisioning*
|
|
else
|
|
log_error \"No binaries found to install\"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
install_libraries() {
|
|
if [[ -d \"core\" ]]; then
|
|
log_info \"Installing core libraries...\"
|
|
cp -r core/* \"$LIB_DIR/\"
|
|
chown -R root:root \"$LIB_DIR\"
|
|
find \"$LIB_DIR\" -type f -name \"*.nu\" -exec chmod 644 {} \\;
|
|
fi
|
|
}
|
|
|
|
install_configuration() {
|
|
if [[ -d \"config\" ]]; then
|
|
log_info \"Installing configuration...\"
|
|
cp config/*.toml \"$CONFIG_DIR/\" 2>/dev/null || true
|
|
cp config/*.template \"$CONFIG_DIR/\" 2>/dev/null || true
|
|
|
|
# Set secure permissions on config files
|
|
chown -R root:\"$INSTALL_GROUP\" \"$CONFIG_DIR\"
|
|
chmod 755 \"$CONFIG_DIR\"
|
|
find \"$CONFIG_DIR\" -name \"*.toml\" -exec chmod 640 {} \\;
|
|
fi
|
|
}
|
|
|
|
install_services() {
|
|
if [[ -d \"services\" ]] && command -v systemctl >/dev/null 2>&1; then
|
|
log_info \"Installing systemd services...\"
|
|
cp services/*.service /etc/systemd/system/ 2>/dev/null || true
|
|
systemctl daemon-reload
|
|
|
|
# Enable but don't start services
|
|
for service in services/*.service; do
|
|
if [[ -f \"$service\" ]]; then
|
|
service_name=$(basename \"$service\")
|
|
log_info \"Enabling service: $service_name\"
|
|
systemctl enable \"$service_name\"
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
configure_environment() {
|
|
log_info \"Configuring environment...\"
|
|
|
|
# Create environment file
|
|
cat > /etc/environment.d/provisioning.conf << EOF
|
|
PROVISIONING_HOME=\"$LIB_DIR\"
|
|
PROVISIONING_CONFIG=\"$CONFIG_DIR\"
|
|
PROVISIONING_DATA=\"$DATA_DIR\"
|
|
PROVISIONING_LOG=\"$LOG_DIR\"
|
|
EOF
|
|
|
|
# Create shell profile
|
|
cat > /etc/profile.d/provisioning.sh << 'EOF'
|
|
# Provisioning System Environment
|
|
if [ -d \"/usr/local/bin\" ]; then
|
|
case \":$PATH:\" in
|
|
*:/usr/local/bin:*) ;;
|
|
*) PATH=\"/usr/local/bin:$PATH\" ;;
|
|
esac
|
|
fi
|
|
export PROVISIONING_HOME=\"/usr/local/lib/provisioning\"
|
|
export PROVISIONING_CONFIG=\"/etc/provisioning\"
|
|
EOF
|
|
}
|
|
|
|
setup_logrotate() {
|
|
log_info \"Setting up log rotation...\"
|
|
cat > /etc/logrotate.d/provisioning << 'EOF'
|
|
/var/log/provisioning/*.log {
|
|
daily
|
|
missingok
|
|
rotate 52
|
|
compress
|
|
delaycompress
|
|
notifempty
|
|
create 644 provisioning provisioning
|
|
}
|
|
EOF
|
|
}
|
|
|
|
main() {
|
|
log_info \"Starting Provisioning System installation...\"
|
|
|
|
check_root
|
|
create_user
|
|
create_directories
|
|
install_binaries
|
|
install_libraries
|
|
install_configuration
|
|
|
|
if [[ \"$1\" != \"--no-services\" ]]; then
|
|
install_services
|
|
fi
|
|
|
|
configure_environment
|
|
setup_logrotate
|
|
|
|
log_info \"Installation completed successfully!\"
|
|
log_info \"\"
|
|
log_info \"Next steps:\"
|
|
log_info \" 1. Review configuration in $CONFIG_DIR\"
|
|
log_info \" 2. Start services: sudo systemctl start provisioning\"
|
|
log_info \" 3. Run 'provisioning help' to get started\"
|
|
log_info \"\"
|
|
log_info \"For more information, see the documentation in $LIB_DIR/docs\"
|
|
}
|
|
|
|
# Handle command line arguments
|
|
case \"$1\" in
|
|
--help|-h)
|
|
echo \"Provisioning System Installer ($version)\"
|
|
echo \"\"
|
|
echo \"Usage: $0 [OPTIONS]\"
|
|
echo \"\"
|
|
echo \"Options:\"
|
|
echo \" --no-services Skip service installation\"
|
|
echo \" --help, -h Show this help message\"
|
|
exit 0
|
|
;;
|
|
*)
|
|
main \"$@\"
|
|
;;
|
|
esac
|
|
"
|
|
|
|
let installer_file = ($installer_config.output_dir | path join $"install-($platform).sh")
|
|
$installer_content | save $installer_file
|
|
chmod +x $installer_file
|
|
|
|
{
|
|
platform: $platform
|
|
status: "success"
|
|
installer_type: "shell"
|
|
installer_file: $installer_file
|
|
size: ($installer_content | str length)
|
|
}
|
|
}
|
|
|
|
# Create Windows batch installer
|
|
def create_windows_batch_installer [
|
|
installer_config: record
|
|
analysis_result: record
|
|
] -> record {
|
|
let version = $analysis_result.version_info.version
|
|
let components = $analysis_result.components
|
|
|
|
let installer_content = $"@echo off
|
|
REM Provisioning System Installer
|
|
REM Platform: Windows
|
|
REM Version: ($version)
|
|
|
|
setlocal EnableDelayedExpansion
|
|
|
|
REM Configuration
|
|
set \"INSTALL_DIR=C:\\Program Files\\Provisioning\"
|
|
set \"CONFIG_DIR=C:\\ProgramData\\Provisioning\"
|
|
set \"SERVICE_NAME=ProvisioningService\"
|
|
|
|
echo.
|
|
echo ========================================
|
|
echo Provisioning System Installer ($version)
|
|
echo ========================================
|
|
echo.
|
|
|
|
REM Check for administrator privileges
|
|
net session >nul 2>&1
|
|
if %errorLevel% neq 0 (
|
|
echo ERROR: This script must be run as Administrator
|
|
echo Right-click and select \"Run as administrator\"
|
|
pause
|
|
exit /b 1
|
|
)
|
|
|
|
echo Creating directories...
|
|
mkdir \"%INSTALL_DIR%\\bin\" 2>nul
|
|
mkdir \"%INSTALL_DIR%\\lib\" 2>nul
|
|
mkdir \"%CONFIG_DIR%\" 2>nul
|
|
|
|
echo Installing binaries...
|
|
if exist \"platform\\\" (
|
|
xcopy platform\\* \"%INSTALL_DIR%\\bin\\\" /Y /Q
|
|
) else if exist \"core\\bin\\\" (
|
|
xcopy core\\bin\\* \"%INSTALL_DIR%\\bin\\\" /Y /Q
|
|
) else (
|
|
echo ERROR: No binaries found to install
|
|
pause
|
|
exit /b 1
|
|
)
|
|
|
|
echo Installing libraries...
|
|
if exist \"core\\\" (
|
|
xcopy core\\* \"%INSTALL_DIR%\\lib\\\" /Y /Q /S
|
|
)
|
|
|
|
echo Installing configuration...
|
|
if exist \"config\\\" (
|
|
xcopy config\\* \"%CONFIG_DIR%\\\" /Y /Q /S
|
|
)
|
|
|
|
echo Configuring environment...
|
|
REM Add installation directory to system PATH
|
|
for /f \"usebackq tokens=2,*\" %%A in (`reg query HKCU\\Environment /v PATH`) do set \"current_path=%%B\"
|
|
echo !current_path! | findstr /C:\"%INSTALL_DIR%\\bin\" >nul
|
|
if !errorLevel! neq 0 (
|
|
setx PATH \"!current_path!;%INSTALL_DIR%\\bin\"
|
|
echo Added %INSTALL_DIR%\\bin to PATH
|
|
)
|
|
|
|
REM Set environment variables
|
|
setx PROVISIONING_HOME \"%INSTALL_DIR%\"
|
|
setx PROVISIONING_CONFIG \"%CONFIG_DIR%\"
|
|
|
|
echo.
|
|
echo Installation completed successfully!
|
|
echo.
|
|
echo Next steps:
|
|
echo 1. Review configuration in %CONFIG_DIR%
|
|
echo 2. Run 'provisioning-orchestrator --help' to get started
|
|
echo 3. Check the documentation in %INSTALL_DIR%\\lib\\docs
|
|
echo.
|
|
echo NOTE: You may need to restart your command prompt for PATH changes to take effect.
|
|
echo.
|
|
pause
|
|
"
|
|
|
|
let installer_file = ($installer_config.output_dir | path join "install-windows.bat")
|
|
$installer_content | save $installer_file
|
|
|
|
{
|
|
platform: "windows"
|
|
status: "success"
|
|
installer_type: "batch"
|
|
installer_file: $installer_file
|
|
size: ($installer_content | str length)
|
|
}
|
|
}
|
|
|
|
# Create package installers (deb, rpm, msi)
|
|
def create_package_installers [
|
|
installer_config: record
|
|
analysis_result: record
|
|
] -> record {
|
|
log info "Creating package installers..."
|
|
|
|
let start_time = (date now)
|
|
|
|
# Package creation would involve:
|
|
# 1. Creating package control files
|
|
# 2. Building packages with appropriate tools
|
|
# 3. Signing packages if requested
|
|
|
|
log warning "Package installer creation not fully implemented"
|
|
|
|
{
|
|
status: "skipped"
|
|
reason: "package installers not fully implemented"
|
|
installers_created: 0
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
}
|
|
|
|
# Create GUI installers
|
|
def create_gui_installers [
|
|
installer_config: record
|
|
analysis_result: record
|
|
] -> record {
|
|
log info "Creating GUI installers..."
|
|
|
|
let start_time = (date now)
|
|
|
|
# GUI installer creation would involve:
|
|
# 1. Creating installer definition files
|
|
# 2. Using platform-specific tools (NSIS, InstallShield, etc.)
|
|
# 3. Including custom installation wizards
|
|
|
|
log warning "GUI installer creation not fully implemented"
|
|
|
|
{
|
|
status: "skipped"
|
|
reason: "GUI installers not fully implemented"
|
|
installers_created: 0
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
}
|
|
|
|
# Create uninstall scripts
|
|
def create_uninstall_scripts [
|
|
installer_config: record
|
|
analysis_result: record
|
|
] -> record {
|
|
log info "Creating uninstall scripts..."
|
|
|
|
let start_time = (date now)
|
|
|
|
try {
|
|
let mut created_uninstallers = []
|
|
|
|
for platform in $installer_config.platforms {
|
|
match $platform {
|
|
"linux" | "macos" => {
|
|
let uninstaller_result = create_unix_uninstaller $platform $installer_config $analysis_result
|
|
|
|
if $uninstaller_result.status == "success" {
|
|
$created_uninstallers = ($created_uninstallers | append $uninstaller_result)
|
|
}
|
|
}
|
|
"windows" => {
|
|
let uninstaller_result = create_windows_uninstaller $installer_config $analysis_result
|
|
|
|
if $uninstaller_result.status == "success" {
|
|
$created_uninstallers = ($created_uninstallers | append $uninstaller_result)
|
|
}
|
|
}
|
|
_ => {
|
|
log warning $"Unsupported platform for uninstaller: ($platform)"
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
status: "success"
|
|
uninstallers_created: ($created_uninstallers | length)
|
|
created_uninstallers: $created_uninstallers
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
|
|
} catch {|err|
|
|
{
|
|
status: "failed"
|
|
reason: $err.msg
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
}
|
|
}
|
|
|
|
# Create Unix uninstaller
|
|
def create_unix_uninstaller [
|
|
platform: string
|
|
installer_config: record
|
|
analysis_result: record
|
|
] -> record {
|
|
let version = $analysis_result.version_info.version
|
|
let requirements = $analysis_result.requirements
|
|
|
|
let uninstaller_content = $"#!/bin/bash
|
|
# Provisioning System Uninstaller
|
|
# Platform: ($platform)
|
|
# Version: ($version)
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\\033[0;31m'
|
|
GREEN='\\033[0;32m'
|
|
YELLOW='\\033[1;33m'
|
|
NC='\\033[0m'
|
|
|
|
log_info() {
|
|
echo -e \"${GREEN}[INFO]${NC} $1\"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e \"${YELLOW}[WARN]${NC} $1\"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e \"${RED}[ERROR]${NC} $1\"
|
|
}
|
|
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error \"This script must be run as root (use sudo)\"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
confirm_uninstall() {
|
|
echo \"This will completely remove the Provisioning System from your system.\"
|
|
echo \"This includes:\"
|
|
echo \" - All binaries and libraries\"
|
|
echo \" - Configuration files\"
|
|
echo \" - Service definitions\"
|
|
echo \" - Log files and data\"
|
|
echo \"\"
|
|
read -p \"Are you sure you want to continue? (y/N): \" -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
log_info \"Uninstallation cancelled\"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
stop_services() {
|
|
log_info \"Stopping services...\"
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
systemctl stop provisioning* 2>/dev/null || true
|
|
systemctl disable provisioning* 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
remove_binaries() {
|
|
log_info \"Removing binaries...\"
|
|
rm -f /usr/local/bin/provisioning*
|
|
}
|
|
|
|
remove_libraries() {
|
|
log_info \"Removing libraries...\"
|
|
rm -rf /usr/local/lib/provisioning
|
|
}
|
|
|
|
remove_configuration() {
|
|
log_info \"Removing configuration...\"
|
|
rm -rf /etc/provisioning
|
|
}
|
|
|
|
remove_data() {
|
|
if [[ \"$1\" == \"--keep-data\" ]]; then
|
|
log_info \"Keeping data directory (--keep-data specified)\"
|
|
else
|
|
log_info \"Removing data and logs...\"
|
|
rm -rf /var/lib/provisioning
|
|
rm -rf /var/log/provisioning
|
|
fi
|
|
}
|
|
|
|
remove_services() {
|
|
log_info \"Removing service definitions...\"
|
|
rm -f /etc/systemd/system/provisioning*.service
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
systemctl daemon-reload
|
|
fi
|
|
}
|
|
|
|
remove_environment() {
|
|
log_info \"Removing environment configuration...\"
|
|
rm -f /etc/environment.d/provisioning.conf
|
|
rm -f /etc/profile.d/provisioning.sh
|
|
rm -f /etc/logrotate.d/provisioning
|
|
}
|
|
|
|
remove_user() {
|
|
if [[ \"$1\" == \"--keep-user\" ]]; then
|
|
log_info \"Keeping system user (--keep-user specified)\"
|
|
else
|
|
log_info \"Removing system user...\"
|
|
userdel provisioning 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
log_info \"Starting Provisioning System uninstallation...\"
|
|
|
|
check_root
|
|
confirm_uninstall
|
|
|
|
stop_services
|
|
remove_services
|
|
remove_binaries
|
|
remove_libraries
|
|
remove_configuration
|
|
remove_data \"$1\"
|
|
remove_environment
|
|
remove_user \"$1\"
|
|
|
|
log_info \"Uninstallation completed successfully!\"
|
|
log_info \"\"
|
|
log_info \"Thank you for using the Provisioning System.\"
|
|
}
|
|
|
|
case \"$1\" in
|
|
--help|-h)
|
|
echo \"Provisioning System Uninstaller ($version)\"
|
|
echo \"\"
|
|
echo \"Usage: $0 [OPTIONS]\"
|
|
echo \"\"
|
|
echo \"Options:\"
|
|
echo \" --keep-data Keep data directory and logs\"
|
|
echo \" --keep-user Keep system user account\"
|
|
echo \" --help, -h Show this help message\"
|
|
exit 0
|
|
;;
|
|
*)
|
|
main \"$@\"
|
|
;;
|
|
esac
|
|
"
|
|
|
|
let uninstaller_file = ($installer_config.output_dir | path join $"uninstall-($platform).sh")
|
|
$uninstaller_content | save $uninstaller_file
|
|
chmod +x $uninstaller_file
|
|
|
|
{
|
|
platform: $platform
|
|
status: "success"
|
|
uninstaller_type: "shell"
|
|
uninstaller_file: $uninstaller_file
|
|
size: ($uninstaller_content | str length)
|
|
}
|
|
}
|
|
|
|
# Create Windows uninstaller
|
|
def create_windows_uninstaller [
|
|
installer_config: record
|
|
analysis_result: record
|
|
] -> record {
|
|
let version = $analysis_result.version_info.version
|
|
|
|
let uninstaller_content = $"@echo off
|
|
REM Provisioning System Uninstaller
|
|
REM Platform: Windows
|
|
REM Version: ($version)
|
|
|
|
setlocal EnableDelayedExpansion
|
|
|
|
echo.
|
|
echo ==========================================
|
|
echo Provisioning System Uninstaller ($version)
|
|
echo ==========================================
|
|
echo.
|
|
|
|
REM Check for administrator privileges
|
|
net session >nul 2>&1
|
|
if %errorLevel% neq 0 (
|
|
echo ERROR: This script must be run as Administrator
|
|
pause
|
|
exit /b 1
|
|
)
|
|
|
|
echo This will completely remove the Provisioning System from your system.
|
|
echo.
|
|
set /p \"confirm=Are you sure you want to continue? (y/N): \"
|
|
if /I not \"!confirm!\"==\"y\" (
|
|
echo Uninstallation cancelled
|
|
exit /b 0
|
|
)
|
|
|
|
echo.
|
|
echo Stopping services...
|
|
REM Stop any running services here
|
|
|
|
echo Removing installation directory...
|
|
if exist \"C:\\Program Files\\Provisioning\" (
|
|
rmdir /S /Q \"C:\\Program Files\\Provisioning\"
|
|
)
|
|
|
|
echo Removing configuration...
|
|
if exist \"C:\\ProgramData\\Provisioning\" (
|
|
rmdir /S /Q \"C:\\ProgramData\\Provisioning\"
|
|
)
|
|
|
|
echo Removing environment variables...
|
|
reg delete HKCU\\Environment /v PROVISIONING_HOME /f 2>nul
|
|
reg delete HKCU\\Environment /v PROVISIONING_CONFIG /f 2>nul
|
|
|
|
echo Removing from PATH...
|
|
for /f \"usebackq tokens=2,*\" %%A in (`reg query HKCU\\Environment /v PATH 2^>nul`) do (
|
|
set \"current_path=%%B\"
|
|
set \"new_path=!current_path:C:\\Program Files\\Provisioning\\bin;=!\"
|
|
set \"new_path=!new_path:;C:\\Program Files\\Provisioning\\bin=!\"
|
|
if not \"!new_path!\"==\"!current_path!\" (
|
|
setx PATH \"!new_path!\"
|
|
echo Removed from PATH
|
|
)
|
|
)
|
|
|
|
echo.
|
|
echo Uninstallation completed successfully!
|
|
echo Thank you for using the Provisioning System.
|
|
echo.
|
|
pause
|
|
"
|
|
|
|
let uninstaller_file = ($installer_config.output_dir | path join "uninstall-windows.bat")
|
|
$uninstaller_content | save $uninstaller_file
|
|
|
|
{
|
|
platform: "windows"
|
|
status: "success"
|
|
uninstaller_type: "batch"
|
|
uninstaller_file: $uninstaller_file
|
|
size: ($uninstaller_content | str length)
|
|
}
|
|
}
|
|
|
|
# Validate generated installers
|
|
def validate_installers [
|
|
installer_config: record
|
|
creation_results: list
|
|
] -> record {
|
|
log info "Validating installers..."
|
|
|
|
let start_time = (date now)
|
|
|
|
# Installer validation would involve:
|
|
# 1. Syntax checking of shell scripts
|
|
# 2. Testing installation in clean environments
|
|
# 3. Verifying uninstaller functionality
|
|
|
|
log warning "Installer validation not fully implemented"
|
|
|
|
{
|
|
status: "skipped"
|
|
reason: "installer validation not fully implemented"
|
|
validated_installers: 0
|
|
duration: ((date now) - $start_time)
|
|
}
|
|
}
|
|
|
|
# Count created installers from results
|
|
def count_created_installers [creation_results: list] -> int {
|
|
let shell_count = try {
|
|
let shell_result = ($creation_results | where phase == "shell" | get 0.result)
|
|
$shell_result.installers_created
|
|
} catch { 0 }
|
|
|
|
let package_count = try {
|
|
let package_result = ($creation_results | where phase == "package" | get 0.result)
|
|
$package_result.installers_created
|
|
} catch { 0 }
|
|
|
|
let gui_count = try {
|
|
let gui_result = ($creation_results | where phase == "gui" | get 0.result)
|
|
$gui_result.installers_created
|
|
} catch { 0 }
|
|
|
|
let uninstall_count = try {
|
|
let uninstall_result = ($creation_results | where phase == "uninstall" | get 0.result)
|
|
$uninstall_result.uninstallers_created
|
|
} catch { 0 }
|
|
|
|
return ($shell_count + $package_count + $gui_count + $uninstall_count)
|
|
}
|
|
|
|
# Get directory size helper
|
|
def get_directory_size [dir: string] -> int {
|
|
if not ($dir | path exists) {
|
|
return 0
|
|
}
|
|
|
|
try {
|
|
find $dir -type f | each {|file| ls $file | get 0.size } | math sum | if $in == null { 0 } else { $in }
|
|
} catch {
|
|
0
|
|
}
|
|
}
|
|
|
|
# Show installer creation status
|
|
def "main status" [distribution_path: string = ""] {
|
|
if $distribution_path == "" {
|
|
return {
|
|
error: "distribution path required"
|
|
usage: "main status <distribution-path>"
|
|
supported_platforms: ["linux", "macos", "windows"]
|
|
installer_types: ["shell", "package", "gui"]
|
|
}
|
|
}
|
|
|
|
let dist_path = ($distribution_path | path expand)
|
|
|
|
if not ($dist_path | path exists) {
|
|
return {
|
|
error: "distribution path does not exist"
|
|
path: $dist_path
|
|
}
|
|
}
|
|
|
|
# Quick analysis of distribution
|
|
let dist_info = {
|
|
path: $dist_path
|
|
type: ($dist_path | path type)
|
|
name: ($dist_path | path basename)
|
|
size: (get_directory_size $dist_path)
|
|
}
|
|
|
|
{
|
|
distribution: $dist_info
|
|
can_create_installers: true
|
|
supported_platforms: ["linux", "macos", "windows"]
|
|
installer_types: ["shell", "package", "gui"]
|
|
features: {
|
|
shell_installers: true
|
|
package_installers: false # Not fully implemented
|
|
gui_installers: false # Not fully implemented
|
|
uninstallers: true
|
|
service_management: true
|
|
}
|
|
}
|
|
}
|
|
|
|
# Quick installer creation with minimal options
|
|
def "main quick" [
|
|
distribution_path: string # Distribution to create installer for
|
|
--platform: string = "linux" # Single platform
|
|
--output-dir: string = "installers" # Output directory
|
|
] {
|
|
main $distribution_path --platforms $platform --installer-types shell --output-dir $output_dir
|
|
} |