221 lines
5.9 KiB
Text
Executable file
221 lines
5.9 KiB
Text
Executable file
#!/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
|
|
}
|
|
}
|