2026-01-14 04:59:49 +00:00

9.6 KiB

Templates

Jinja2 and Nickel templates for configuration and deployment generation.

Purpose

Templates provide:

  • Nickel output generation - Jinja2 templates for TypeDialog nickel-roundtrip
  • Docker Compose generation - Infrastructure-as-code for containerized deployment
  • Kubernetes manifests - Declarative deployment manifests
  • TOML export - Service configuration generation for Rust codebase

File Organization

templates/
├── README.md                           # This file
├── orchestrator-config.ncl.j2          # Nickel output template (Jinja2)
├── control-center-config.ncl.j2        # Nickel output template (Jinja2)
├── mcp-server-config.ncl.j2            # Nickel output template (Jinja2)
├── installer-config.ncl.j2             # Nickel output template (Jinja2)
├── docker-compose/                     # Docker Compose templates
│   ├── platform-stack.solo.yml.ncl
│   ├── platform-stack.multiuser.yml.ncl
│   ├── platform-stack.cicd.yml.ncl
│   └── platform-stack.enterprise.yml.ncl
├── kubernetes/                         # Kubernetes templates
│   ├── orchestrator-deployment.yaml.ncl
│   ├── orchestrator-service.yaml.ncl
│   ├── control-center-deployment.yaml.ncl
│   ├── control-center-service.yaml.ncl
│   └── platform-ingress.yaml.ncl
└── configs/                            # Service config templates (optional)
    ├── orchestrator-config.toml.ncl
    ├── control-center-config.toml.ncl
    └── mcp-server-config.toml.ncl

Jinja2 Config Templates

Critical for TypeDialog nickel-roundtrip workflow:

typedialog-web nickel-roundtrip "$CONFIG" "forms/{service}-form.toml" --output "$CONFIG" --template "templates/{service}-config.ncl.j2"

Template Pattern: orchestrator-config.ncl.j2

# Orchestrator Configuration - Nickel Format
# Auto-generated by provisioning TypeDialog
# Edit via: nu scripts/configure.nu orchestrator {mode}

{
  orchestrator = {
    # Workspace Configuration
    workspace = {
      {%- if workspace_name %}
      name = "{{ workspace_name }}",
      {%- endif %}
      {%- if workspace_path %}
      path = "{{ workspace_path }}",
      {%- endif %}
      {%- if workspace_enabled is defined %}
      enabled = {{ workspace_enabled | lower }},
      {%- endif %}
      {%- if multi_workspace is defined %}
      multi_workspace = {{ multi_workspace | lower }},
      {%- endif %}
    },

    # Server Configuration
    server = {
      {%- if server_host %}
      host = "{{ server_host }}",
      {%- endif %}
      {%- if server_port %}
      port = {{ server_port }},
      {%- endif %}
      {%- if server_workers %}
      workers = {{ server_workers }},
      {%- endif %}
      {%- if server_keep_alive %}
      keep_alive = {{ server_keep_alive }},
      {%- endif %}
    },

    # Storage Configuration
    storage = {
      {%- if storage_backend %}
      backend = '{{ storage_backend }},
      {%- endif %}
      {%- if storage_path %}
      path = "{{ storage_path }}",
      {%- endif %}
      {%- if surrealdb_url %}
      surrealdb_url = "{{ surrealdb_url }}",
      {%- endif %}
    },

    # Queue Configuration
    queue = {
      {%- if max_concurrent_tasks %}
      max_concurrent_tasks = {{ max_concurrent_tasks }},
      {%- endif %}
      {%- if retry_attempts %}
      retry_attempts = {{ retry_attempts }},
      {%- endif %}
      {%- if retry_delay %}
      retry_delay = {{ retry_delay }},
      {%- endif %}
      {%- if task_timeout %}
      task_timeout = {{ task_timeout }},
      {%- endif %}
    },

    # Monitoring Configuration (optional)
    {%- if enable_monitoring is defined and enable_monitoring %}
    monitoring = {
      enabled = true,
      {%- if metrics_interval %}
      metrics_interval = {{ metrics_interval }},
      {%- endif %}
      {%- if health_check_interval %}
      health_check_interval = {{ health_check_interval }},
      {%- endif %}
    },
    {%- endif %}
  },
}

Key Jinja2 Patterns

Conditional blocks (only include if field is set):

{%- if workspace_name %}
name = "{{ workspace_name }}",
{%- endif %}

String values (with quotes):

{%- if storage_backend %}
backend = '{{ storage_backend }},  # Enum (atom syntax)
{%- endif %}

Numeric values (no quotes):

{%- if server_port %}
port = {{ server_port }},  # Number
{%- endif %}

Boolean values (lower case):

{%- if workspace_enabled is defined %}
enabled = {{ workspace_enabled | lower }},  # Boolean (true/false)
{%- endif %}

Comments (for generated files):

# Auto-generated by provisioning TypeDialog
# Edit via: nu scripts/configure.nu orchestrator {mode}

Docker Compose Templates

Nickel templates that import from values/*.ncl:

# templates/docker-compose/platform-stack.solo.yml.ncl
# Docker Compose Platform Stack - Solo Mode
# Imports config from values/orchestrator.solo.ncl

let orchestrator_config = import "../../values/orchestrator.solo.ncl" in
let control_center_config = import "../../values/control-center.solo.ncl" in

{
  version = "3.8",
  services = {
    orchestrator = {
      image = "provisioning-orchestrator:latest",
      container_name = "orchestrator",
      ports = [
        "%{std.to_string orchestrator_config.orchestrator.server.port}:9090",
      ],
      environment = {
        ORCHESTRATOR_SERVER_HOST = orchestrator_config.orchestrator.server.host,
        ORCHESTRATOR_SERVER_PORT = std.to_string orchestrator_config.orchestrator.server.port,
        ORCHESTRATOR_STORAGE_BACKEND = orchestrator_config.orchestrator.storage.backend,
      },
      volumes = [
        "./data/orchestrator:%{orchestrator_config.orchestrator.storage.path}",
      ],
      restart = "unless-stopped",
    },
    control-center = {
      image = "provisioning-control-center:latest",
      container_name = "control-center",
      ports = [
        "%{std.to_string control_center_config.control_center.server.port}:8080",
      ],
      environment = {
        CONTROL_CENTER_SERVER_HOST = control_center_config.control_center.server.host,
        CONTROL_CENTER_SERVER_PORT = std.to_string control_center_config.control_center.server.port,
      },
      restart = "unless-stopped",
    },
  },
}

Rendering Docker Compose

# Export Nickel template to YAML
nickel export --format json templates/docker-compose/platform-stack.solo.yml.ncl | yq -P > docker-compose.solo.yml

Kubernetes Templates

Nickel templates for Kubernetes manifests:

# templates/kubernetes/orchestrator-deployment.yaml.ncl
let config = import "../../values/orchestrator.solo.ncl" in

{
  apiVersion = "apps/v1",
  kind = "Deployment",
  metadata = {
    name = "orchestrator",
    labels = {
      app = "orchestrator",
    },
  },
  spec = {
    replicas = 1,
    selector = {
      matchLabels = {
        app = "orchestrator",
      },
    },
    template = {
      metadata = {
        labels = {
          app = "orchestrator",
        },
      },
      spec = {
        containers = [
          {
            name = "orchestrator",
            image = "provisioning-orchestrator:latest",
            ports = [
              {
                containerPort = 9090,
              },
            ],
            env = [
              {
                name = "ORCHESTRATOR_SERVER_PORT",
                value = std.to_string config.orchestrator.server.port,
              },
              {
                name = "ORCHESTRATOR_STORAGE_BACKEND",
                value = config.orchestrator.storage.backend,
              },
            ],
            volumeMounts = [
              {
                name = "data",
                mountPath = config.orchestrator.storage.path,
              },
            ],
          },
        ],
        volumes = [
          {
            name = "data",
            persistentVolumeClaim = {
              claimName = "orchestrator-pvc",
            },
          },
        ],
      },
    },
  },
}

Rendering Templates

Render to JSON

nickel export --format json templates/orchestrator-config.ncl.j2 > config.json

Render to YAML (via yq)

nickel export --format json templates/kubernetes/orchestrator-deployment.yaml.ncl | yq -P > deployment.yaml

Render to TOML

nickel export --format toml templates/configs/orchestrator-config.toml.ncl > config.toml

Template Variables

Variables in templates come from:

  1. Form values (TypeDialog input)
  2. Imported configs (Nickel imports)
  3. Constraint interpolation (constraints.toml)

Best Practices

  1. Use conditional blocks - Only include fields if set
  2. Import configs - Reuse Nickel configs in templates
  3. Type conversion - Use std.to_string for numeric values
  4. Comments - Explain generated/auto-edited markers
  5. Validation - Use nickel typecheck to verify templates
  6. Environment variables - Prefer env over hardcoding

Template Testing

# Typecheck Jinja2 + Nickel template
nickel typecheck templates/orchestrator-config.ncl.j2

# Evaluate and view output
nickel eval templates/orchestrator-config.ncl.j2

# Export and validate output
nickel export --format json templates/orchestrator-config.ncl.j2 | jq '.'

Adding a New Template

  1. Create template file ({service}-config.ncl.j2 or {name}.yml.ncl)
  2. Define structure (Nickel or Jinja2)
  3. Import configs (if Nickel)
  4. Use variables (from forms or imports)
  5. Typecheck: nickel typecheck templates/{file}
  6. Test rendering: nickel export {format} templates/{file}

Version: 1.0.0 Last Updated: 2025-01-05