416 lines
13 KiB
Plaintext
416 lines
13 KiB
Plaintext
|
|
#!/usr/bin/env nu
|
||
|
|
|
||
|
|
# Main Deployment Script for Provisioning Platform Installer
|
||
|
|
#
|
||
|
|
# Provides three deployment modes:
|
||
|
|
# 1. Interactive: TUI-based deployment with user prompts
|
||
|
|
# 2. Headless: CLI-based deployment with command-line args
|
||
|
|
# 3. Unattended: Fully automated deployment from config files
|
||
|
|
|
||
|
|
use helpers.nu *
|
||
|
|
use platforms.nu *
|
||
|
|
use integration.nu *
|
||
|
|
|
||
|
|
# Deploy provisioning platform interactively
|
||
|
|
#
|
||
|
|
# Uses the Rust TUI installer for interactive configuration
|
||
|
|
# and deployment. This is the recommended method for first-time
|
||
|
|
# users and development environments.
|
||
|
|
#
|
||
|
|
# @returns: Deployment result record
|
||
|
|
export def deploy-interactive []: nothing -> record {
|
||
|
|
print "🚀 Starting interactive deployment..."
|
||
|
|
|
||
|
|
# Check prerequisites
|
||
|
|
let prereqs = check-prerequisites
|
||
|
|
if not $prereqs.success {
|
||
|
|
error make {
|
||
|
|
msg: "Prerequisites check failed"
|
||
|
|
label: {text: $prereqs.error}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Launch Rust TUI installer
|
||
|
|
let installer_path = get-installer-path
|
||
|
|
|
||
|
|
try {
|
||
|
|
^$installer_path
|
||
|
|
|
||
|
|
{
|
||
|
|
success: true
|
||
|
|
mode: "interactive"
|
||
|
|
message: "Interactive deployment completed"
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
} catch {|err|
|
||
|
|
error make {
|
||
|
|
msg: "Interactive deployment failed"
|
||
|
|
label: {text: $err.msg}
|
||
|
|
help: "Check installer logs for details"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Deploy provisioning platform in headless mode
|
||
|
|
#
|
||
|
|
# Uses command-line arguments to configure and deploy without
|
||
|
|
# interactive prompts. Suitable for scripted deployments and
|
||
|
|
# environments where TUI is not available.
|
||
|
|
#
|
||
|
|
# @param platform: Target platform (docker, podman, kubernetes, orbstack)
|
||
|
|
# @param mode: Deployment mode (solo, multi-user, cicd, enterprise)
|
||
|
|
# @param domain: Domain/hostname for services (default: localhost)
|
||
|
|
# @param services: Comma-separated list of services to enable
|
||
|
|
# @param auto_confirm: Skip confirmation prompts if true
|
||
|
|
# @returns: Deployment result record
|
||
|
|
export def deploy-headless [
|
||
|
|
platform: string # Target platform
|
||
|
|
mode: string # Deployment mode
|
||
|
|
--domain: string = "localhost" # Domain/hostname
|
||
|
|
--services: string = "" # Services to enable
|
||
|
|
--auto-confirm # Skip confirmations
|
||
|
|
--config-only # Generate config only
|
||
|
|
]: nothing -> record {
|
||
|
|
print $"🚀 Starting headless deployment: ($mode) on ($platform)"
|
||
|
|
|
||
|
|
# Validate inputs
|
||
|
|
let validation = validate-deployment-params $platform $mode
|
||
|
|
if not $validation.success {
|
||
|
|
error make {
|
||
|
|
msg: "Invalid deployment parameters"
|
||
|
|
label: {text: $validation.error}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check prerequisites
|
||
|
|
let prereqs = check-prerequisites
|
||
|
|
if not $prereqs.success {
|
||
|
|
error make {
|
||
|
|
msg: "Prerequisites check failed"
|
||
|
|
label: {text: $prereqs.error}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Build config
|
||
|
|
let config = build-deployment-config {
|
||
|
|
platform: $platform
|
||
|
|
mode: $mode
|
||
|
|
domain: $domain
|
||
|
|
services: ($services | split row "," | where {|s| $s != ""})
|
||
|
|
auto_generate_secrets: true
|
||
|
|
}
|
||
|
|
|
||
|
|
# Save config
|
||
|
|
let config_path = save-deployment-config $config
|
||
|
|
print $"📝 Configuration saved to: ($config_path)"
|
||
|
|
|
||
|
|
# Stop if config-only mode
|
||
|
|
if $config_only {
|
||
|
|
return {
|
||
|
|
success: true
|
||
|
|
mode: "headless"
|
||
|
|
config_path: $config_path
|
||
|
|
message: "Configuration generated successfully"
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Confirm deployment unless auto-confirmed
|
||
|
|
if not $auto_confirm {
|
||
|
|
let confirmed = confirm-deployment $config
|
||
|
|
if not $confirmed {
|
||
|
|
return {
|
||
|
|
success: false
|
||
|
|
mode: "headless"
|
||
|
|
message: "Deployment cancelled by user"
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Execute deployment
|
||
|
|
let result = match $platform {
|
||
|
|
"docker" => { deploy-docker $config }
|
||
|
|
"podman" => { deploy-podman $config }
|
||
|
|
"kubernetes" => { deploy-kubernetes $config }
|
||
|
|
"orbstack" => { deploy-orbstack $config }
|
||
|
|
_ => {
|
||
|
|
error make {msg: $"Unsupported platform: ($platform)"}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Run health checks
|
||
|
|
if $result.success {
|
||
|
|
print "🔍 Running health checks..."
|
||
|
|
let health = check-deployment-health $config
|
||
|
|
|
||
|
|
if not $health.success {
|
||
|
|
print "⚠️ Health checks failed - attempting rollback..."
|
||
|
|
rollback-deployment $config
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: false
|
||
|
|
mode: "headless"
|
||
|
|
error: $health.error
|
||
|
|
message: "Deployment rolled back due to health check failures"
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$result
|
||
|
|
}
|
||
|
|
|
||
|
|
# Deploy provisioning platform in unattended mode
|
||
|
|
#
|
||
|
|
# Fully automated deployment using pre-configured settings from
|
||
|
|
# a TOML config file, MCP server, or API. No user interaction
|
||
|
|
# required. Includes strict validation, progress tracking, and
|
||
|
|
# automated rollback on failure.
|
||
|
|
#
|
||
|
|
# @param config_source: Configuration source (file, mcp, api)
|
||
|
|
# @param source_path: Path/URL to config source
|
||
|
|
# @param webhook_url: Optional webhook for notifications
|
||
|
|
# @returns: Deployment result record
|
||
|
|
export def deploy-unattended [
|
||
|
|
config_source: string # Config source type (file, mcp, api)
|
||
|
|
source_path: string # Path/URL to config
|
||
|
|
--webhook-url: string = "" # Notification webhook
|
||
|
|
--strict # Strict validation mode
|
||
|
|
]: nothing -> record {
|
||
|
|
print $"🚀 Starting unattended deployment from ($config_source)..."
|
||
|
|
|
||
|
|
# Notify start if webhook configured
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url {
|
||
|
|
event: "deployment_started"
|
||
|
|
source: $config_source
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Load configuration from source
|
||
|
|
let config = match $config_source {
|
||
|
|
"file" => { load-config-from-file $source_path }
|
||
|
|
"mcp" => { load-config-from-mcp $source_path }
|
||
|
|
"api" => { load-config-from-api $source_path }
|
||
|
|
_ => {
|
||
|
|
error make {msg: $"Invalid config source: ($config_source)"}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Strict validation in unattended mode
|
||
|
|
let validation = if $strict {
|
||
|
|
validate-deployment-config $config --strict
|
||
|
|
} else {
|
||
|
|
validate-deployment-config $config
|
||
|
|
}
|
||
|
|
|
||
|
|
if not $validation.success {
|
||
|
|
let error_result = {
|
||
|
|
success: false
|
||
|
|
mode: "unattended"
|
||
|
|
error: $validation.error
|
||
|
|
message: "Config validation failed"
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url ($error_result | merge {event: "deployment_failed"})
|
||
|
|
}
|
||
|
|
|
||
|
|
return $error_result
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check prerequisites
|
||
|
|
let prereqs = check-prerequisites
|
||
|
|
if not $prereqs.success {
|
||
|
|
let error_result = {
|
||
|
|
success: false
|
||
|
|
mode: "unattended"
|
||
|
|
error: $prereqs.error
|
||
|
|
message: "Prerequisites check failed"
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url ($error_result | merge {event: "deployment_failed"})
|
||
|
|
}
|
||
|
|
|
||
|
|
return $error_result
|
||
|
|
}
|
||
|
|
|
||
|
|
# Execute deployment steps with progress tracking
|
||
|
|
try {
|
||
|
|
# Step 1: Validate configuration
|
||
|
|
print "[1/7] Validating configuration"
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url {event: "deployment_progress", step: "Validating configuration", progress: 14.3, timestamp: (date now)}
|
||
|
|
}
|
||
|
|
let _ = validate-deployment-config $config
|
||
|
|
|
||
|
|
# Step 2: Check platform availability
|
||
|
|
print "[2/7] Checking platform availability"
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url {event: "deployment_progress", step: "Checking platform availability", progress: 28.6, timestamp: (date now)}
|
||
|
|
}
|
||
|
|
let platform_check = check-platform-availability $config.platform
|
||
|
|
if not $platform_check.available {
|
||
|
|
error make {msg: $"Platform ($config.platform) not available"}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Step 3: Generate secrets
|
||
|
|
print "[3/7] Generating secrets"
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url {event: "deployment_progress", step: "Generating secrets", progress: 42.9, timestamp: (date now)}
|
||
|
|
}
|
||
|
|
let secrets = generate-secrets $config
|
||
|
|
|
||
|
|
# Step 4: Create deployment manifests
|
||
|
|
print "[4/7] Creating deployment manifests"
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url {event: "deployment_progress", step: "Creating deployment manifests", progress: 57.2, timestamp: (date now)}
|
||
|
|
}
|
||
|
|
let manifests = create-deployment-manifests $config $secrets
|
||
|
|
|
||
|
|
# Step 5: Deploy services
|
||
|
|
print "[5/7] Deploying services"
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url {event: "deployment_progress", step: "Deploying services", progress: 71.5, timestamp: (date now)}
|
||
|
|
}
|
||
|
|
let deploy_result = match $config.platform {
|
||
|
|
"docker" => { deploy-docker $config }
|
||
|
|
"podman" => { deploy-podman $config }
|
||
|
|
"kubernetes" => { deploy-kubernetes $config }
|
||
|
|
"orbstack" => { deploy-orbstack $config }
|
||
|
|
_ => {
|
||
|
|
error make {msg: $"Unsupported platform: ($config.platform)"}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if not $deploy_result.success {
|
||
|
|
error make {msg: $"Deployment failed: ($deploy_result.error)"}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Step 6: Run health checks
|
||
|
|
print "[6/7] Running health checks"
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url {event: "deployment_progress", step: "Running health checks", progress: 85.8, timestamp: (date now)}
|
||
|
|
}
|
||
|
|
let health = check-deployment-health $config
|
||
|
|
|
||
|
|
if not $health.success {
|
||
|
|
error make {msg: $"Health checks failed: ($health.error)"}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Step 7: Finalize
|
||
|
|
print "[7/7] Finalizing deployment"
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url {event: "deployment_progress", step: "Finalizing deployment", progress: 100.0, timestamp: (date now)}
|
||
|
|
}
|
||
|
|
|
||
|
|
let success_result = {
|
||
|
|
success: true
|
||
|
|
mode: "unattended"
|
||
|
|
platform: $config.platform
|
||
|
|
deployment_mode: $config.mode
|
||
|
|
services: ($config.services | get name)
|
||
|
|
message: "Deployment completed successfully"
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url ($success_result | merge {event: "deployment_completed"})
|
||
|
|
}
|
||
|
|
|
||
|
|
print "✅ Deployment completed successfully!"
|
||
|
|
$success_result
|
||
|
|
|
||
|
|
} catch {|err|
|
||
|
|
print $"❌ Deployment failed: ($err.msg)"
|
||
|
|
|
||
|
|
# Attempt rollback
|
||
|
|
print "🔄 Attempting rollback..."
|
||
|
|
|
||
|
|
try {
|
||
|
|
rollback-deployment $config
|
||
|
|
print "✅ Rollback completed successfully"
|
||
|
|
} catch {|rb_err|
|
||
|
|
print $"❌ Rollback failed: ($rb_err.msg)"
|
||
|
|
}
|
||
|
|
|
||
|
|
let error_result = {
|
||
|
|
success: false
|
||
|
|
mode: "unattended"
|
||
|
|
error: $err.msg
|
||
|
|
message: "Deployment failed and rolled back"
|
||
|
|
timestamp: (date now)
|
||
|
|
}
|
||
|
|
|
||
|
|
if $webhook_url != "" {
|
||
|
|
notify-webhook $webhook_url ($error_result | merge {event: "deployment_failed"})
|
||
|
|
}
|
||
|
|
|
||
|
|
error make {
|
||
|
|
msg: "Unattended deployment failed"
|
||
|
|
label: {text: $err.msg}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Show deployment modes and usage examples
|
||
|
|
export def "deploy help" [] {
|
||
|
|
print "
|
||
|
|
🚀 Provisioning Platform Deployment Modes
|
||
|
|
|
||
|
|
INTERACTIVE MODE (Recommended for first-time users)
|
||
|
|
nu deploy.nu deploy-interactive
|
||
|
|
|
||
|
|
- TUI-based configuration
|
||
|
|
- Step-by-step guidance
|
||
|
|
- Visual platform detection
|
||
|
|
- Service selection UI
|
||
|
|
|
||
|
|
HEADLESS MODE (For scripted deployments)
|
||
|
|
nu deploy.nu deploy-headless docker solo
|
||
|
|
nu deploy.nu deploy-headless kubernetes enterprise --domain prod.example.com
|
||
|
|
nu deploy.nu deploy-headless podman cicd --services orchestrator,gitea,postgres
|
||
|
|
|
||
|
|
Platforms: docker, podman, kubernetes, orbstack
|
||
|
|
Modes: solo, multi-user, cicd, enterprise
|
||
|
|
|
||
|
|
Options:
|
||
|
|
--domain <hostname> Domain/hostname (default: localhost)
|
||
|
|
--services <list> Comma-separated service list
|
||
|
|
--auto-confirm Skip confirmation prompts
|
||
|
|
--config-only Generate config only, don't deploy
|
||
|
|
|
||
|
|
UNATTENDED MODE (For CI/CD and automation)
|
||
|
|
nu deploy.nu deploy-unattended file ./deploy-config.toml
|
||
|
|
nu deploy.nu deploy-unattended mcp http://mcp-server:8084/config
|
||
|
|
nu deploy.nu deploy-unattended api http://api.example.com/deployment/config
|
||
|
|
|
||
|
|
Sources: file, mcp, api
|
||
|
|
|
||
|
|
Options:
|
||
|
|
--webhook-url <url> Send progress notifications to webhook
|
||
|
|
--strict Enable strict validation (default: true)
|
||
|
|
|
||
|
|
EXAMPLES:
|
||
|
|
|
||
|
|
# Solo development deployment
|
||
|
|
nu deploy.nu deploy-headless docker solo --auto-confirm
|
||
|
|
|
||
|
|
# Enterprise deployment with custom domain
|
||
|
|
nu deploy.nu deploy-headless kubernetes enterprise --domain prod.acme.com
|
||
|
|
|
||
|
|
# Unattended CI/CD deployment
|
||
|
|
nu deploy.nu deploy-unattended file ./configs/cicd-deploy.toml --webhook-url http://hooks.slack.com/...
|
||
|
|
|
||
|
|
# Config generation only
|
||
|
|
nu deploy.nu deploy-headless docker solo --config-only
|
||
|
|
|
||
|
|
For more information, see README.md
|
||
|
|
"
|
||
|
|
}
|