use ../../lib_provisioning/config/accessor.nu * use ../../lib_provisioning/utils/interface.nu [_print] use ../../workspace/state.nu * use ../../workspace/sync.nu * export def handle_state_command [cmd: string, ops: string, flags: record] { let workspace_path = if ($env.PROVISIONING_WORKSPACE_PATH? | is-not-empty) { $env.PROVISIONING_WORKSPACE_PATH } else { $env.PWD } let infra = ($flags | get -o infra | default "") let server = ($flags | get -o server | default "") let taskserv = ($flags | get -o taskserv | default "") let kubeconfig = ($flags | get -o kubeconfig | default "") # When help_category == command name ("state"), the subcommand lands in $ops, not $cmd. let subcmd = if ($ops | is-not-empty) { ($ops | split row " " | first) } else { $cmd } match $subcmd { "show" | "s" => { state-show $workspace_path --server $server }, "init" | "i" => { let curr_settings = (find_get_settings --infra $infra) state-init $workspace_path $curr_settings _print $"State initialized at (state-path $workspace_path)" }, "reset" | "r" => { if ($server | is-empty) or ($taskserv | is-empty) { error make { msg: "state reset requires --server --taskserv " } } state-node-reset $workspace_path $server $taskserv _print $"($server)/($taskserv) reset to pending" }, "migrate" | "m" => { state-migrate-from-json $workspace_path }, "sync" => { let curr_settings = (find_get_settings --infra $infra) # 1. Drift detection + reconcile against servers.ncl let drift_rows = (state-drift $workspace_path $curr_settings --server $server) let has_drift = ($drift_rows | where drift != "ok" | is-not-empty) if $has_drift { _print "── drift ──" print ($drift_rows | where drift != "ok" | table) let result = (state-reconcile $workspace_path $curr_settings --server $server) if ($result.removed | is-not-empty) { _print $"šŸ—‘ Removed ($result.removed | length) orphaned" } if ($result.added | is-not-empty) { _print $"āž• Added ($result.added | length) pending" } } else { _print "āœ… No drift against servers.ncl" } # 2. External API sync (Hetzner, K8s, SSH) let skip_ssh = ($flags | get -o skip_ssh | default false) state-sync $workspace_path $curr_settings --kubeconfig $kubeconfig --skip-ssh=$skip_ssh }, "drift" | "d" => { let curr_settings = (find_get_settings --infra $infra) let rows = (state-drift $workspace_path $curr_settings --server $server) let has_drift = ($rows | where drift != "ok" | is-not-empty) if ($rows | is-empty) { _print "(no state entries to compare)" } else { print ($rows | table) if $has_drift { _print $"\n⚠ Drift detected. Run (_ansi yellow_bold)provisioning state reconcile(_ansi reset) to fix." } else { _print "\nāœ… No drift — state matches servers.ncl" } } }, "reconcile" | "rec" => { let curr_settings = (find_get_settings --infra $infra) let dry_run = ($flags | get -o dry_run | default false) # Always show drift first let drift_rows = (state-drift $workspace_path $curr_settings --server $server) let has_drift = ($drift_rows | where drift != "ok" | is-not-empty) if not $has_drift { _print "āœ… No drift — nothing to reconcile" return } print ($drift_rows | where drift != "ok" | table) if $dry_run { _print "\n(dry-run: no changes applied)" return } let result = (state-reconcile $workspace_path $curr_settings --server $server) if ($result.removed | is-not-empty) { _print $"\nšŸ—‘ Removed ($result.removed | length) orphaned entries:" for r in $result.removed { _print $" ($r.server)/($r.taskserv)" } } if ($result.added | is-not-empty) { _print $"\nāž• Added ($result.added | length) pending entries:" for a in $result.added { _print $" ($a.server)/($a.taskserv)" } } _print "\nāœ… State reconciled with servers.ncl" }, _ => { _print "Usage: provisioning state [--infra ]" _print "" _print " show [--server ] — display state table" _print " init [--infra ] — bootstrap state from settings" _print " reset --server --taskserv — reset node to pending" _print " migrate — migrate .json → .ncl" _print " sync [--infra ] [--kubeconfig ] — reconcile from APIs" _print " drift [--infra ] [--server ] — detect state vs servers.ncl divergence" _print " reconcile [--infra ] [--server ] — fix drift (remove orphaned, add missing)" }, } }