provisioning/docs/src/architecture/adr/ADR-006-provisioning-cli-refactoring.md

390 lines
12 KiB
Markdown
Raw Normal View History

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.*