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
- 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
734 lines
15 KiB
Markdown
734 lines
15 KiB
Markdown
# nu_plugin_nickel
|
|
|
|
Nushell plugin for seamless Nickel configuration language integration. Load, evaluate, format, and validate Nickel files directly from Nushell scripts.
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# 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**
|
|
|
|
```nu
|
|
# 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**
|
|
|
|
```nu
|
|
# 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**
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
nickel-cache-status
|
|
# Output:
|
|
# ╭──────────────────────────────────────────────────╮
|
|
# │ cache_dir: ~/.cache/provisioning/config-cache/ │
|
|
# │ entries: 42 │
|
|
# │ enabled: true │
|
|
# ╰──────────────────────────────────────────────────╯
|
|
```
|
|
|
|
---
|
|
|
|
## Real-World Examples
|
|
|
|
### Example 1: Multi-Environment Configuration
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
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
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
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:**
|
|
```nu
|
|
nickel-export config.ncl # → object
|
|
nickel-export config.ncl -f json # → JSON string
|
|
nickel-export config.ncl -f yaml -o out.yaml
|
|
```
|
|
|
|
### nickel-eval
|
|
|
|
```nu
|
|
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:**
|
|
```nu
|
|
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
|
|
|
|
```nu
|
|
nickel-format <FILE>
|
|
```
|
|
|
|
**Arguments:**
|
|
- `FILE` - Path to Nickel file to format (required)
|
|
|
|
**Examples:**
|
|
```nu
|
|
nickel-format config.ncl
|
|
glob "**/*.ncl" | each {|f| nickel-format $f}
|
|
```
|
|
|
|
### nickel-validate
|
|
|
|
```nu
|
|
nickel-validate [DIR]
|
|
```
|
|
|
|
**Arguments:**
|
|
- `DIR` - Directory to validate (optional, defaults to current directory)
|
|
|
|
**Examples:**
|
|
```nu
|
|
nickel-validate ./workspace/config
|
|
nickel-validate
|
|
```
|
|
|
|
### nickel-cache-status
|
|
|
|
```nu
|
|
nickel-cache-status
|
|
```
|
|
|
|
Returns record with cache information:
|
|
- `cache_dir` - Cache directory path
|
|
- `entries` - Number of cached entries
|
|
- `enabled` - Whether caching is enabled
|
|
|
|
**Examples:**
|
|
```nu
|
|
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:
|
|
|
|
```nu
|
|
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:
|
|
|
|
```nu
|
|
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:**
|
|
|
|
```nu
|
|
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:**
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```nu
|
|
# 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:
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```rust
|
|
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
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
# 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
|
|
|
|
```nu
|
|
# 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**
|
|
```nu
|
|
# ❌ 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**
|
|
```nu
|
|
# ❌ 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**
|
|
```nu
|
|
# ❌ 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
|
|
|
|
```bash
|
|
cd nu_plugin_nickel
|
|
cargo build --release
|
|
```
|
|
|
|
Binary will be at: `target/release/nu_plugin_nickel`
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
# Run unit tests
|
|
cargo test
|
|
|
|
# Verify compilation
|
|
cargo check
|
|
|
|
# Run clippy linting
|
|
cargo clippy -- -D warnings
|
|
```
|
|
|
|
---
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
---
|
|
|
|
## Further Reading
|
|
|
|
- [Nickel Official Documentation](https://nickel-lang.org/)
|
|
- [Nushell Plugin Development](https://www.nushell.sh/book/plugins.html)
|
|
- [Architecture Decision Record](./adr-001-nickel-cli-wrapper-architecture.md)
|