Jesús Pérez 8d6598211b chore: update all plugins to Nushell 0.111.0
- Bump all 18 plugins from 0.110.0 to 0.111.0
  - Update rust-toolchain.toml channel to 1.93.1 (nu 0.111.0 requires ≥1.91.1)

  Fixes:
  - interprocess pin =2.2.x → ^2.3.1 in nu_plugin_mcp, nu_plugin_nats, nu_plugin_typedialog
    (required by nu-plugin-core 0.111.0)
  - nu_plugin_typedialog: BackendType::Web initializer — add open_browser: false field
  - nu_plugin_auth: implement missing user_info_to_value helper referenced in tests

  Scripts:
  - update_all_plugins.nu: fix [package].version update on minor bumps; add [dev-dependencies]
    pass; add nu-plugin-test-support to managed crates
  - download_nushell.nu: rustup override unset before rm -rf on nushell dir replace;
    fix unclosed ) in string interpolation
2026-03-11 03:21:44 +00:00
2025-06-26 23:21:16 +01:00
2025-06-26 23:20:26 +01:00
2025-06-26 23:20:26 +01:00

nu_plugin_tera

A powerful Nushell plugin for rendering Tera templates with structured data from Nushell pipelines.

Overview

This plugin integrates Mozilla's Tera templating engine with Nushell, enabling you to generate dynamic content from structured data. Perfect for configuration files, documentation generation, reports, and any scenario where you need to combine templates with data.

Installing

Clone this repository

Warning

nu_plugin_tera has dependencies to nushell source via local path in Cargo.toml Nushell and plugins require to be sync with same version

Clone Nushell alongside this plugin or change dependencies in Cargo.toml

This plugin is also included as submodule in nushell-plugins as part of plugins collection for Provisioning project

Build from source

> cd nu_plugin_tera
> cargo install --path .

Nushell

In a Nushell

> plugin add ~/.cargo/bin/nu_plugin_tera

Commands

tera-render

Render Tera templates with structured data from Nushell pipelines or arguments.

> tera-render <template> [context]

Parameters:

  • template <path>: Path to the .tera template file or directory (with --directory)
  • context <any>: Context data (record or JSON file path) (optional)

Flags:

  • --directory: Load all templates from a directory (enables {% include %}, {% extends %}, macros)
  • --entry-point <string>: Template to render in directory mode (default: main.j2, fallback: index.j2)
  • -h, --help: Display the help message for this command

Template Syntax

Tera uses a Jinja2-inspired syntax with powerful features:

Basic Variables

Hello, {{ name }}!
Your age is {{ age }}.

Control Structures

{% if user.is_admin %}
  <p>Welcome, admin!</p>
{% else %}
  <p>Welcome, user!</p>
{% endif %}

{% for item in items %}
  - {{ item.name }}: {{ item.value }}
{% endfor %}

Filters

{{ name | upper }}
{{ price | round(precision=2) }}
{{ date | date(format="%Y-%m-%d") }}

Macros

{% macro render_field(name, value) %}
  <div class="field">
    <label>{{ name }}</label>
    <span>{{ value }}</span>
  </div>
{% endmacro %}

{{ render_field(name="Username", value=user.name) }}

Usage Examples

Basic Usage

data.json

{
  "name": "Akasha",
  "projects": [
    {
      "name": "TheProject",
      "status": "active"
    },
    {
      "name": "AnotherProject",
      "status": "completed"
    }
  ]
}

template.tera

Hello, {{ name }}!

Projects:
{% for project in projects -%}
- {{ project.name }} ({{ project.status }})
{% endfor %}

Rendering:

> open data.json | tera-render template.tera
Hello, Akasha!

Projects:
- TheProject (active)
- AnotherProject (completed)

Pipeline Data Processing

# Process system information
> sys | tera-render system-report.tera

# Generate configuration files from structured data
> {
    server: {
      host: "localhost",
      port: 8080,
      ssl: true
    },
    database: {
      host: "db.example.com",
      name: "myapp"
    }
  } | tera-render config.tera

# Process CSV data
> open employees.csv
  | tera-render employee-report.tera

Multiple Data Sources

# Using JSON file as context
> tera-render template.tera data.json

# Using record from pipeline
> { name: 'Akasha', projects: [ {'name': 'TheProject', 'status': 'active'} ] }
  | tera-render template.tera

# Wrapping data for template access
> open data.json | wrap value | tera-render template.tera

Template Composition with Directory Mode

The --directory flag enables true template composition using Tera's include and extends features. Load all templates from a directory and use advanced template patterns.

Directory Structure

templates/
├── base.j2              # Base layout template
├── includes/
│   ├── _header.j2       # Reusable header component
│   ├── _footer.j2       # Reusable footer component
│   └── _helpers.j2      # Shared macros and filters
└── main.j2              # Main template using includes

Usage with Directory Mode

# Single render with ALL templates loaded (enables includes, extends, macros)
> {
    title: "My Report",
    sections: [
      { name: "Features", items: ["fast", "reliable"] }
    ]
  } | tera-render templates --directory

# Renders main.j2 with all includes and macros loaded

Infrastructure-as-Code Example

templates/hetzner/ Directory structure:

templates/hetzner/
├── base.j2                     # Shared macros
├── includes/
│   ├── _ssh_keys.j2           # SSH key setup
│   ├── _networks.j2           # Network creation
│   └── _firewalls.j2          # Firewall rules
└── server.j2                   # Main orchestration

templates/hetzner/base.j2

{% macro setup_ssh_key(name, public_key) %}
hcloud ssh-key create --name "{{ name }}" --public-key "{{ public_key }}"
{% endmacro %}

{% macro create_network(name, ip_range) %}
hcloud network create --name "{{ name }}" --ip-range "{{ ip_range }}"
{% endmacro %}

templates/hetzner/server.j2

#!/bin/bash
set -euo pipefail

{% include "includes/_ssh_keys.j2" %}
{% include "includes/_networks.j2" %}
{% include "includes/_firewalls.j2" %}

hcloud server create ...

Usage:

> { ssh_key: {...}, network: {...}, server: {...} }
  | tera-render templates/hetzner --directory
  | save server-setup.sh

Result: Single atomic script with all sections composed from DRY templates via includes and shared macros.

Custom Entry Points

The --entry-point parameter allows you to select different templates within the same directory. This enables multiple configurations or deployment scenarios from a single template library.

Use Cases

templates/deployment/
├── base.j2          # Shared macros for all configurations
├── dev.j2           # Development environment config
├── staging.j2       # Staging environment config
├── prod.j2          # Production environment config
├── includes/
│   ├── _setup.j2    # Common setup steps
│   └── _cleanup.j2  # Common cleanup steps
└── main.j2          # Default (used when no entry-point specified)

Examples

Render default template (main.j2):

> { env: "production" } | tera-render templates/deployment --directory
# Uses: main.j2

Render development configuration:

> { env: "development", debug: true } | tera-render templates/deployment --directory --entry-point dev.j2
# Uses: dev.j2 (with development-specific settings)

Render production configuration:

> { env: "production", replicas: 5 } | tera-render templates/deployment --directory --entry-point prod.j2
# Uses: prod.j2 (with production-specific settings)

Template Structure Example

templates/deployment/base.j2

{% macro deploy_service(name, replicas) %}
  echo "Deploying {{ name }} with {{ replicas }} replicas"
{% endmacro %}

{% macro setup_monitoring() %}
  # Common monitoring setup
{% endmacro %}

templates/deployment/dev.j2

{% import "base.j2" as base %}
{% include "includes/_setup.j2" %}

{{ base::deploy_service(name=service, replicas=1) }}
{{ base::setup_monitoring() }}

# Development-specific settings
DEBUG=true
LOG_LEVEL=debug

templates/deployment/prod.j2

{% import "base.j2" as base %}
{% include "includes/_setup.j2" %}

{{ base::deploy_service(name=service, replicas=replicas) }}
{{ base::setup_monitoring() }}

# Production-specific settings
DEBUG=false
LOG_LEVEL=warn
REPLICAS={{ replicas }}

Benefits

Feature Default Custom Entry Point
Reuse macros/includes
Multiple configurations
Dev/staging/prod variants
Single template directory
DRY principle

Advanced Templates

Configuration File Generation

nginx.conf.tera

server {
    listen {{ server.port }}{% if server.ssl %} ssl{% endif %};
    server_name {{ server.host }};

    {% if server.ssl %}
    ssl_certificate {{ ssl.cert_path }};
    ssl_certificate_key {{ ssl.key_path }};
    {% endif %}

    {% for location in locations %}
    location {{ location.path }} {
        proxy_pass {{ location.upstream }};
        {% for header in location.headers | default(value=[]) %}
        proxy_set_header {{ header.name }} {{ header.value }};
        {% endfor %}
    }
    {% endfor %}
}

Usage:

> {
    server: {
      host: "example.com",
      port: 443,
      ssl: true
    },
    ssl: {
      cert_path: "/etc/ssl/cert.pem",
      key_path: "/etc/ssl/key.pem"
    },
    locations: [
      {
        path: "/api/",
        upstream: "http://backend:8080",
        headers: [
          {name: "Host", value: "$host"},
          {name: "X-Real-IP", value: "$remote_addr"}
        ]
      }
    ]
  } | tera-render nginx.conf.tera | save nginx.conf

Documentation Generation

api-docs.md.tera

# {{ api.title }} API Documentation

Version: {{ api.version }}
Last updated: {{ "now" | date(format="%Y-%m-%d") }}

## Endpoints

{% for endpoint in api.endpoints %}
### {{ endpoint.method | upper }} {{ endpoint.path }}

{{ endpoint.description }}

{% if endpoint.parameters %}
**Parameters:**
{% for param in endpoint.parameters %}
- `{{ param.name }}` ({{ param.type }}){% if param.required %} *required*{% endif %}: {{ param.description }}
{% endfor %}
{% endif %}

**Example Response:**

{{ endpoint.example_response | tojson }}


---
{% endfor %}

Report Generation

# Generate system report
def generate-system-report [] {
    let data = {
        timestamp: (date now | format date "%Y-%m-%d %H:%M:%S"),
        system: (sys),
        processes: (ps | first 10),
        disk_usage: (df)
    }

    $data | tera-render system-report.html.tera | save $"reports/system-report-(date now | format date "%Y%m%d").html"
}

# Generate project status report
def generate-project-report [project_dir: string] {
    let data = {
        project: (open $"($project_dir)/package.json" | get name),
        files: (ls $project_dir | length),
        git_status: (git -C $project_dir status --porcelain | lines),
        last_commit: (git -C $project_dir log -1 --format="%h %s %an %ad" --date=short)
    }

    $data | tera-render project-report.md.tera
}

Workflow Examples

CI/CD Pipeline Configuration

# Generate Kubernetes manifests
def generate-k8s-manifests [env: string] {
    let config = (open $"configs/($env).json")

    [
        "deployment.yaml.tera",
        "service.yaml.tera",
        "ingress.yaml.tera"
    ] | each { |template|
        let output = ($config | tera-render $"templates/($template)")
        $output | save $"manifests/($env)/(($template | str replace '.tera' '')))"
    }
}

# Generate environment-specific configs
def generate-env-configs [] {
    ["dev", "staging", "prod"] | each { |env|
        let base_config = (open base-config.json)
        let env_config = (open $"($env)-config.json")
        let merged = ($base_config | merge $env_config)

        $merged | tera-render app-config.yaml.tera | save $"configs/($env).yaml"
    }
}

Content Management

# Generate blog posts from structured data
def generate-blog-posts [] {
    ls content/*.json
    | each { |post_file|
        let post_data = (open $post_file.name)
        let filename = ($post_file.name | path basename | str replace '.json' '.html')

        $post_data | tera-render blog-post.html.tera | save $"output/($filename)"
    }
}

# Generate navigation menus
def generate-navigation [site_config: string] {
    open $site_config
    | get pages
    | tera-render navigation.html.tera
    | save includes/navigation.html
}

Infrastructure as Code

# Generate Terraform configurations
def generate-terraform [environment: string] {
    let vars = (open $"terraform/($environment).tfvars.json")

    ls terraform/templates/*.tf.tera
    | each { |template|
        let output_name = ($template.name | path basename | str replace '.tera' '')
        $vars | tera-render $template.name | save $"terraform/($environment)/($output_name)"
    }
}

# Generate Docker Compose files
def generate-docker-compose [services: list] {
    let compose_data = {
        services: $services,
        networks: ["app-network"],
        volumes: ["db-data", "app-logs"]
    }

    $compose_data | tera-render docker-compose.yml.tera | save docker-compose.yml
}

Integration with Nushell Data Processing

Leverage Nushell's powerful data manipulation with Tera templating:

# Process and template CSV data
> open sales.csv
  | group-by region
  | transpose region sales
  | insert total { |row| $row.sales | math sum }
  | tera-render sales-report.html.tera
  | save reports/sales-summary.html

# Combine multiple data sources
> let weather = (http get "https://api.weather.com/current")
> let system = (sys)
> {weather: $weather, system: $system}
  | tera-render dashboard.html.tera

# Batch process templates
> ls templates/*.tera
  | each { |template|
      let data = (open $"data/(($template.name | path basename | str replace '.tera' '.json'))")
      $data | tera-render $template.name
    }

Error Handling

The plugin provides detailed error messages for common issues:

# Template syntax errors
> {} | tera-render broken-template.tera
Error: Template syntax error
  ╭─[calling tera-render]
  │ Template syntax error: Unexpected token...

# Missing variables
> {} | tera-render template-with-vars.tera
Error: Template render error
  ╭─[calling tera-render]
  │ Variable 'name' not found in context

# File not found
> {} | tera-render nonexistent.tera
Error: Failed to read template file
  ╭─[calling tera-render]
  │ Failed to read file 'nonexistent.tera': No such file...

Features

  • Pipeline Integration - Seamlessly process Nushell data structures
  • Multiple Data Sources - Support for JSON files, records, and pipeline data
  • Rich Template Syntax - Full Tera templating features (filters, macros, inheritance)
  • Error Handling - Detailed error messages with context
  • File I/O - Read templates from files, save rendered output
  • Data Wrapping - Automatic handling of different data structures
  • Performance - Efficient template compilation and rendering

Use Cases

  • Configuration Management: Generate environment-specific config files
  • Documentation: Create dynamic documentation from structured data
  • Infrastructure as Code: Template Terraform, Kubernetes, Docker configurations
  • Reports: Generate HTML/Markdown reports from system data
  • Web Development: Template generation for static sites
  • CI/CD: Dynamic pipeline configurations
  • Content Management: Blog posts, newsletters, and content generation
  • Monitoring: Dashboard and alert template generation

Template Best Practices

1. Organize Templates

templates/
├── base.html.tera          # Base layouts
├── components/
│   ├── header.html.tera    # Reusable components
│   └── footer.html.tera
├── pages/
│   ├── home.html.tera      # Page templates
│   └── about.html.tera
└── configs/
    ├── nginx.conf.tera     # Configuration templates
    └── docker.yml.tera

2. Use Template Inheritance

base.html.tera

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{{ site.title }}{% endblock %}</title>
</head>
<body>
    {% block content %}{% endblock %}
</body>
</html>

page.html.tera

{% extends "base.html.tera" %}

{% block title %}{{ page.title }} - {{ site.title }}{% endblock %}

{% block content %}
    <h1>{{ page.title }}</h1>
    {{ page.content }}
{% endblock %}

3. Create Reusable Functions

# Template rendering helper
def render-template [template: string, data: any] {
    $data | tera-render $template
}

# Batch template processing
def render-all-templates [data_dir: string, template_dir: string, output_dir: string] {
    ls $"($data_dir)/*.json"
    | each { |data_file|
        let basename = ($data_file.name | path basename | str replace '.json' '')
        let template = $"($template_dir)/($basename).tera"
        let output = $"($output_dir)/($basename).html"

        if ($template | path exists) {
            open $data_file.name | tera-render $template | save $output
            print $"Generated: ($output)"
        }
    }
}

Contributing

Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.

License

This project is licensed under the MIT License.

Description
A Nushell plugin to use Tera templates
Readme MIT 167 KiB
Languages
Nushell 58.2%
Rust 41.8%