557 files merged. Conflicts resolved: - CHANGELOG.md: took refactor/lazy-loading (session changelog) - versions.ncl: took refactor/lazy-loading (adds typedialog entries)
127 lines
5.5 KiB
Text
127 lines
5.5 KiB
Text
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 <hostname> --taskserv <name>" }
|
||
}
|
||
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 <subcommand> [--infra <path>]"
|
||
_print ""
|
||
_print " show [--server <hostname>] — display state table"
|
||
_print " init [--infra <path>] — bootstrap state from settings"
|
||
_print " reset --server <hostname> --taskserv <name> — reset node to pending"
|
||
_print " migrate — migrate .json → .ncl"
|
||
_print " sync [--infra <path>] [--kubeconfig <path>] — reconcile from APIs"
|
||
_print " drift [--infra <path>] [--server <hostname>] — detect state vs servers.ncl divergence"
|
||
_print " reconcile [--infra <path>] [--server <hostname>] — fix drift (remove orphaned, add missing)"
|
||
},
|
||
}
|
||
}
|