provisioning/scripts/docker-generate-builds.nu

222 lines
5.9 KiB
Text
Raw Permalink Normal View History

#!/usr/bin/env nu
# Docker Dockerfile Generator
# Generates Dockerfiles from Nickel templates + build configuration
#
# Usage:
# docker-generate-builds.nu extension-registry --mode solo
# docker-generate-builds.nu orchestrator --mode cicd
#
# Environment:
# PROVISIONING_ROOT - Optional: override project root detection
#
# Patterns:
# - Result pattern (NO try-catch)
# - Pipeline let binding (Nushell 0.110.0+)
# - Guards for validation
# Search up directory tree for provisioning root (helper)
def search-up-for-provisioning [dir: string]: nothing -> string {
if ($"($dir)/provisioning/schemas/platform" | path exists) {
$"($dir)/provisioning"
} else {
let parent = ($dir | path dirname)
if $parent == $dir {
""
} else {
search-up-for-provisioning $parent
}
}
}
# Detect provisioning project root
# Follows same pattern as platform-generate-manifests.nu
def get-provisioning-root []: nothing -> string {
# 1. Check PROVISIONING environment variable (standard)
if ($env.PROVISIONING? != null) {
return $env.PROVISIONING
}
# 2. Check PROVISIONING_ROOT (alternative)
if ($env.PROVISIONING_ROOT? != null) {
return $env.PROVISIONING_ROOT
}
# 3. Check if we're in provisioning/ directory
let cwd = (pwd)
if ($cwd | path basename) == "provisioning" {
# We're inside provisioning/, use current dir
if ("schemas/platform" | path exists) {
return "."
}
}
# 4. Check if provisioning/ exists as subdirectory
if ("provisioning/schemas/platform" | path exists) {
return "provisioning"
}
# 5. Search up the directory tree
let found = (search-up-for-provisioning $cwd)
if $found != "" {
return $found
}
# 6. Fallback to current directory (same as platform-generate-manifests.nu)
pwd
}
# Valid service names
const VALID_SERVICES = [
"orchestrator"
"control-center"
"extension-registry"
"mcp-server"
"provisioning-daemon"
"ai-service"
"rag"
"vault-service"
]
# Valid deployment modes
const VALID_MODES = ["solo", "cicd", "enterprise"]
# Service name to Nickel schema key mapping
const SERVICE_TO_SCHEMA_KEY = {
orchestrator: "orchestrator",
control-center: "control_center",
extension-registry: "extension_registry",
mcp-server: "mcp_server",
provisioning-daemon: "provisioning_daemon",
ai-service: "ai_service",
rag: "rag",
vault-service: "vault",
}
# Service name to package directory mapping
const SERVICE_TO_DIR = {
orchestrator: "orchestrator",
control-center: "control-center",
extension-registry: "extension-registry",
mcp-server: "mcp-server",
provisioning-daemon: "daemon",
ai-service: "ai-service",
rag: "rag",
vault-service: "vault-service",
}
# Generate Dockerfile for a service
def main [
service: string, # Service name (e.g., "orchestrator", "extension-registry")
--mode: string = "solo", # Deployment mode (solo, cicd, enterprise)
]: nothing -> record<ok: bool, err: string, path: string> {
# Guard: Validate service name
if not ($service in $VALID_SERVICES) {
return {
ok: false,
err: $"Invalid service: ($service). Valid: ($VALID_SERVICES | str join ', ')",
path: ""
}
}
# Guard: Validate mode
if not ($mode in $VALID_MODES) {
return {
ok: false,
err: $"Invalid mode: ($mode). Valid: ($VALID_MODES | str join ', ')",
path: ""
}
}
# Get schema key for this service
let schema_key = $SERVICE_TO_SCHEMA_KEY | get $service
# Get target directory for this service
let target_dir = $SERVICE_TO_DIR | get $service
# Detect provisioning root
let prov_root = (get-provisioning-root)
# Construct paths
let defaults_file = $"($prov_root)/schemas/platform/defaults/($service)-defaults.ncl"
let template_file = $"($prov_root)/schemas/platform/templates/docker/Dockerfile.chef.ncl"
let output_file = $"($prov_root)/platform/crates/($target_dir)/Dockerfile"
# Guard: Check defaults file exists
if not ($defaults_file | path exists) {
return {
ok: false,
err: $"Defaults file not found: ($defaults_file)",
path: ""
}
}
# Guard: Check template file exists
if not ($template_file | path exists) {
return {
ok: false,
err: $"Template file not found: ($template_file)",
path: ""
}
}
# Generate Nickel expression to export Dockerfile
let nickel_expr = $"
let template = import \"($template_file)\" in
let defaults = import \"($defaults_file)\" in
template defaults.($schema_key).build
"
# Execute nickel export with Result pattern
let result = (
echo $nickel_expr
| nickel export --format raw
| complete
)
if $result.exit_code != 0 {
return {
ok: false,
err: $"Nickel export failed: ($result.stderr)",
path: ""
}
}
let dockerfile = $result.stdout
# Check if generation succeeded
if ($dockerfile | is-empty) {
return {
ok: false,
err: "Generated Dockerfile is empty",
path: ""
}
}
# Ensure output directory exists
let output_dir = ($output_file | path dirname)
if not ($output_dir | path exists) {
mkdir $output_dir
}
# Write Dockerfile
$dockerfile | save --force $output_file
# Return success
{
ok: true,
err: "",
path: $output_file
}
}
# Helper to generate Dockerfiles for all services
export def "main all" [
--mode: string = "solo" # Deployment mode
]: nothing -> table<service: string, ok: bool, err: string, path: string> {
$VALID_SERVICES
| each {|service|
let result = (main $service --mode $mode)
$result | insert service $service
}
}