# Nushell Script Best Practices for Provisioning

Develop high-quality, maintainable Nushell scripts for infrastructure automation.

## Script Structure

### Modular Design

```nushell
# scripts/lib/common.nu
# Shared utilities for all scripts

export def get-config [--workspace: string = "default"] {
    let config_path = $"($env.HOME)/.provisioning/workspaces/($workspace)/config.yaml"
    open $config_path | from yaml
}

export def log [--level: string = "info", --message: string] {
    let timestamp = (date now | date format "%Y-%m-%d %H:%M:%S")
    print $"[$timestamp] [($level | str upcase)] ($message)"
}

export def error [--message: string] {
    log --level "error" --message $message
}

export def info [--message: string] {
    log --level "info" --message $message
}

# scripts/deployment.nu
# Main deployment script

use lib/common.nu [get-config, log, error]

def deploy [--workspace: string = "default", --environment: string = "staging"] {
    info --message $"Deploying ($workspace) to ($environment)"

    let config = (get-config --workspace $workspace)

    # Validate configuration
    if ($config.version | is-empty) {
        error --message "Config version missing"
        return 1
    }

    # Perform deployment
    info --message "Starting deployment..."
}
```

### Error Handling

```nushell
def robust-operation [] {
    let result = (
        try {
            provisioning deploy --config config.ncl
        } catch { | err |
            {
                status: "error",
                error: $err,
                message: "Deployment failed"
            }
        }
    )

    if $result.status == "error" {
        error --message $result.message
        error --message $"Details: ($result.error)"
        return 1
    }

    return 0
}

def validate-input [--value: any, --type: string] {
    if ($value | is-empty) {
        error --message "Input required"
        return 1
    }

    match $type {
        "string" => {
            if ($value | type) != "string" {
                error --message "Expected string"
                return 1
            }
        },
        "number" => {
            if ($value | type) != "number" {
                error --message "Expected number"
                return 1
            }
        }
    }

    return 0
}
```

## Performance Optimization

### Using the Provisioning Daemon

The `provisioning-daemon` service speeds up Nushell script execution by maintaining a long-running process:

```nushell
# Without daemon - slower, spawns new process each time
provisioning workspace list > 1.2s

# With daemon - faster, uses existing process
provisioning daemon workspace list > 50ms
```

### Configuration

```nushell
# Enable daemon for faster script execution
provisioning config set --key daemon.enabled --value true

# Set daemon port
provisioning config set --key daemon.port --value 9090

# Set daemon auto-restart
provisioning config set --key daemon.auto_restart --value true

# Start daemon
provisioning daemon start

# Check daemon status
provisioning daemon status
```

### Script with Daemon

```nushell
def efficient-workflow [] {
    # Connect to daemon for fast operations
    let daemon_client = (provisioning daemon connect)

    # Perform multiple operations quickly
    let workspaces = ($daemon_client | send "workspace list")
    let resources = ($daemon_client | send "resource list")
    let status = ($daemon_client | send "status")

    # All operations share single daemon process - very fast
    info --message "Operations completed in ${elapsed}ms"
}
```

## Data Processing Patterns

### Streaming Large Datasets

```nushell
def process-large-dataset [--source: string] {
    # Use streaming to avoid loading entire dataset in memory
    let result = (
        open $source
        | lines
        | chunk 1000
        | each { | batch |
            $batch
            | parse "{id},{name},{value}"
            | select id value
            | each { | row |
                {
                    id: $row.id,
                    processed_value: ($row.value | into int | math abs)
                }
            }
        }
    )

    $result
}
```

### Parallel Processing

```nushell
def parallel-process [--items: list, --workers: int = 4] {
    let chunk_size = (($items | length) / $workers | math ceil)

    $items
    | chunks $chunk_size
    | par-each -n $workers { | chunk |
        $chunk | each { | item |
            provisioning process --input $item
        }
    }
}
```

## Testing Nushell Scripts

```nushell
# tests/deployment_test.nu

use lib/test_utils.nu [assert_equal, assert_true, assert_false]

def test_deploy_validation [] {
    let result = (deploy --config "" --dry-run true)
    assert_equal $result.status "error"
}

def test_workspace_creation [] {
    let workspace = (provisioning workspace create --name "test-$nu.env.USER")
    assert_true ($workspace.id | is-not-empty)
}

def test_config_parsing [] {
    let config = (get-config --workspace "test")
    assert_true ($config.version | is-not-empty)
}

# Run tests
def run-tests [] {
    let tests = [
        "test_deploy_validation",
        "test_workspace_creation",
        "test_config_parsing"
    ]

    let results = ($tests | each { | test |
        try {
            ($test | do)
            { name: $test, status: "passed" }
        } catch { | err |
            { name: $test, status: "failed", error: $err }
        }
    })

    let passed = ($results | where status == "passed" | length)
    let failed = ($results | where status == "failed" | length)

    print $"Tests: ($passed) passed, ($failed) failed"

    if $failed > 0 {
        $results | where status == "failed"
    }
}
```

## CLI Best Practices

### Help Documentation

```nushell
def deploy [
    --workspace: string = "default"
    --environment: string = "staging"
    --dry-run: bool = false
] {
    # Deploy infrastructure to target environment
    #
    # Examples:
    #   deploy --workspace production --environment prod
    #   deploy --dry-run true  # See what would be deployed
    #
    # Requires:
    #   - $env.PROVISIONING_TOKEN set
    #   - Valid workspace configuration
}
```

### Argument Validation

```nushell
def deploy [--workspace: string] {
    # Validate workspace name format
    if ($workspace | str length) < 3 {
        error --message "Workspace name must be at least 3 characters"
        return 1
    }

    if not ($workspace | str match --regex "^[a-z0-9-]+$") {
        error --message "Workspace name must contain only lowercase letters, numbers, and hyphens"
        return 1
    }

    # Workspace validation passed
    info --message $"Deploying workspace: ($workspace)"
}
```

## State Management

### Idempotent Operations

```nushell
def ensure-workspace-exists [--name: string] {
    let existing = (
        provisioning workspace list
        | where name == $name
        | first
    )

    if ($existing | is-not-empty) {
        info --message $"Workspace ($name) already exists"
        return $existing.id
    }

    let created = (provisioning workspace create --name $name)
    info --message $"Created workspace: ($name)"
    return $created.id
}

def ensure-config-deployed [--config: string] {
    let config_hash = ($config | file-hash)
    let deployed_hash = (
        provisioning config status
        | get config_hash
    )

    if $config_hash == $deployed_hash {
        info --message "Configuration already deployed"
        return 0
    }

    provisioning config deploy --file $config
    info --message "Configuration deployed"
    return 0
}
```

## Debugging

### Verbose Logging

```nushell
export env PROVISIONING_LOG_LEVEL = "debug"

def deploy-with-debug [] {
    # Enable debug logging
    $env.PROVISIONING_LOG_LEVEL = "debug"

    # Script executes with detailed logging
    provisioning deploy --config config.ncl
}
```

### Tracing Execution

```nushell
def trace-execution [--command: string] {
    let start = (date now)

    print $"[TRACE] Starting: ($command)"
    let result = (eval $command)
    let duration = ((date now) - $start)

    print $"[TRACE] Completed in ($duration.ms)ms"
    print $"[TRACE] Result: ($result | to json)"

    return $result
}
```

## Documentation

### Inline Comments

```nushell
def deploy [--workspace: string] {
    # Get workspace configuration - validates workspace exists
    let config = (get-config --workspace $workspace)

    # Pre-deployment validation - ensures configuration is valid
    validate-config $config

    # Deploy to infrastructure - applies Nickel configuration
    provision infra apply --config ($config | to-json)

    # Post-deployment verification - confirms success
    verify-deployment $workspace
}
```

### Examples in Help

```nushell
def list-resources [] {
    # List infrastructure resources in workspace
    #
    # Examples:
    #   list-resources
    #   list-resources | where type == "compute"
    #   list-resources | get name | sort
}
```

## See Also

- [Batch Workflow System](../infrastructure/batch-workflows.md)
- [Task Services Guide](../infrastructure/task-services-guide.md)
