Jesús Pérez d9ef2f0d5b
Some checks failed
Build and Test / Validate Setup (push) Has been cancelled
Build and Test / Build (darwin-amd64) (push) Has been cancelled
Build and Test / Build (darwin-arm64) (push) Has been cancelled
Build and Test / Build (linux-amd64) (push) Has been cancelled
Build and Test / Build (windows-amd64) (push) Has been cancelled
Build and Test / Build (linux-arm64) (push) Has been cancelled
Build and Test / Security Audit (push) Has been cancelled
Build and Test / Package Results (push) Has been cancelled
Build and Test / Quality Gate (push) Has been cancelled
Nightly Build / Check for Changes (push) Has been cancelled
Nightly Build / Validate Setup (push) Has been cancelled
Nightly Build / Nightly Build (darwin-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (darwin-arm64) (push) Has been cancelled
Nightly Build / Nightly Build (linux-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (windows-amd64) (push) Has been cancelled
Nightly Build / Nightly Build (linux-arm64) (push) Has been cancelled
Nightly Build / Create Nightly Pre-release (push) Has been cancelled
Nightly Build / Notify Build Status (push) Has been cancelled
Nightly Build / Nightly Maintenance (push) Has been cancelled
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:22:42 +00:00

15 KiB

nu_plugin_nickel

Nushell plugin for seamless Nickel configuration language integration. Load, evaluate, format, and validate Nickel files directly from Nushell scripts.

Quick Start

# Build and register the plugin
just build-plugin nu_plugin_nickel
just register-plugin nu_plugin_nickel

# Verify installation
plugin list | where name == "nickel-export"

Overview

5 commands for working with Nickel files:

Command Purpose
nickel-export Export Nickel to JSON/YAML (with smart type conversion)
nickel-eval Evaluate with automatic caching (for config loading)
nickel-format Format Nickel files (in-place)
nickel-validate Validate all Nickel files in a directory
nickel-cache-status Show cache statistics

Core Concept: Smart Type Conversion

The plugin converts output intelligently based on whether you specify a format:

nickel-export config.ncl           →  Nushell object (parsed JSON)
nickel-export config.ncl -f json   →  Raw JSON string
nickel-export config.ncl -f yaml   →  Raw YAML string

Why? Default behavior gives you structured data for programming. Explicit -f gives you raw output for external tools.


Complete Usage Guide

1. Load Configuration as Object (Most Common)

Without -f flag → Returns Nushell object

# Load configuration into a variable
let config = nickel-export workspace/config.ncl

# Access nested values with cell paths
$config.database.host          # "localhost"
$config.database.port          # 5432
$config.database.username      # "admin"

# Work with arrays
$config.servers | length       # 3
$config.servers | map {|s| $s.name}

# Filter and transform
$config.services
  | where enabled == true
  | each {|svc| {name: $svc.name, port: $svc.port}}

2. Get Raw Output (For External Tools)

With -f flag → Returns raw string

# Export as raw JSON string
let json = nickel-export config.ncl -f json
$json | save output.json

# Export as raw YAML string
nickel-export config.ncl -f yaml | save config.yaml

# Pipe to external tools
nickel-export config.ncl -f json | jq '.database'
nickel-export config.ncl -f json | curl -X POST -d @- http://api.example.com

3. Primary Config Loader with Caching

nickel-eval is optimized for configuration loading

# Load with automatic caching (80-90% hit rate)
let cfg = nickel-eval workspace/provisioning.ncl

# Works just like nickel-export (same smart conversion)
$cfg.infrastructure.cloud_provider    # "aws"
$cfg.infrastructure.region            # "us-east-1"

# Caching is transparent
nickel-eval config.ncl                # First call: 100-200ms
nickel-eval config.ncl                # Subsequent calls: 1-5ms

4. Format Nickel Files

# Format a single file (modifies in place)
nickel-format config.ncl

# Format multiple files
glob "**/*.ncl" | each {|f| nickel-format $f}

5. Validate Nickel Project

# Validate all .ncl files in directory
nickel-validate ./workspace/config

# Validate current directory
nickel-validate

# Output:
# ✅ workspace/config/main.ncl
# ✅ workspace/config/lib.ncl
# ✅ workspace/config/vars.ncl

6. Check Cache Status

nickel-cache-status
# Output:
# ╭──────────────────────────────────────────────────╮
# │ cache_dir: ~/.cache/provisioning/config-cache/   │
# │ entries: 42                                      │
# │ enabled: true                                    │
# ╰──────────────────────────────────────────────────╯

Real-World Examples

Example 1: Multi-Environment Configuration

# Load environment-specific config
let env = "production"
let config = nickel-eval "config/provisioning-($env).ncl"

# Use in deployment
def deploy [service: string] {
    let svc_config = $config.services | where name == $service | first

    print $"Deploying ($service) to ($svc_config.region)"
    print $"  Image: ($svc_config.docker.image)"
    print $"  Replicas: ($svc_config.replicas)"
    print $"  Port: ($svc_config.port)"
}

deploy "api-server"

Example 2: Generate Kubernetes Manifests

# Load infrastructure config
let infra = nickel-eval "infrastructure.ncl"

# Generate K8s manifests
$infra.services | each {|svc|
    {
        apiVersion: "apps/v1"
        kind: "Deployment"
        metadata: {name: $svc.name}
        spec: {
            replicas: $svc.replicas
            template: {
                spec: {
                    containers: [{
                        name: $svc.name
                        image: $svc.docker.image
                        ports: [{containerPort: $svc.port}]
                    }]
                }
            }
        }
    }
}
| each {|manifest|
    $manifest | to json | save "k8s/($manifest.metadata.name).yaml"
}

Example 3: Configuration Validation Script

def validate-config [config-path: path] {
    # Validate syntax
    nickel-validate $config-path | print

    # Load and check required fields
    let config = nickel-eval $config-path

    let required = ["database", "services", "infrastructure"]
    $required | each {|field|
        if ($config | has $field) {
            print $"✅ ($field): present"
        } else {
            print $"❌ ($field): MISSING"
        }
    }

    # Check configuration consistency
    let db_replicas = $config.database.replicas
    let svc_replicas = ($config.services | map {|s| $s.replicas} | math sum)

    if $db_replicas >= $svc_replicas {
        print "✅ Database replicas sufficient"
    } else {
        print "❌ WARNING: Services exceed database capacity"
    }
}

validate-config "workspace/config.ncl"

Example 4: Generate Configuration from Template

# Load base config template
let template = nickel-eval "templates/base.ncl"

# Customize for specific environment
let prod_config = {
    environment: "production"
    debug: false
    replicas: ($template.replicas * 3)
    services: ($template.services | map {|s|
        $s | merge {
            replicas: 5
            resources: {
                memory: "2Gi"
                cpu: "1000m"
            }
        }
    })
}

# Export as JSON
$prod_config | to json | save "production-config.json"

Example 5: Merge Multiple Configurations

# Load base config
let base = nickel-eval "config/base.ncl"

# Load environment-specific overrides
let env_overrides = nickel-eval "config/($env).ncl"

# Load local customizations
let local = nickel-eval "config/local.ncl"

# Merge with precedence: local > env > base
let final_config = $base
    | merge $env_overrides
    | merge $local

print $"Final configuration:"
print $final_config

Command Reference

nickel-export

nickel-export <FILE> [-f FORMAT] [-o OUTPUT]

Arguments:

  • FILE - Path to Nickel file (required)

Flags:

  • -f, --format - Output format: json (default), yaml (optional)
  • -o, --output - Save to file instead of returning value (optional)

Return Type:

  • Without -f: Nushell object (Record/List)
  • With -f json: Raw JSON string
  • With -f yaml: Raw YAML string

Examples:

nickel-export config.ncl                    # → object
nickel-export config.ncl -f json            # → JSON string
nickel-export config.ncl -f yaml -o out.yaml

nickel-eval

nickel-eval <FILE> [-f FORMAT] [--cache]

Arguments:

  • FILE - Path to Nickel file (required)

Flags:

  • -f, --format - Output format: json (default), yaml (optional)
  • --cache - Use caching (enabled by default, flag for future use)

Return Type:

  • Without -f: Nushell object (with caching)
  • With -f json: Raw JSON string (with caching)
  • With -f yaml: Raw YAML string (with caching)

Examples:

nickel-eval workspace/config.ncl            # → cached object
nickel-eval config.ncl -f json              # → cached JSON string
let cfg = nickel-eval config.ncl
$cfg.database.host

nickel-format

nickel-format <FILE>

Arguments:

  • FILE - Path to Nickel file to format (required)

Examples:

nickel-format config.ncl
glob "**/*.ncl" | each {|f| nickel-format $f}

nickel-validate

nickel-validate [DIR]

Arguments:

  • DIR - Directory to validate (optional, defaults to current directory)

Examples:

nickel-validate ./workspace/config
nickel-validate

nickel-cache-status

nickel-cache-status

Returns record with cache information:

  • cache_dir - Cache directory path
  • entries - Number of cached entries
  • enabled - Whether caching is enabled

Examples:

nickel-cache-status
let cache = nickel-cache-status
print $"Cache has ($cache.entries) entries at ($cache.cache_dir)"

Type Conversion Details

Without -f Flag (Object Mode)

The plugin converts Nickel output to Nushell types:

JSON Input           →  Nushell Type
─────────────────────────────────────
{"key": "value"}     →  {key: "value"}
[1, 2, 3]           →  [1, 2, 3]
"string"            →  "string"
123                 →  123
true                →  true
null                →  null

Enables full Nushell data access:

let config = nickel-export config.ncl

# Cell path access
$config.database.host

# Filtering
$config.services | where enabled == true

# Transformation
$config.services | map {|s| {name: $s.name, port: $s.port}}

# Custom functions
def get-service [name: string] {
    $config.services | where name == $name | first
}

With -f Flag (Raw Mode)

Returns unprocessed string:

nickel-export config.ncl -f json
# Returns: "{\"database\":{\"host\":\"localhost\", ...}}"

nickel-export config.ncl -f yaml
# Returns: "database:\n  host: localhost\n  ..."

Use for:

  • Saving to files with specific format
  • Piping to external JSON/YAML tools
  • API calls requiring raw format
  • Integration with non-Nushell tools

Caching

Automatic Caching with nickel-eval

Results are cached using content-addressed storage:

Cache location: ~/.cache/provisioning/config-cache/
Cache key: SHA256(file_content + format)
First call: ~100-200ms
Cached calls: ~1-5ms

Characteristics:

  • Non-blocking (errors are silently ignored)
  • Transparent (no configuration needed)
  • Hit rate: ~80-90% in typical workflows
  • Per-format caching (json and yaml cached separately)

Manual cache inspection:

let status = nickel-cache-status
print $"Cache entries: ($status.entries)"
print $"Cache location: ($status.cache_dir)"

# List cache files
ls ($status.cache_dir)

Troubleshooting

Plugin Not Found

Error: Unknown command 'nickel-export'

Solution:

# Register the plugin
just register-plugin nu_plugin_nickel

# Verify registration
plugin list | grep nickel

Nickel Binary Not Found

Error: Nickel execution failed: No such file or directory

Solution: Ensure nickel CLI is installed and in PATH:

# Check if nickel is available
which nickel

# Install nickel (macOS)
brew install nickel-lang/nickel/nickel

# Or build from source
cargo install nickel

File Not Found

Error: Nickel file not found: config.ncl

Solution: Use absolute path or verify file exists:

# Use absolute path
nickel-export /absolute/path/to/config.ncl

# Verify file exists
ls config.ncl

Cache Issues

# Clear cache if needed
rm -rf ~/.cache/provisioning/config-cache/*

# Check cache status
nickel-cache-status

JSON Parsing Error

If -f json returns parsing error, the Nickel file may not export valid JSON:

# Test with raw Nickel output
nickel-export config.ncl -f json | print

Architecture

Design Pattern: CLI Wrapper

The plugin uses an elegant CLI wrapper pattern:

Nushell Script
    ↓
nickel-export/eval command
    ↓
Command::new("nickel")
    ↓
Nickel official CLI
    ↓
Module resolution (guaranteed correct)
    ↓
JSON/YAML output
    ↓
Smart type conversion
    ↓
Nushell object or raw string

Benefits:

  • Module resolution guaranteed correct (official CLI)
  • Works with all Nickel versions automatically
  • All Nickel CLI features automatically supported
  • Zero maintenance burden

Trade-offs:

  • ⚠️ Requires nickel binary in PATH
  • ⚠️ ~100-200ms per evaluation (mitigated by caching)

Type Conversion Flow

nickel export /file.ncl --format json
    
Captures stdout (JSON string)
    
serde_json::from_str (parse)
    
json_value_to_nu_value (convert recursively)
    ├── Object  Record
    ├── Array  List
    ├── String  String
    ├── Number  Int or Float
    ├── Boolean  Bool
    └── Null  Nothing
    
Returns nu_protocol::Value
    
Nushell receives properly typed data

Integration Examples

With Provisioning System

# Load provisioning config
let prov = nickel-eval "workspace/provisioning.ncl"

# Deploy infrastructure
def deploy [] {
    for region in $prov.regions {
        print $"Deploying to ($region.name)..."
        # Your deployment logic
    }
}

# Validate configuration before deploy
def validate [] {
    nickel-validate "workspace/provisioning.ncl"
}

In Nushell Configuration

# env.nu or config.nu

# Load environment from Nickel
let env-config = nickel-eval "~/.config/nushell/environment.ncl"

# Set environment variables
$env.MY_VAR = $env-config.my_var
$env.DATABASE_URL = $env-config.database.url

In CI/CD Pipelines

# GitHub Actions / GitLab CI script

# Load config
let config = nickel-eval ".provisioning/ci-config.ncl"

# Check if tests should run
if $config.run_tests {
    print "Running tests..."
}

# Set deployment target
export DEPLOY_TARGET = $config.deployment.target

Performance Tips

  1. Use nickel-eval for repeated access

    # ❌ Bad: 3 separate evaluations
    print ($config.database | nickel-eval)
    print ($config.services | nickel-eval)
    
    # ✅ Good: Single evaluation, cached
    let cfg = nickel-eval config.ncl
    print $cfg.database
    print $cfg.services
    
  2. Avoid format conversion loops

    # ❌ Bad: Converts each time
    (1..100) | each {|i| nickel-export config.ncl | ...}
    
    # ✅ Good: Convert once
    let cfg = nickel-eval config.ncl
    (1..100) | each {|i| ... $cfg ...}
    
  3. Use raw output for large datasets

    # ❌ Bad: Large object in memory
    let big = nickel-export huge-config.ncl
    
    # ✅ Good: Stream raw JSON
    nickel-export huge-config.ncl -f json | jq '.items[]'
    

Requirements

  • Nushell: 0.110.0 or later
  • Nickel CLI: Latest version (install via brew or cargo)
  • Rust: For building the plugin (if not using pre-built binary)

Building from Source

cd nu_plugin_nickel
cargo build --release

Binary will be at: target/release/nu_plugin_nickel


Testing

# Run unit tests
cargo test

# Verify compilation
cargo check

# Run clippy linting
cargo clippy -- -D warnings

License

MIT


Further Reading