- DAG architecture: `dag show/validate/export` (nulib/main_provisioning/dag.nu),
config loader (lib_provisioning/config/loader/dag.nu), taskserv dag-executor.
Backed by schemas/lib/dag/*.ncl; orchestrator emits NATS events via
WorkspaceComposition::into_workflow. See ADR-020, ADR-021.
- Unified Component Architecture: components/mod.nu, main_provisioning/
{components,workflow,extensions,ontoref-queries}.nu. Full workflow engine with
topological sort and NATS subject emission. Blocks A-H complete (libre-daoshi).
- Commands-registry: nulib/commands-registry.ncl (Nickel source, 314 lines) +
JSON cache at ~/.cache/provisioning/commands-registry.json rebuilt on source
change. cli/provisioning fast-path alias expansion avoids cold Nu startup.
ADDING_COMMANDS.md documents new-command workflow.
- Platform service manager: service-manager.nu (+573), startup.nu (+611),
service-check.nu (+255); autostart/bootstrap/health/target refactored.
- Nushell 0.112.2 migration: removed all try/catch and bash redirections;
external commands prefixed with ^; type signatures enforced. Driven by
scripts/refactor-try-catch{,-simplified}.nu.
- TTY stack: removed shlib/*-tty.sh; replaced by cli/tty-dispatch.sh,
tty-filter.sh, tty-commands.conf.
- New domain modules: images/ (golden image lifecycle), workspace/{state,sync}.nu,
main_provisioning/{bootstrap,cluster-deploy,fip,state}.nu, commands/{state,
build,integrations/auth,utilities/alias}.nu, platform.nu expanded (+874).
- Config loader overhaul: loader/core.nu slimmed (-759), cache/core.nu
refactored (-454), removed legacy loaders/file_loader.nu (-330).
- Thirteen new provisioning-<domain>.nu top-level modules for bash dispatcher.
- Tests: test_workspace_state.nu (+351); updates to test_oci_registry,
test_services.
- README + CHANGELOG updated.
321 lines
9.7 KiB
Text
321 lines
9.7 KiB
Text
#!/usr/bin/env nu
|
|
# Automated try-catch to Result pattern refactorer
|
|
# Refactors 276+ try-catch blocks to use Result pattern helpers
|
|
# Version: 1.0
|
|
|
|
use std log
|
|
|
|
# Configuration
|
|
let config = {
|
|
dry_run: false
|
|
backup: true
|
|
verbose: true
|
|
patterns: [
|
|
"bash_check" # try { bash -c ... | complete } catch { ... }
|
|
"bash_or" # try { bash ... } catch { fallback }
|
|
"json_read" # try { open file | from json } catch { ... }
|
|
"bash_wrap" # try { bash -c ... } catch { ... }
|
|
]
|
|
}
|
|
|
|
# Report structure
|
|
mut report = {
|
|
total_files: 0
|
|
files_processed: 0
|
|
patterns_found: {}
|
|
errors: []
|
|
changes_by_file: {}
|
|
}
|
|
|
|
# Add result.nu import if not present
|
|
def ensure-result-import [file_path: string] {
|
|
let content_result = (do { open $file_path } | complete)
|
|
if $content_result.exit_code != 0 {
|
|
return false
|
|
}
|
|
let content = $content_result.stdout
|
|
|
|
# Check if already imported
|
|
if ($content | str contains "use.*result.nu") {
|
|
return false
|
|
}
|
|
|
|
# Check where to insert import
|
|
let lines = ($content | lines)
|
|
let insert_pos = (
|
|
$lines
|
|
| enumerate
|
|
| find -a {|x| $x.item =~ "^(use|def|export)" }
|
|
| get 0?.index
|
|
| default 0
|
|
)
|
|
|
|
# Insert import
|
|
let new_lines = (
|
|
$lines
|
|
| enumerate
|
|
| each {|x|
|
|
if $x.index == $insert_pos {
|
|
["use lib_provisioning/result.nu *", $x.item]
|
|
} else {
|
|
$x.item
|
|
}
|
|
}
|
|
| flatten
|
|
)
|
|
|
|
true
|
|
}
|
|
|
|
# Pattern 1: bash-check (try { bash -c ... | complete } catch { {exit_code: 1, stderr: $err} })
|
|
def refactor-bash-check [content: string] -> {changed: bool, content: string} {
|
|
# Match pattern: try { bash -c $"..." | complete } catch {|err| {exit_code: 1, stderr: $err} }
|
|
let pattern = 'try\s*\{\s*bash\s+-c\s+\$"([^"]+)"\s*\|\s*complete\s*\}\s*catch\s*\{\|err\|\s*\{exit_code:\s*1,\s*stderr:\s*\$err\s*\}\s*\}'
|
|
|
|
if not ($content =~ $pattern) {
|
|
return {changed: false, content: $content}
|
|
}
|
|
|
|
# Replace with bash-check helper
|
|
let new_content = (
|
|
$content
|
|
| str replace -a -m $pattern 'bash-check $"$1"'
|
|
)
|
|
|
|
{changed: true, content: $new_content}
|
|
}
|
|
|
|
# Pattern 2: bash-or (try { bash -c ... } catch { fallback })
|
|
def refactor-bash-or [content: string] -> {changed: bool, content: string} {
|
|
# Match pattern: try { bash -c $"..." } catch { fallback_value }
|
|
let pattern = 'try\s*\{\s*bash\s+-c\s+\$"([^"]+)"\s*\}\s*catch\s*\{\s*([^}]+)\s*\}'
|
|
|
|
if not ($content =~ $pattern) {
|
|
return {changed: false, content: $content}
|
|
}
|
|
|
|
# Replace with bash-or helper
|
|
let new_content = (
|
|
$content
|
|
| str replace -a -m $pattern 'bash-or $"$1" $2'
|
|
)
|
|
|
|
{changed: true, content: $new_content}
|
|
}
|
|
|
|
# Pattern 3: json-read (try { open file | from json } catch { ... })
|
|
def refactor-json-read [content: string] -> {changed: bool, content: string} {
|
|
# Match pattern: try { open $path | from json } catch { default_value }
|
|
let pattern = 'try\s*\{\s*open\s+(\$\w+)\s*\|\s*from\s+json\s*\}\s*catch\s*\{\s*([^}]+)\s*\}'
|
|
|
|
if not ($content =~ $pattern) {
|
|
return {changed: false, content: $content}
|
|
}
|
|
|
|
# Replace with json-read helper + match-result
|
|
let new_content = (
|
|
$content
|
|
| str replace -a -m $pattern '(json-read $1) | match-result {|data| $data} {|_err| $2}'
|
|
)
|
|
|
|
{changed: true, content: $new_content}
|
|
}
|
|
|
|
# Pattern 4: bash-wrap (try { bash -c ... } catch { error_record })
|
|
def refactor-bash-wrap [content: string] -> {changed: bool, content: string} {
|
|
# Match pattern: try { bash -c $"..." } catch {|err| error_record }
|
|
let pattern = 'try\s*\{\s*bash\s+-c\s+\$"([^"]+)"\s*\}\s*catch\s*\{\|err\|\s*([^}]+)\s*\}'
|
|
|
|
if not ($content =~ $pattern) {
|
|
return {changed: false, content: $content}
|
|
}
|
|
|
|
# Replace with bash-wrap helper
|
|
let new_content = (
|
|
$content
|
|
| str replace -a -m $pattern '(bash-wrap $"$1") | match-result {|output| output} {|err| $2}'
|
|
)
|
|
|
|
{changed: true, content: $new_content}
|
|
}
|
|
|
|
# Apply all refactoring patterns
|
|
def apply-patterns [content: string] -> {changed: bool, content: string, patterns_applied: list} {
|
|
mut result = {changed: false, content: $content, patterns_applied: []}
|
|
|
|
# Apply each pattern
|
|
for pattern in ["bash_check", "bash_or", "json_read", "bash_wrap"] {
|
|
let pattern_result = (
|
|
match $pattern {
|
|
"bash_check" => (refactor-bash-check $result.content)
|
|
"bash_or" => (refactor-bash-or $result.content)
|
|
"json_read" => (refactor-json-read $result.content)
|
|
"bash_wrap" => (refactor-bash-wrap $result.content)
|
|
_ => {changed: false, content: $result.content}
|
|
}
|
|
)
|
|
|
|
if $pattern_result.changed {
|
|
$result.changed = true
|
|
$result.content = $pattern_result.content
|
|
$result.patterns_applied = ($result.patterns_applied | append $pattern)
|
|
}
|
|
}
|
|
|
|
$result
|
|
}
|
|
|
|
# Refactor single file
|
|
def refactor-file [file_path: string] -> record {
|
|
let content_result = (do { open $file_path } | complete)
|
|
if $content_result.exit_code != 0 {
|
|
return {
|
|
file: $file_path
|
|
changed: false
|
|
patterns_applied: []
|
|
import_added: false
|
|
backup_created: false
|
|
}
|
|
}
|
|
let original_content = $content_result.stdout
|
|
|
|
# Ensure result.nu import
|
|
let import_added = (ensure-result-import $file_path)
|
|
|
|
# Apply refactoring patterns
|
|
let refactor_result = (apply-patterns $original_content)
|
|
|
|
# Check if any changes
|
|
let has_changes = ($refactor_result.changed or $import_added)
|
|
|
|
if $has_changes and (not $config.dry_run) {
|
|
# Create backup
|
|
if $config.backup {
|
|
let backup_result = (do { bash -c $"cp ($file_path) ($file_path).bak" } | complete)
|
|
if $backup_result.exit_code != 0 {
|
|
# Log but continue
|
|
if $config.verbose {
|
|
print $"Warning: backup failed for ($file_path)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Write new content
|
|
let save_result = (do { $refactor_result.content | save -f $file_path } | complete)
|
|
if $save_result.exit_code != 0 {
|
|
if $config.verbose {
|
|
print $"Warning: save failed for ($file_path)"
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
file: $file_path
|
|
changed: $has_changes
|
|
patterns_applied: $refactor_result.patterns_applied
|
|
import_added: $import_added
|
|
backup_created: ($has_changes and $config.backup)
|
|
}
|
|
}
|
|
|
|
# Main refactoring loop
|
|
def main [] {
|
|
print "🔧 Automated try-catch → Result Pattern Refactorer"
|
|
print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
print $"Dry run: ($config.dry_run)"
|
|
print $"Backup enabled: ($config.backup)"
|
|
print ""
|
|
|
|
# Find all .nu files with try-catch
|
|
print "📁 Scanning for try-catch patterns..."
|
|
let files = (
|
|
glob "provisioning/core/nulib/**/*.nu"
|
|
| par-each {|f|
|
|
if (open $f | str contains "try\s*{") {
|
|
$f
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
| filter {|x| $x != null}
|
|
)
|
|
|
|
$report.total_files = ($files | length)
|
|
print $"Found ($($files | length)) files with try-catch patterns"
|
|
print ""
|
|
|
|
# Process files
|
|
print "🔄 Processing files..."
|
|
let results = (
|
|
$files | par-each {|file|
|
|
refactor-file $file
|
|
}
|
|
)
|
|
|
|
# Generate report
|
|
mut changed_count = 0
|
|
mut pattern_counts = {}
|
|
|
|
for result in $results {
|
|
if $result.changed {
|
|
$changed_count += 1
|
|
$report.changes_by_file = ($report.changes_by_file | insert $result.file {
|
|
patterns: $result.patterns_applied
|
|
backup: $result.backup_created
|
|
})
|
|
|
|
for pattern in $result.patterns_applied {
|
|
let current = ($pattern_counts | get -i $pattern | default 0)
|
|
$pattern_counts = ($pattern_counts | insert $pattern ($current + 1))
|
|
}
|
|
}
|
|
|
|
if $config.verbose {
|
|
let status = (if $result.changed { "✅ CHANGED" } else { "⏭️ SKIPPED" })
|
|
print $"($status): ($result.file | path basename)"
|
|
}
|
|
}
|
|
|
|
$report.files_processed = $changed_count
|
|
$report.patterns_found = $pattern_counts
|
|
|
|
# Final report
|
|
print ""
|
|
print "📊 REFACTORING REPORT"
|
|
print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
print $"Total files scanned: ($report.total_files)"
|
|
print $"Files changed: ($report.files_processed)"
|
|
print ""
|
|
print "Patterns refactored:"
|
|
for {pattern, count} in ($report.patterns_found | to entries) {
|
|
print $" • ($pattern): ($count) occurrences"
|
|
}
|
|
|
|
if $config.dry_run {
|
|
print ""
|
|
print "⚠️ DRY RUN MODE - No files were modified"
|
|
print "Run with --no-dry-run to apply changes"
|
|
} else if $config.backup {
|
|
print ""
|
|
print "✅ Backups created for all changed files (.bak)"
|
|
}
|
|
|
|
print ""
|
|
print "Next steps:"
|
|
print "1. Review changes: git diff"
|
|
print "2. Verify helpers are imported: grep 'use lib_provisioning/result.nu' *.nu"
|
|
print "3. Test: cargo test (if applicable)"
|
|
print "4. Commit: git add -A && git commit -m 'refactor: eliminate try-catch blocks'"
|
|
}
|
|
|
|
# Parse command line arguments
|
|
let args = $env.ARGS.positional
|
|
if ($args | any {|arg| $arg == "--apply"}) {
|
|
$config.dry_run = false
|
|
}
|
|
if ($args | any {|arg| $arg == "--verbose"}) {
|
|
$config.verbose = true
|
|
}
|
|
|
|
# Run main
|
|
main
|