#!/usr/bin/env nu # Configuration Deployment Orchestrator # Orchestrates Nickel configuration + SOPS secrets workflow # Handles: config generation → secrets decryption → deployment # Usage: config-deploy [options] use std log def get-vault-url [] { $env.VAULT_SERVICE_URL? // "http://localhost:9094" } def get-vault-token [] { $env.VAULT_SERVICE_TOKEN? // "" } def check-nickel [] { let result = (^nickel --version | complete) if $result.exit_code != 0 { error make { msg: "Nickel not found or not in PATH" label: { text: "Install Nickel or add to PATH: https://github.com/tweag/nickel" } } } true } def check-sops [] { let result = (^sops --version | complete) if $result.exit_code != 0 { error make { msg: "SOPS not found or not in PATH" label: { text: "Install SOPS: https://github.com/mozilla/sops" } } } true } def validate-environment [environment: string] { if $environment not-in ["dev", "staging", "prod"] { error make { msg: "Invalid environment" label: { text: "Must be: dev, staging, or prod" } } } true } def decrypt-secrets [environment: string] { let secrets_file = $"provisioning/config/secrets/secrets.($environment).yaml.enc" if not ($secrets_file | path exists) { print $"⚠️ Secrets file not found: ($secrets_file)" return {} } print $"Decrypting secrets for ($environment)..." let token = (get-vault-token) if ($token | is-empty) { error make { msg: "VAULT_SERVICE_TOKEN required for secret decryption" label: { text: "Set environment variable: export VAULT_SERVICE_TOKEN=..." } } } let output_file = $"provisioning/config/secrets/secrets.($environment).yaml" let result = ( nu provisioning/scripts/secrets-decrypt.nu $secrets_file --environment $environment --output $output_file ) if not ($result.success) { error make { msg: "Failed to decrypt secrets" label: { text: $result } } } print "✓ Secrets decrypted successfully" $output_file } def generate-nickel-config [config_path: string, environment: string, secrets_path: string] { print "Generating configuration from Nickel..." let result = ( ^nickel eval -f json provisioning/schemas/examples/deployment-with-secrets.ncl | complete ) if $result.exit_code != 0 { error make { msg: "Nickel evaluation failed" label: { text: $result.stderr } } } print "✓ Configuration generated" $result.stdout | from json } def validate-config [config: record] { print "Validating configuration..." let required_keys = ["database", "redis", "api"] let missing = ( $required_keys | where {|key| not ($key in ($config | keys))} ) if ($missing | length) > 0 { let missing_str = ($missing | str join ", ") error make { msg: "Configuration validation failed" label: { text: $"Missing required sections: ($missing_str)" } } } print "✓ Configuration validated" true } def create-deployment-package [config: record, environment: string, output_dir: string] { print $"Creating deployment package for ($environment)..." if not ($output_dir | path exists) { ^mkdir -p $output_dir } # Write configuration as JSON let config_file = $"($output_dir)/config.json" $config | to json | save -f $config_file print $"✓ Config: ($config_file)" # Write configuration as YAML (for readability) let yaml_file = $"($output_dir)/config.yaml" # Note: Would use actual JSON-to-YAML converter in production $config | to json | save -f $yaml_file print $"✓ Config: ($yaml_file)" # Create deployment manifest let manifest = { environment: $environment, timestamp: (date now | format date "%Y-%m-%dT%H:%M:%SZ"), config_version: "1.0", components: [ { name: "database", host: ($config.database.host // "unknown") }, { name: "redis", host: ($config.redis.host // "unknown") }, { name: "api", port: ($config.api.port // 0) }, ], } let manifest_file = $"($output_dir)/manifest.json" $manifest | to json | save -f $manifest_file print $"✓ Manifest: ($manifest_file)" $output_dir } def main [ action: string = "help" --environment: string = "dev" --config-path: string = "provisioning/schemas/examples/deployment-with-secrets.ncl" --output-dir: string = "deploy/output" --clean ] { match $action { "validate-tools" => { print "Checking required tools..." check-nickel print "✓ Nickel: installed" check-sops print "✓ SOPS: installed" print "" print "All tools present" }, "generate" => { validate-environment $environment print "" print $"====== Generate Configuration for ($environment) ======" print "" print "Step 1: Checking tools..." check-nickel check-sops print "✓ Tools validated" print "" print "Step 2: Decrypting secrets..." let secrets_file = (decrypt-secrets $environment) print "" print "Step 3: Generating configuration..." let config = (generate-nickel-config $config_path $environment $secrets_file) print "" print "Step 4: Validating configuration..." validate-config $config print "" print "Step 5: Creating deployment package..." let output = (create-deployment-package $config $environment $output_dir) print "" print $"====== Configuration Ready for Deployment ======" print $"Output directory: ($output)" print "" if $clean { print "Cleaning up decrypted secrets..." rm -f $secrets_file print "✓ Secrets cleaned up" } }, "validate-nickel" => { print "Validating Nickel configuration..." let result = (^nickel check $config_path | complete) if $result.exit_code == 0 { print "✓ Nickel configuration valid" } else { error make { msg: "Nickel validation failed" label: { text: $result.stderr } } } }, "list-environments" => { print "Available environments:" ["dev", "staging", "prod"] | each {|env_name| let secrets_file = $"provisioning/config/secrets/secrets.($env_name).yaml.enc" let exists = if ($secrets_file | path exists) { "✓" } else { "✗" } print $" ($exists) ($env_name) - ($secrets_file)" } }, "status" => { print "" print "====== Deployment Configuration Status ======" print "" print "Tools:" (check-nickel) | if $in { print " ✓ Nickel" } else { print " ✗ Nickel" } (check-sops) | if $in { print " ✓ SOPS" } else { print " ✗ SOPS" } print "" print "Vault Service:" print $" URL: (get-vault-url)" print $" Token: $(if (get-vault-token | is-empty) { 'not set' } else { 'set' })" print "" print "Configuration:" print $" Nickel config: ($config_path)" print $" Output dir: ($output_dir)" print "" print "Secrets:" (main "list-environments") print "" }, "help" => { print "Configuration Deployment Orchestrator" print "" print "Combines Nickel infrastructure-as-code with SOPS secrets" print "" print "Usage: config-deploy [--environment dev|staging|prod] [--config-path ] [--output-dir ]" print "" print "Actions:" print " validate-tools - Check Nickel and SOPS installation" print " validate-nickel - Validate Nickel configuration syntax" print " list-environments - List available secret environments" print " status - Show deployment configuration status" print " generate - Full workflow: decrypt → generate → validate → package" print " help - Show this help message" print "" print "Examples:" print " config-deploy validate-tools" print " config-deploy status" print " config-deploy generate --environment prod --clean" print " config-deploy generate --environment dev --output-dir ./deploy/dev" print "" print "Environment Variables:" print " VAULT_SERVICE_URL - Vault endpoint (default: http://localhost:9094)" print " VAULT_SERVICE_TOKEN - Vault authentication token (required for decrypt)" print " PROVISIONING_ENV - Environment (dev, staging, prod)" print "" }, _ => { print $"Unknown action: ($action)" print "Run 'config-deploy help' for available commands" } } }