syntaxis/docs/installation/wrapper-design.md

418 lines
11 KiB
Markdown
Raw Normal View History

# Wrapper Script Design
Technical documentation for syntaxis wrapper scripts.
## Overview
Wrapper scripts are NuShell scripts created during installation that provide intelligent configuration discovery and environment setup for workspace binaries.
### Goals
1. **Automatic config discovery** - Find configuration files without user intervention
2. **Environment setup** - Set required environment variables before execution
3. **Transparency** - User sees no difference between wrapper and binary
4. **Flexibility** - Support multiple config file locations
5. **Debugging** - Easy to inspect what's happening
## Architecture
### Binary Layout After Installation
```
~/.cargo/bin/
├── workspace # Wrapper script (NuShell)
├── workspace.real # Actual binary (compiled executable)
├── syntaxis-tui # Wrapper script (NuShell)
├── syntaxis-tui.real # Actual binary (compiled executable)
└── syntaxis-dashboard # Actual binary (no wrapper, runs standalone)
```
### Wrapper Script Flow
```
User executes: workspace --help
NuShell wrapper script (workspace)
1. Search for config file in priority order
2. If found: Set WORKSPACE_CONFIG_PATH
3. Set other environment variables:
- WORKSPACE_CONFIG_DIR
- WORKSPACE_DATA_DIR
4. Create data directory if needed
5. Execute actual binary (workspace.real)
Binary executes with environment setup
```
## Configuration Discovery
### Search Order
Wrapper scripts search for configuration files in this priority order:
```
1. ~/.config/core/workspace.toml
└─ User configuration
2. ~/.config/core/syntaxis-api.toml
└─ Fallback to main API config
3. .project/workspace.toml
└─ Project-specific configuration
4. .vapora/workspace.toml
└─ VAPORA-specific configuration
```
Returns first matching file, or `null` if none found.
### Configuration Discovery Logic
```nushell
let config_paths = [
$"($env.HOME)/.config/core/workspace.toml"
$"($env.HOME)/.config/core/syntaxis-api.toml"
".project/workspace.toml"
".vapora/workspace.toml"
]
let config_file = (
$config_paths
| where { |p| $p | path exists }
| first
)
```
This ensures:
- User configs are tried first
- Fallback to shared config if specific config not found
- Project configs override user configs
- VAPORA configs are respected
- If no config found, `config_file` is `null`
## Environment Variables
### Set by Wrapper
Wrapper scripts set these environment variables before execution:
| Variable | Purpose | Example |
|----------|---------|---------|
| `WORKSPACE_CONFIG_PATH` | Absolute path to found config file | `~/.config/core/workspace.toml` |
| `WORKSPACE_CONFIG_DIR` | Configuration directory | `~/.config/syntaxis` |
| `WORKSPACE_DATA_DIR` | Runtime data directory | `~/.local/share/syntaxis` |
### Read by Application
The workspace binary reads these variables:
```rust
// In Rust code
let config_path = std::env::var("WORKSPACE_CONFIG_PATH").ok();
let config_dir = std::env::var("WORKSPACE_CONFIG_DIR").ok();
let data_dir = std::env::var("WORKSPACE_DATA_DIR").ok();
```
The binary will:
1. Use `WORKSPACE_CONFIG_PATH` if set (from wrapper)
2. Search in `WORKSPACE_CONFIG_DIR` if set
3. Fall back to default locations if neither set
4. Create `WORKSPACE_DATA_DIR` if it doesn't exist
## Wrapper Script Implementation
### workspace wrapper script location
`~/.cargo/bin/workspace` (or wherever cargo/bin is)
### Full Wrapper Script Example
```nushell
#!/usr/bin/env nu
# Generated wrapper for workspace
# Automatically finds configuration and sets up environment
# Find configuration file in standard locations
let config_paths = [
$"($env.HOME)/.config/core/workspace.toml"
$"($env.HOME)/.config/core/syntaxis-api.toml"
".project/workspace.toml"
".vapora/workspace.toml"
]
let config_file = (
$config_paths
| where { |p| $p | path exists }
| first
)
# Setup environment variables
if ($config_file != null) {
$env.WORKSPACE_CONFIG_PATH = $config_file
}
$env.WORKSPACE_CONFIG_DIR = $"($env.HOME)/.config/syntaxis"
$env.WORKSPACE_DATA_DIR = $"($env.HOME)/.local/share/syntaxis"
# Create data directory if needed
^mkdir -p $env.WORKSPACE_DATA_DIR
# Call actual binary with all arguments
^~/.cargo/bin/workspace.real
```
### Key Design Decisions
1. **NuShell as wrapper language**
- Reason: Already used in project build system
- Advantage: Native path handling, string interpolation
- Consistency: All scripts use same language
2. **Search before setting variables**
- Reason: Only set `WORKSPACE_CONFIG_PATH` if config actually exists
- Advantage: Clear separation of concerns
- Benefit: Application can detect which config was used
3. **Always set `WORKSPACE_CONFIG_DIR` and `WORKSPACE_DATA_DIR`**
- Reason: Application needs these for fallback behavior
- Advantage: Consistent environment across all invocations
- Benefit: Predictable runtime behavior
4. **Create data directory automatically**
- Reason: Application might not have write permission to create
- Advantage: Fails fast if permission issues
- Benefit: Cleaner error messages
5. **Pass all arguments through**
- Reason: Transparent to user
- Advantage: No argument parsing needed in wrapper
- Method: Shell directly executes real binary (arguments auto-passed)
## Binary Renaming Strategy
### Why Rename?
Renaming actual binaries to `.real` suffix:
1. **Clarity** - Easy to see which files are wrappers vs. actual binaries
2. **Safety** - Prevents accidental execution of wrong file
3. **Maintenance** - Can easily identify wrapped binaries
4. **Reversibility** - Can restore by removing wrappers
### Renaming Process
```nushell
def create_wrappers [] {
let cargo_bin = ($env.HOME + "/.cargo/bin")
let binaries = ["workspace" "syntaxis-tui"]
for binary in $binaries {
let binary_path = ($cargo_bin + "/" + $binary)
let real_binary_path = ($cargo_bin + "/" + $binary + ".real")
if ($binary_path | path exists) {
^mv $binary_path $real_binary_path
# Now create wrapper script at original location
}
}
}
```
Results:
```
Before: ~/.cargo/bin/workspace (executable binary)
After: ~/.cargo/bin/workspace.real (actual binary)
~/.cargo/bin/workspace (wrapper script)
```
## Installation Integration
### Creation During Installation
The `install_binary()` function:
1. Compiles binary with `cargo install`
2. Creates wrapper script via `create_wrapper_script()`
3. Renames binary to `.real` via `create_wrappers()`
4. Registers in manifest via `register_installations()`
### Manifest Tracking
Wrapper creation is tracked in `.syntaxis/manifest.toml`:
```toml
[binaries.workspace]
installed = true
wrapper = true # ← Indicates wrapper created
path = "~/.cargo/bin/workspace" # ← Wrapper location
real_binary = "~/.cargo/bin/workspace.real" # ← Real binary location
```
## Debugging Wrappers
### Inspect Wrapper Script
```bash
cat ~/.cargo/bin/workspace
```
Shows exact config discovery logic and environment setup.
### Trace Execution
Add debug output to see what's happening:
```nushell
# Modify wrapper to add debugging
let config_file = (
$config_paths
| where { |p| $p | path exists }
| first
)
print -e $"Found config: ($config_file)" # ← Add this line
print -e $"Config dir: ($env.WORKSPACE_CONFIG_DIR)"
print -e $"Data dir: ($env.WORKSPACE_DATA_DIR)"
^~/.cargo/bin/workspace.real
```
### Check Environment
```bash
# See what environment wrapper sets
strace -e execve ~/.cargo/bin/workspace --help 2>&1 | grep execve
# Or use nu debugging
nu -c 'source ~/.cargo/bin/workspace; print $env'
```
### Test Config Discovery
```bash
# Create test wrapper
cat > /tmp/test_wrapper.nu << 'EOF'
#!/usr/bin/env nu
let config_paths = [
$"($env.HOME)/.config/core/workspace.toml"
$"($env.HOME)/.config/core/syntaxis-api.toml"
".project/workspace.toml"
".vapora/workspace.toml"
]
let config_file = (
$config_paths
| where { |p| $p | path exists }
| first
)
print $"Found config: ($config_file)"
EOF
# Run test
nu /tmp/test_wrapper.nu
```
## Performance Considerations
### Startup Overhead
Wrapper adds minimal overhead:
1. NuShell startup: ~10-50ms
2. Config search: ~0-5ms (depends on number of existing config files)
3. Directory creation: ~1-2ms
4. Total: ~15-60ms (negligible for CLI tools)
### Optimization Opportunities
If performance becomes critical:
1. **Cache config file location**
```nushell
# Store found path in environment for child processes
$env.WORKSPACE_CONFIG_FOUND = $config_file
```
2. **Short-circuit search**
```nushell
# Stop searching after first find
if ($"($env.HOME)/.config/core/workspace.toml" | path exists) {
# Use this, don't check others
}
```
3. **Skip directory creation**
```nushell
# Only create if needed (check first)
if not ($env.WORKSPACE_DATA_DIR | path exists) {
^mkdir -p $env.WORKSPACE_DATA_DIR
}
```
## Troubleshooting Guide
### Wrapper Not Found
**Problem**: `command not found: workspace`
**Solutions**:
1. Check PATH: `echo $PATH | grep .cargo/bin`
2. Verify installation: `ls -la ~/.cargo/bin/workspace*`
3. Check permissions: `ls -l ~/.cargo/bin/workspace`
4. Re-install: `just scripts-install workspace`
### Config Not Loading
**Problem**: Binary doesn't see configuration
**Solutions**:
1. Verify config exists: `ls ~/.config/core/workspace.toml`
2. Check wrapper sees it: `cat ~/.cargo/bin/workspace | grep config_paths`
3. Add debugging to wrapper (see Debugging section)
4. Check environment: `env | grep WORKSPACE`
### Permission Denied
**Problem**: `permission denied: ~/.cargo/bin/workspace`
**Solutions**:
1. Check permissions: `ls -l ~/.cargo/bin/workspace`
2. Make executable: `chmod +x ~/.cargo/bin/workspace`
3. Re-install: `just scripts-install workspace`
### Wrong Binary Executed
**Problem**: `.real` binary being called instead of wrapper
**Solutions**:
1. Check file order: `ls -la ~/.cargo/bin/workspace*`
2. Verify PATH order: `which workspace`
3. Clear PATH cache: `hash -r` (bash) or `rehash` (zsh)
4. Use full path: `~/.cargo/bin/workspace --help`
## Future Improvements
### Version 1.1
1. **Config file caching** - Cache found path to reduce startup time
2. **Wrapper updates** - Auto-update wrappers when configs change
3. **Enhanced debugging** - Built-in debug mode via `--debug` flag
4. **Config merging** - Merge multiple config files for inheritance
### Version 2.0
1. **Binary shims** - Replace NuShell wrappers with compiled shims (faster)
2. **Config templating** - Jinja2-style variable substitution in configs
3. **Hot reload** - Reload configs without restarting application
4. **Remote configs** - Load configs from remote servers
## Related Documentation
- **docs/installation-guide.md** - How to install wrappers
- **docs/configuration.md** - Configuration file options
- **scripts/install-cli.nu** - Wrapper creation implementation