2026-01-14 04:53:21 +00:00
|
|
|
# ADR-006: Provisioning CLI Refactoring to Modular Architecture
|
|
|
|
|
|
|
|
|
|
**Status**: Implemented ✅
|
|
|
|
|
**Date**: 2025-09-30
|
|
|
|
|
**Authors**: Infrastructure Team
|
|
|
|
|
**Related**: ADR-001 (Project Structure), ADR-004 (Hybrid Architecture)
|
|
|
|
|
|
|
|
|
|
## Context
|
|
|
|
|
|
|
|
|
|
The main provisioning CLI script (`provisioning/core/nulib/provisioning`) had grown to
|
|
|
|
|
**1,329 lines** with a massive 1,100+ line match statement handling all commands. This
|
|
|
|
|
monolithic structure created multiple critical problems:
|
|
|
|
|
|
|
|
|
|
### Problems Identified
|
|
|
|
|
|
|
|
|
|
1. **Maintainability Crisis**
|
|
|
|
|
- 54 command branches in one file
|
|
|
|
|
- Code duplication: Flag handling repeated 50+ times
|
|
|
|
|
- Hard to navigate: Finding specific command logic required scrolling through 1,000+ lines
|
|
|
|
|
- Mixed concerns: Routing, validation, and execution all intertwined
|
|
|
|
|
|
|
|
|
|
2. **Development Friction**
|
|
|
|
|
- Adding new commands required editing massive file
|
|
|
|
|
- Testing was nearly impossible (monolithic, no isolation)
|
|
|
|
|
- High cognitive load for contributors
|
|
|
|
|
- Code review difficult due to file size
|
|
|
|
|
|
|
|
|
|
3. **Technical Debt**
|
|
|
|
|
- 10+ lines of repetitive flag handling per command
|
|
|
|
|
- No separation of concerns
|
|
|
|
|
- Poor code reusability
|
|
|
|
|
- Difficult to test individual command handlers
|
|
|
|
|
|
|
|
|
|
4. **User Experience Issues**
|
|
|
|
|
- No bi-directional help system
|
|
|
|
|
- Inconsistent command shortcuts
|
|
|
|
|
- Help system not fully integrated
|
|
|
|
|
|
|
|
|
|
## Decision
|
|
|
|
|
|
|
|
|
|
We refactored the monolithic CLI into a **modular, domain-driven architecture** with the following structure:
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```bash
|
2026-01-14 04:53:21 +00:00
|
|
|
provisioning/core/nulib/
|
|
|
|
|
├── provisioning (211 lines) ⬅️ 84% reduction
|
|
|
|
|
├── main_provisioning/
|
|
|
|
|
│ ├── flags.nu (139 lines) ⭐ Centralized flag handling
|
|
|
|
|
│ ├── dispatcher.nu (264 lines) ⭐ Command routing
|
|
|
|
|
│ ├── mod.nu (updated)
|
|
|
|
|
│ └── commands/ ⭐ Domain-focused handlers
|
|
|
|
|
│ ├── configuration.nu (316 lines)
|
|
|
|
|
│ ├── development.nu (72 lines)
|
|
|
|
|
│ ├── generation.nu (78 lines)
|
|
|
|
|
│ ├── infrastructure.nu (117 lines)
|
|
|
|
|
│ ├── orchestration.nu (64 lines)
|
|
|
|
|
│ ├── utilities.nu (157 lines)
|
|
|
|
|
│ └── workspace.nu (56 lines)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Key Components
|
|
|
|
|
|
|
|
|
|
#### 1. Centralized Flag Handling (`flags.nu`)
|
|
|
|
|
|
|
|
|
|
Single source of truth for all flag parsing and argument building:
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```javascript
|
2026-01-14 04:53:21 +00:00
|
|
|
export def parse_common_flags [flags: record]: nothing -> record
|
|
|
|
|
export def build_module_args [flags: record, extra: string = ""]: nothing -> string
|
|
|
|
|
export def set_debug_env [flags: record]
|
|
|
|
|
export def get_debug_flag [flags: record]: nothing -> string
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Benefits:**
|
|
|
|
|
|
|
|
|
|
- Eliminates 50+ instances of duplicate code
|
|
|
|
|
- Single place to add/modify flags
|
|
|
|
|
- Consistent flag handling across all commands
|
|
|
|
|
- Reduced from 10 lines to 3 lines per command handler
|
|
|
|
|
|
|
|
|
|
#### 2. Command Dispatcher (`dispatcher.nu`)
|
|
|
|
|
|
|
|
|
|
Central routing with 80+ command mappings:
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```javascript
|
2026-01-14 04:53:21 +00:00
|
|
|
export def get_command_registry []: nothing -> record # 80+ shortcuts
|
|
|
|
|
export def dispatch_command [args: list, flags: record] # Main router
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Features:**
|
|
|
|
|
|
|
|
|
|
- Command registry with shortcuts (ws → workspace, orch → orchestrator, etc.)
|
|
|
|
|
- Bi-directional help support (`provisioning ws help` works)
|
|
|
|
|
- Domain-based routing (infrastructure, orchestration, development, etc.)
|
|
|
|
|
- Special command handling (create, delete, price, etc.)
|
|
|
|
|
|
|
|
|
|
#### 3. Domain Command Handlers (`commands/*.nu`)
|
|
|
|
|
|
|
|
|
|
Seven focused modules organized by domain:
|
|
|
|
|
|
|
|
|
|
| Module | Lines | Responsibility |
|
|
|
|
|
| -------- | ------- | ---------------- |
|
|
|
|
|
| `infrastructure.nu` | 117 | Server, taskserv, cluster, infra |
|
|
|
|
|
| `orchestration.nu` | 64 | Workflow, batch, orchestrator |
|
|
|
|
|
| `development.nu` | 72 | Module, layer, version, pack |
|
|
|
|
|
| `workspace.nu` | 56 | Workspace, template |
|
|
|
|
|
| `generation.nu` | 78 | Generate commands |
|
|
|
|
|
| `utilities.nu` | 157 | SSH, SOPS, cache, providers |
|
|
|
|
|
| `configuration.nu` | 316 | Env, show, init, validate |
|
|
|
|
|
|
|
|
|
|
Each handler:
|
|
|
|
|
|
|
|
|
|
- Exports `handle_<domain>_command` function
|
|
|
|
|
- Uses shared flag handling
|
|
|
|
|
- Provides error messages with usage hints
|
|
|
|
|
- Isolated and testable
|
|
|
|
|
|
|
|
|
|
## Architecture Principles
|
|
|
|
|
|
|
|
|
|
### 1. Separation of Concerns
|
|
|
|
|
|
|
|
|
|
- **Routing** → `dispatcher.nu`
|
|
|
|
|
- **Flag parsing** → `flags.nu`
|
|
|
|
|
- **Business logic** → `commands/*.nu`
|
|
|
|
|
- **Help system** → `help_system.nu` (existing)
|
|
|
|
|
|
|
|
|
|
### 2. Single Responsibility
|
|
|
|
|
|
|
|
|
|
Each module has ONE clear purpose:
|
|
|
|
|
|
|
|
|
|
- Command handlers execute specific domains
|
|
|
|
|
- Dispatcher routes to correct handler
|
|
|
|
|
- Flags module normalizes all inputs
|
|
|
|
|
|
|
|
|
|
### 3. DRY (Don't Repeat Yourself)
|
|
|
|
|
|
|
|
|
|
Eliminated repetition:
|
|
|
|
|
|
|
|
|
|
- Flag handling: 50+ instances → 1 function
|
|
|
|
|
- Command routing: Scattered logic → Command registry
|
|
|
|
|
- Error handling: Consistent across all domains
|
|
|
|
|
|
|
|
|
|
### 4. Open/Closed Principle
|
|
|
|
|
|
|
|
|
|
- Open for extension: Add new handlers easily
|
|
|
|
|
- Closed for modification: Core routing unchanged
|
|
|
|
|
|
|
|
|
|
### 5. Dependency Inversion
|
|
|
|
|
|
|
|
|
|
All handlers depend on abstractions (flag records, not concrete flags):
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```bash
|
2026-01-14 04:53:21 +00:00
|
|
|
# Handler signature
|
|
|
|
|
export def handle_infrastructure_command [
|
|
|
|
|
command: string
|
|
|
|
|
ops: string
|
|
|
|
|
flags: record # ⬅️ Abstraction, not concrete flags
|
|
|
|
|
]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Implementation Details
|
|
|
|
|
|
|
|
|
|
### Migration Path (Completed in 2 Phases)
|
|
|
|
|
|
|
|
|
|
**Phase 1: Foundation**
|
|
|
|
|
|
|
|
|
|
1. ✅ Created `commands/` directory structure
|
|
|
|
|
2. ✅ Created `flags.nu` with common flag handling
|
|
|
|
|
3. ✅ Created initial command handlers (infrastructure, utilities, configuration)
|
|
|
|
|
4. ✅ Created `dispatcher.nu` with routing logic
|
|
|
|
|
5. ✅ Refactored main file (1,329 → 211 lines)
|
|
|
|
|
6. ✅ Tested basic functionality
|
|
|
|
|
|
|
|
|
|
**Phase 2: Completion**
|
|
|
|
|
|
|
|
|
|
1. ✅ Fixed bi-directional help (`provisioning ws help` now works)
|
|
|
|
|
2. ✅ Created remaining handlers (orchestration, development, workspace, generation)
|
|
|
|
|
3. ✅ Removed duplicate code from dispatcher
|
|
|
|
|
4. ✅ Added comprehensive test suite
|
|
|
|
|
5. ✅ Verified all shortcuts work
|
|
|
|
|
|
|
|
|
|
### Bi-directional Help System
|
|
|
|
|
|
|
|
|
|
Users can now access help in multiple ways:
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```bash
|
2026-01-14 04:53:21 +00:00
|
|
|
# All these work equivalently:
|
|
|
|
|
provisioning help workspace
|
|
|
|
|
provisioning workspace help # ⬅️ NEW: Bi-directional
|
|
|
|
|
provisioning ws help # ⬅️ NEW: With shortcuts
|
|
|
|
|
provisioning help ws # ⬅️ NEW: Shortcut in help
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Implementation:**
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```bash
|
2026-01-14 04:53:21 +00:00
|
|
|
# Intercept "command help" → "help command"
|
|
|
|
|
let first_op = if ($ops_list | length) > 0 { ($ops_list | get 0) } else { "" }
|
|
|
|
|
if $first_op in ["help" "h"] {
|
|
|
|
|
exec $"($env.PROVISIONING_NAME)" help $task --notitles
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Command Shortcuts
|
|
|
|
|
|
|
|
|
|
Comprehensive shortcut system with 30+ mappings:
|
|
|
|
|
|
|
|
|
|
**Infrastructure:**
|
|
|
|
|
|
|
|
|
|
- `s` → `server`
|
|
|
|
|
- `t`, `task` → `taskserv`
|
|
|
|
|
- `cl` → `cluster`
|
|
|
|
|
- `i` → `infra`
|
|
|
|
|
|
|
|
|
|
**Orchestration:**
|
|
|
|
|
|
|
|
|
|
- `wf`, `flow` → `workflow`
|
|
|
|
|
- `bat` → `batch`
|
|
|
|
|
- `orch` → `orchestrator`
|
|
|
|
|
|
|
|
|
|
**Development:**
|
|
|
|
|
|
|
|
|
|
- `mod` → `module`
|
|
|
|
|
- `lyr` → `layer`
|
|
|
|
|
|
|
|
|
|
**Workspace:**
|
|
|
|
|
|
|
|
|
|
- `ws` → `workspace`
|
|
|
|
|
- `tpl`, `tmpl` → `template`
|
|
|
|
|
|
|
|
|
|
## Testing
|
|
|
|
|
|
|
|
|
|
Comprehensive test suite created (`tests/test_provisioning_refactor.nu`):
|
|
|
|
|
|
|
|
|
|
### Test Coverage
|
|
|
|
|
|
|
|
|
|
- ✅ Main help display
|
|
|
|
|
- ✅ Category help (infrastructure, orchestration, development, workspace)
|
|
|
|
|
- ✅ Bi-directional help routing
|
|
|
|
|
- ✅ All command shortcuts
|
|
|
|
|
- ✅ Category shortcut help
|
|
|
|
|
- ✅ Command routing to correct handlers
|
|
|
|
|
|
|
|
|
|
### Test Results
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```bash
|
2026-01-14 04:53:21 +00:00
|
|
|
📋 Testing main help... ✅
|
|
|
|
|
📋 Testing category help... ✅
|
|
|
|
|
🔄 Testing bi-directional help... ✅
|
|
|
|
|
⚡ Testing command shortcuts... ✅
|
|
|
|
|
📚 Testing category shortcut help... ✅
|
|
|
|
|
🎯 Testing command routing... ✅
|
|
|
|
|
|
|
|
|
|
📊 TEST RESULTS: 6 passed, 0 failed
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Results
|
|
|
|
|
|
|
|
|
|
### Quantitative Improvements
|
|
|
|
|
|
|
|
|
|
| Metric | Before | After | Improvement |
|
|
|
|
|
| -------- | -------- | ------- | ------------- |
|
|
|
|
|
| **Main file size** | 1,329 lines | 211 lines | **84% reduction** |
|
|
|
|
|
| **Command handler** | 1 massive match (1,100+ lines) | 7 focused modules | **Domain separation** |
|
|
|
|
|
| **Flag handling** | Repeated 50+ times | 1 function | **98% duplication removal** |
|
|
|
|
|
| **Code per command** | 10 lines | 3 lines | **70% reduction** |
|
|
|
|
|
| **Modules count** | 1 monolith | 9 modules | **Modular architecture** |
|
|
|
|
|
| **Test coverage** | None | 6 test groups | **Comprehensive testing** |
|
|
|
|
|
|
|
|
|
|
### Qualitative Improvements
|
|
|
|
|
|
|
|
|
|
**Maintainability**
|
|
|
|
|
|
|
|
|
|
- ✅ Easy to find specific command logic
|
|
|
|
|
- ✅ Clear separation of concerns
|
|
|
|
|
- ✅ Self-documenting structure
|
|
|
|
|
- ✅ Focused modules (< 320 lines each)
|
|
|
|
|
|
|
|
|
|
**Extensibility**
|
|
|
|
|
|
|
|
|
|
- ✅ Add new commands: Just update appropriate handler
|
|
|
|
|
- ✅ Add new flags: Single function update
|
|
|
|
|
- ✅ Add new shortcuts: Update command registry
|
|
|
|
|
- ✅ No massive file edits required
|
|
|
|
|
|
|
|
|
|
**Testability**
|
|
|
|
|
|
|
|
|
|
- ✅ Isolated command handlers
|
|
|
|
|
- ✅ Mockable dependencies
|
|
|
|
|
- ✅ Test individual domains
|
|
|
|
|
- ✅ Fast test execution
|
|
|
|
|
|
|
|
|
|
**Developer Experience**
|
|
|
|
|
|
|
|
|
|
- ✅ Lower cognitive load
|
|
|
|
|
- ✅ Faster onboarding
|
|
|
|
|
- ✅ Easier code review
|
|
|
|
|
- ✅ Better IDE navigation
|
|
|
|
|
|
|
|
|
|
## Trade-offs
|
|
|
|
|
|
|
|
|
|
### Advantages
|
|
|
|
|
|
|
|
|
|
1. **Dramatically reduced complexity**: 84% smaller main file
|
|
|
|
|
2. **Better organization**: Domain-focused modules
|
|
|
|
|
3. **Easier testing**: Isolated, testable units
|
|
|
|
|
4. **Improved maintainability**: Clear structure, less duplication
|
|
|
|
|
5. **Enhanced UX**: Bi-directional help, shortcuts
|
|
|
|
|
6. **Future-proof**: Easy to extend
|
|
|
|
|
|
|
|
|
|
### Disadvantages
|
|
|
|
|
|
|
|
|
|
1. **More files**: 1 file → 9 files (but smaller, focused)
|
|
|
|
|
2. **Module imports**: Need to import multiple modules (automated via mod.nu)
|
|
|
|
|
3. **Learning curve**: New structure requires documentation (this ADR)
|
|
|
|
|
|
|
|
|
|
**Decision**: Advantages significantly outweigh disadvantages.
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
### Before: Repetitive Flag Handling
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```bash
|
2026-01-14 04:53:21 +00:00
|
|
|
"server" => {
|
|
|
|
|
let use_check = if $check { "--check "} else { "" }
|
|
|
|
|
let use_yes = if $yes { "--yes" } else { "" }
|
|
|
|
|
let use_wait = if $wait { "--wait" } else { "" }
|
|
|
|
|
let use_keepstorage = if $keepstorage { "--keepstorage "} else { "" }
|
|
|
|
|
let str_infra = if $infra != null { $"--infra ($infra) "} else { "" }
|
|
|
|
|
let str_outfile = if $outfile != null { $"--outfile ($outfile) "} else { "" }
|
|
|
|
|
let str_out = if $out != null { $"--out ($out) "} else { "" }
|
|
|
|
|
let arg_include_notuse = if $include_notuse { $"--include_notuse "} else { "" }
|
|
|
|
|
run_module $"($str_ops) ($str_infra) ($use_check)..." "server" --exec
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### After: Clean, Reusable
|
|
|
|
|
|
2026-01-14 04:53:58 +00:00
|
|
|
```python
|
2026-01-14 04:53:21 +00:00
|
|
|
def handle_server [ops: string, flags: record] {
|
|
|
|
|
let args = build_module_args $flags $ops
|
|
|
|
|
run_module $args "server" --exec
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Reduction: 10 lines → 3 lines (70% reduction)**
|
|
|
|
|
|
|
|
|
|
## Future Considerations
|
|
|
|
|
|
|
|
|
|
### Potential Enhancements
|
|
|
|
|
|
|
|
|
|
1. **Unit test expansion**: Add tests for each command handler
|
|
|
|
|
2. **Integration tests**: End-to-end workflow tests
|
|
|
|
|
3. **Performance profiling**: Measure routing overhead (expected to be negligible)
|
|
|
|
|
4. **Documentation generation**: Auto-generate docs from handlers
|
|
|
|
|
5. **Plugin architecture**: Allow third-party command extensions
|
|
|
|
|
|
|
|
|
|
### Migration Guide for Contributors
|
|
|
|
|
|
|
|
|
|
See `docs/development/COMMAND_HANDLER_GUIDE.md` for:
|
|
|
|
|
|
|
|
|
|
- How to add new commands
|
|
|
|
|
- How to modify existing handlers
|
|
|
|
|
- How to add new shortcuts
|
|
|
|
|
- Testing guidelines
|
|
|
|
|
|
|
|
|
|
## Related Documentation
|
|
|
|
|
|
|
|
|
|
- **Architecture Overview**: `docs/architecture/system-overview.md`
|
|
|
|
|
- **Developer Guide**: `docs/development/COMMAND_HANDLER_GUIDE.md`
|
|
|
|
|
- **Main Project Docs**: `CLAUDE.md` (updated with new structure)
|
|
|
|
|
- **Test Suite**: `tests/test_provisioning_refactor.nu`
|
|
|
|
|
|
|
|
|
|
## Conclusion
|
|
|
|
|
|
|
|
|
|
This refactoring transforms the provisioning CLI from a monolithic, hard-to-maintain script into a modular, well-organized system following software
|
|
|
|
|
engineering best practices. The 84% reduction in main file size, elimination of code duplication, and comprehensive test coverage position the project
|
|
|
|
|
for sustainable long-term growth.
|
|
|
|
|
|
|
|
|
|
The new architecture enables:
|
|
|
|
|
|
|
|
|
|
- **Faster development**: Add commands in minutes, not hours
|
|
|
|
|
- **Better quality**: Isolated testing catches bugs early
|
|
|
|
|
- **Easier maintenance**: Clear structure reduces cognitive load
|
|
|
|
|
- **Enhanced UX**: Shortcuts and bi-directional help improve usability
|
|
|
|
|
|
|
|
|
|
**Status**: Successfully implemented and tested. All commands operational. Ready for production use.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
*This ADR documents a major architectural improvement completed on 2025-09-30.*
|