2 lines
9.9 KiB
Markdown
2 lines
9.9 KiB
Markdown
# Templates\n\nJinja2 and Nickel templates for configuration and deployment generation.\n\n## Purpose\n\nTemplates provide:\n- **Nickel output generation** - Jinja2 templates for TypeDialog nickel-roundtrip\n- **Docker Compose generation** - Infrastructure-as-code for containerized deployment\n- **Kubernetes manifests** - Declarative deployment manifests\n- **TOML export** - Service configuration generation for Rust codebase\n\n## File Organization\n\n```\ntemplates/\n├── README.md # This file\n├── orchestrator-config.ncl.j2 # Nickel output template (Jinja2)\n├── control-center-config.ncl.j2 # Nickel output template (Jinja2)\n├── mcp-server-config.ncl.j2 # Nickel output template (Jinja2)\n├── installer-config.ncl.j2 # Nickel output template (Jinja2)\n├── docker-compose/ # Docker Compose templates\n│ ├── platform-stack.solo.yml.ncl\n│ ├── platform-stack.multiuser.yml.ncl\n│ ├── platform-stack.cicd.yml.ncl\n│ └── platform-stack.enterprise.yml.ncl\n├── kubernetes/ # Kubernetes templates\n│ ├── orchestrator-deployment.yaml.ncl\n│ ├── orchestrator-service.yaml.ncl\n│ ├── control-center-deployment.yaml.ncl\n│ ├── control-center-service.yaml.ncl\n│ └── platform-ingress.yaml.ncl\n└── configs/ # Service config templates (optional)\n ├── orchestrator-config.toml.ncl\n ├── control-center-config.toml.ncl\n └── mcp-server-config.toml.ncl\n```\n\n## Jinja2 Config Templates\n\n**Critical for TypeDialog nickel-roundtrip workflow**:\n\n```\ntypedialog-web nickel-roundtrip "$CONFIG" "forms/{service}-form.toml" --output "$CONFIG" --template "templates/{service}-config.ncl.j2"\n```\n\n### Template Pattern: orchestrator-config.ncl.j2\n\n```\n# Orchestrator Configuration - Nickel Format\n# Auto-generated by provisioning TypeDialog\n# Edit via: nu scripts/configure.nu orchestrator {mode}\n\n{\n orchestrator = {\n # Workspace Configuration\n workspace = {\n {%- if workspace_name %}\n name = "{{ workspace_name }}",\n {%- endif %}\n {%- if workspace_path %}\n path = "{{ workspace_path }}",\n {%- endif %}\n {%- if workspace_enabled is defined %}\n enabled = {{ workspace_enabled | lower }},\n {%- endif %}\n {%- if multi_workspace is defined %}\n multi_workspace = {{ multi_workspace | lower }},\n {%- endif %}\n },\n\n # Server Configuration\n server = {\n {%- if server_host %}\n host = "{{ server_host }}",\n {%- endif %}\n {%- if server_port %}\n port = {{ server_port }},\n {%- endif %}\n {%- if server_workers %}\n workers = {{ server_workers }},\n {%- endif %}\n {%- if server_keep_alive %}\n keep_alive = {{ server_keep_alive }},\n {%- endif %}\n },\n\n # Storage Configuration\n storage = {\n {%- if storage_backend %}\n backend = '{{ storage_backend }},\n {%- endif %}\n {%- if storage_path %}\n path = "{{ storage_path }}",\n {%- endif %}\n {%- if surrealdb_url %}\n surrealdb_url = "{{ surrealdb_url }}",\n {%- endif %}\n },\n\n # Queue Configuration\n queue = {\n {%- if max_concurrent_tasks %}\n max_concurrent_tasks = {{ max_concurrent_tasks }},\n {%- endif %}\n {%- if retry_attempts %}\n retry_attempts = {{ retry_attempts }},\n {%- endif %}\n {%- if retry_delay %}\n retry_delay = {{ retry_delay }},\n {%- endif %}\n {%- if task_timeout %}\n task_timeout = {{ task_timeout }},\n {%- endif %}\n },\n\n # Monitoring Configuration (optional)\n {%- if enable_monitoring is defined and enable_monitoring %}\n monitoring = {\n enabled = true,\n {%- if metrics_interval %}\n metrics_interval = {{ metrics_interval }},\n {%- endif %}\n {%- if health_check_interval %}\n health_check_interval = {{ health_check_interval }},\n {%- endif %}\n },\n {%- endif %}\n },\n}\n```\n\n### Key Jinja2 Patterns\n\n**Conditional blocks** (only include if field is set):\n\n```\n{%- if workspace_name %}\nname = "{{ workspace_name }}",\n{%- endif %}\n```\n\n**String values** (with quotes):\n\n```\n{%- if storage_backend %}\nbackend = '{{ storage_backend }}, # Enum (atom syntax)\n{%- endif %}\n```\n\n**Numeric values** (no quotes):\n\n```\n{%- if server_port %}\nport = {{ server_port }}, # Number\n{%- endif %}\n```\n\n**Boolean values** (lower case):\n\n```\n{%- if workspace_enabled is defined %}\nenabled = {{ workspace_enabled | lower }}, # Boolean (true/false)\n{%- endif %}\n```\n\n**Comments** (for generated files):\n\n```\n# Auto-generated by provisioning TypeDialog\n# Edit via: nu scripts/configure.nu orchestrator {mode}\n```\n\n## Docker Compose Templates\n\nNickel templates that import from `values/*.ncl`:\n\n```\n# templates/docker-compose/platform-stack.solo.yml.ncl\n# Docker Compose Platform Stack - Solo Mode\n# Imports config from values/orchestrator.solo.ncl\n\nlet orchestrator_config = import "../../values/orchestrator.solo.ncl" in\nlet control_center_config = import "../../values/control-center.solo.ncl" in\n\n{\n version = "3.8",\n services = {\n orchestrator = {\n image = "provisioning-orchestrator:latest",\n container_name = "orchestrator",\n ports = [\n "%{std.to_string orchestrator_config.orchestrator.server.port}:9090",\n ],\n environment = {\n ORCHESTRATOR_SERVER_HOST = orchestrator_config.orchestrator.server.host,\n ORCHESTRATOR_SERVER_PORT = std.to_string orchestrator_config.orchestrator.server.port,\n ORCHESTRATOR_STORAGE_BACKEND = orchestrator_config.orchestrator.storage.backend,\n },\n volumes = [\n "./data/orchestrator:%{orchestrator_config.orchestrator.storage.path}",\n ],\n restart = "unless-stopped",\n },\n control-center = {\n image = "provisioning-control-center:latest",\n container_name = "control-center",\n ports = [\n "%{std.to_string control_center_config.control_center.server.port}:8080",\n ],\n environment = {\n CONTROL_CENTER_SERVER_HOST = control_center_config.control_center.server.host,\n CONTROL_CENTER_SERVER_PORT = std.to_string control_center_config.control_center.server.port,\n },\n restart = "unless-stopped",\n },\n },\n}\n```\n\n### Rendering Docker Compose\n\n```\n# Export Nickel template to YAML\nnickel export --format json templates/docker-compose/platform-stack.solo.yml.ncl | yq -P > docker-compose.solo.yml\n```\n\n## Kubernetes Templates\n\nNickel templates for Kubernetes manifests:\n\n```\n# templates/kubernetes/orchestrator-deployment.yaml.ncl\nlet config = import "../../values/orchestrator.solo.ncl" in\n\n{\n apiVersion = "apps/v1",\n kind = "Deployment",\n metadata = {\n name = "orchestrator",\n labels = {\n app = "orchestrator",\n },\n },\n spec = {\n replicas = 1,\n selector = {\n matchLabels = {\n app = "orchestrator",\n },\n },\n template = {\n metadata = {\n labels = {\n app = "orchestrator",\n },\n },\n spec = {\n containers = [\n {\n name = "orchestrator",\n image = "provisioning-orchestrator:latest",\n ports = [\n {\n containerPort = 9090,\n },\n ],\n env = [\n {\n name = "ORCHESTRATOR_SERVER_PORT",\n value = std.to_string config.orchestrator.server.port,\n },\n {\n name = "ORCHESTRATOR_STORAGE_BACKEND",\n value = config.orchestrator.storage.backend,\n },\n ],\n volumeMounts = [\n {\n name = "data",\n mountPath = config.orchestrator.storage.path,\n },\n ],\n },\n ],\n volumes = [\n {\n name = "data",\n persistentVolumeClaim = {\n claimName = "orchestrator-pvc",\n },\n },\n ],\n },\n },\n },\n}\n```\n\n## Rendering Templates\n\n### Render to JSON\n\n```\nnickel export --format json templates/orchestrator-config.ncl.j2 > config.json\n```\n\n### Render to YAML (via yq)\n\n```\nnickel export --format json templates/kubernetes/orchestrator-deployment.yaml.ncl | yq -P > deployment.yaml\n```\n\n### Render to TOML\n\n```\nnickel export --format toml templates/configs/orchestrator-config.toml.ncl > config.toml\n```\n\n## Template Variables\n\nVariables in templates come from:\n1. **Form values** (TypeDialog input)\n2. **Imported configs** (Nickel imports)\n3. **Constraint interpolation** (constraints.toml)\n\n## Best Practices\n\n1. **Use conditional blocks** - Only include fields if set\n2. **Import configs** - Reuse Nickel configs in templates\n3. **Type conversion** - Use `std.to_string` for numeric values\n4. **Comments** - Explain generated/auto-edited markers\n5. **Validation** - Use `nickel typecheck` to verify templates\n6. **Environment variables** - Prefer env over hardcoding\n\n## Template Testing\n\n```\n# Typecheck Jinja2 + Nickel template\nnickel typecheck templates/orchestrator-config.ncl.j2\n\n# Evaluate and view output\nnickel eval templates/orchestrator-config.ncl.j2\n\n# Export and validate output\nnickel export --format json templates/orchestrator-config.ncl.j2 | jq '.'\n```\n\n## Adding a New Template\n\n1. **Create template file** (`{service}-config.ncl.j2` or `{name}.yml.ncl`)\n2. **Define structure** (Nickel or Jinja2)\n3. **Import configs** (if Nickel)\n4. **Use variables** (from forms or imports)\n5. **Typecheck**: `nickel typecheck templates/{file}`\n6. **Test rendering**: `nickel export {format} templates/{file}`\n\n---\n\n**Version**: 1.0.0\n**Last Updated**: 2025-01-05
|