prvng_core/scripts/refactor-try-catch.nu
Jesús Pérez 894046ef5a
feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration
- 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.
2026-04-17 04:27:33 +01:00

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