#!/usr/bin/env nu # Thin entry for component commands. # Bypasses full dispatcher — loads only components/mod.nu + targeted lib_provisioning. # Mirrors the provisioning-taskserv.nu pattern for <1s startup. export-env { let lib_dirs_raw = ($env.NU_LIB_DIRS? | default "") let current_lib_dirs = if ($lib_dirs_raw | type) == "string" { if ($lib_dirs_raw | is-empty) { [] } else { ($lib_dirs_raw | split row ":") } } else { $lib_dirs_raw } let dynamic = ($env.PROVISIONING? | default "" | path join "core" "nulib") $env.NU_LIB_DIRS = ([ "/opt/provisioning/core/nulib" "/usr/local/provisioning/core/nulib" ] | append $current_lib_dirs | append (if ($dynamic | is-not-empty) { [$dynamic] } else { [] })) } use primitives/io/interface.nu [_ansi] use cli/components.nu [ "main component list" "main component info" "main component mark-complete" "main component bulk-mark-complete" "main component drop-key" "main component drop-server" "main component state-check" "main component state-normalize" "main validate capabilities" "main validate components" "main component install" "main component delete" "main component update" "main component reinstall" "main component restart" "main component backup" "main component restore" "main component check-updates" "main component render" comp-load-component-record ] use tools/nickel/process.nu [comp-ncl-export] use domain/cluster/access.nu [comp-kubectl-access] use domain/workspace/resolve.nu [comp-state-server comp-ws-path comp-ws-infra] use cli/components_cluster.nu [ comp-live-check comp-pods-for comp-ssh-pods comp-ssh-k8s-data comp-ssh-describe-pod comp-load-server-info comp-server-badge comp-cluster-summary comp-cluster-badge comp-cache-last-sync comp-settings-k0s-version ] def state-color [s: string]: nothing -> string { match $s { "completed" => $"(_ansi green)($s)(_ansi reset)" "running" => $"(_ansi yellow)($s)(_ansi reset)" "failed" => $"(_ansi red)($s)(_ansi reset)" "blocked" => $"(_ansi red)($s)(_ansi reset)" "partial" => $"(_ansi yellow)($s)(_ansi reset)" _ => $"(_ansi dark_gray)($s)(_ansi reset)" } } def main [ ...args: string --workspace (-w): string = "" --mode: string = "" --out (-o): string = "" --infra (-i): string = "" --server (-s): string = "" --op: string = "init" # render: which op's plan scripts to include --bundle # render: also write .tar.gz.b64 --check --dry-run # alias for --check: validates bundle without submitting --wait --live --ext --json (-j) --debug (-x) --filter (-f): string = "" # substring on name, or col=val (state=failed, ns=monitoring) ]: nothing -> nothing { if $debug { $env.PROVISIONING_DEBUG = "true" } let effective_check = ($check or $dry_run) # Args come in as: ["component", "ls"] or ["ls", "postgresql"] depending on dispatch let rest = if (($args | length) > 0) and (($args | first) in ["component", "comp", "c"]) { $args | skip 1 } else { $args } let sub = ($rest | get 0? | default "") let name = ($rest | get 1? | default "") # Workspace resolution: explicit flag > active env > empty (ext_only view) let ws = if ($workspace | is-not-empty) { $workspace } else { $env.PROVISIONING_KLOUD? | default "" } let help_text = { print "Component Management" print "====================" print "" print "Usage: prvng component [options]" print "" print "Read operations:" print " list [--workspace ] [--live] Provisioning state per component (alias: ls, l)" print " ll [--workspace ] Same as list --live (alias for --live)" print " lc [--workspace ] Same as list --mode cluster (alias: list cluster)" print " lcl [--workspace ] Same as list --mode cluster --live (alias: lc + live)" print " [--server ] Filter taskserv rows to one server" print " [] Positional server filter (same as --server)" print " [--mode cluster|taskserv] Filter by mode" print " [--filter ] Substring match on name (e.g. --filter prom)" print " [--filter col=val] Per-column match: state=failed, ns=monitoring, srv=wrk-0" print " [--json] Raw JSON output (flat table)" print " show [--workspace ] [--live] Full definition + state history (alias: sh, info)" print " status [--workspace ] [--live] Operational snapshot + state (alias: s, st)" print "" print "State column values:" print " pending Task not yet started (or server never initialized)" print " running Task in progress — or stuck if ended_at is empty" print " completed Task finished successfully via orchestrator" print " failed Task finished with error" print " — Component not in any DAG formula and no state entry yet" print "" print "Write operations (require orchestrator):" print " install [--workspace ] [--server ] [--check] [--wait]" print " delete [--workspace ] [--server ] [--check] [--wait]" print " update [--workspace ] [--server ] [--check] [--wait]" print " reinstall [--workspace ] [--server ] [--check] [--wait]" print " restart [--workspace ] [--server ] [--wait]" print " backup [--workspace ] [--server ] [--wait]" print " restore [--workspace ] [--server ] [--check] [--wait]" print " check-updates [--workspace ] [--server ]" print "" print "State correction:" print " mark-complete [--server ] [--note ]" print " Force state → completed with actor attribution. Use when:" print " • Component deployed manually or by an agent without orchestrator callback" print " • State stuck in 'running (ended_at empty) after successful deploy" print " • Task completed via human intervention and needs verification record" print " Alias: mc" print "" print " mark-complete-live [--workspace ]" print " Bulk mark-complete for all components where state=running AND live=healthy." print " Fetches live k8s data (one SSH session) before deciding — only marks what" print " is confirmed running by the live check strategy." print " Alias: mcl" print "" print " mark-complete-all [--workspace ]" print " Bulk mark-complete for ALL components where state=running, no live check." print " Use for components without live_check (os, vol_prepare, kubernetes, etc.)" print " or when k8s is unreachable and state is known to be correct." print " Alias: mca" print "" print " bulk-mark-complete [--include-failed] [--dry-run] [--note ]" print " Single-write sweep: marks all state=running (ended_at empty) as completed." print " --include-failed: also sweep state=failed entries (e.g. cilium/longhorn that" print " failed on first attempt but are actually running — verify pods first)." print " Alias: bmc" print "" print " state-check [--server ]" print " Diagnose state incoherence: lists stale-running, failed-but-recovered," print " and duplicate kebab/snake keys. Use this first to understand what to fix." print " Alias: sc" print "" print " drop-key --server [--dry-run]" print " Remove a specific state key from a server's taskservs. Use to prune" print " orphaned duplicate entries left by naming migrations (e.g. odoo-jesusperez" print " coexisting with odoo_jesusperez)." print " Alias: dk" print "" print " state-normalize [--dry-run]" print " One-shot migration: merge all snake_case state keys into their kebab" print " equivalents. Eliminates duplicate entries from previous naming inconsistency." print " Run once, then verify with sc. Future writes always use kebab (fixed upstream)." print " Alias: sn" print "" print "Flags:" print " --live Fetch live pod status from k8s via SSH (k0s kubectl get pods)." print " Shows actual pod counts — not a proxy for provisioning state." print " --check / --dry-run Dry-run: validates bundle without submitting to orchestrator." print " --wait Block until orchestrator task reaches terminal state." print "" print "Examples:" print " prvng c l # provisioning state" print " prvng c ll # + live pod status" print " prvng c lc # cluster components only" print " prvng c lcl # cluster components + live" print " prvng c l libre-wuji-wrk-0 # taskserv for one server" print " prvng c l --live --workspace libre-wuji" print " prvng c mc postgresql --note 'verified: kubectl get sts -n data'" print " prvng c install postgresql --wait" print " prvng c sh postgresql --live" } match $sub { "" => { do $help_text } "list" | "ls" | "l" | "ll" | "lc" | "lcl" => { let is_live = $live or ($sub == "ll") or ($sub == "lcl") let effective_mode = if $sub in ["lc", "lcl"] { "cluster" } else if ($mode | is-not-empty) { $mode } else { "" } # Second positional arg is treated as a server filter for list ops. let effective_server = if ($server | is-not-empty) { $server } else { $name } let all_rows = with-env { PROVISIONING_NO_CACHE: "true" } { if ($ws | is-not-empty) { main component list --workspace $ws --mode $effective_mode --server $effective_server } else { main component list --mode $effective_mode --server $effective_server } } let filtered_rows = if ($filter | is-not-empty) { if ($filter | str contains "=") { let parts = ($filter | split row "=" | take 2) let col = ($parts | get 0) let val = ($parts | get 1 | str downcase) let col_map = { ns: "namespace", state: "state", deploy: "state", name: "name", srv: "server", server: "server" } let real_col = ($col_map | get -o $col | default $col) $all_rows | where { ($in | get -o $real_col | default "" | into string | str downcase) | str contains $val } } else { let term = ($filter | str downcase) $all_rows | where { $in.name | str downcase | str contains $term } } } else { $all_rows } let cluster_rows = ($filtered_rows | where mode == "cluster") let taskserv_rows = ($filtered_rows | where mode == "taskserv") # Single SSH session for all live checks (cluster + taskserv). let ws_root_l = if ($ws | is-not-empty) { comp-ws-path $ws } else { comp-ws-path "" } let infra_l = (comp-ws-infra $ws_root_l) let state_root_l = if (($ws_root_l | path join "infra" $infra_l ".provisioning-state.ncl") | path exists) { $ws_root_l | path join "infra" $infra_l } else { $ws_root_l } let ssh_srv = if $is_live { (comp-kubectl-access $ws_root_l $infra_l).host } else { "" } let k8s_data = if $is_live { let systemd_svcs = ($filtered_rows | where { ($in | get -o live_check | default {} | get -o strategy | default "") == "systemd" } | each { $in | get -o live_check | default {} | get -o service | default "" } | where { $in | is-not-empty }) comp-ssh-k8s-data $ws_root_l $infra_l $ssh_srv --systemd-services $systemd_svcs } else { {} } # A taskserv's systemd service runs on the node it was deployed to — not the # CP. The single CP gather above only sees cp-0's units, so a worker-only # service (e.g. coredns-vpn-wrk1 on wrk-1) always shows inactive. Gather # systemd per-server for taskserv rows whose server is not the CP, so each is # checked on its own node. (live_check.scope is advisory; the deploy server is # authoritative for where a taskserv's unit actually runs.) let extra_systemd = if $is_live { $taskserv_rows | where { (($in | get -o live_check | default {} | get -o strategy | default "") == "systemd") and (($in.server? | default "") != $ssh_srv) and (($in.server? | default "") | is-not-empty) } | group-by server | items {|srv, rows| let svcs = ($rows | each { $in | get -o live_check | default {} | get -o service | default "" } | where { $in | is-not-empty } | uniq) { server: $srv, systemd: ((comp-ssh-k8s-data $ws_root_l $infra_l $srv --systemd-services $svcs) | get -o systemd | default {}) } } | reduce --fold {} {|it, acc| $acc | insert $it.server $it.systemd } } else { {} } # If live data could not be fetched (SSH/kubectl unreachable), every row # would misleadingly show "no pods". Warn once that it's a fetch failure, # not pod absence. if $is_live and ($k8s_data.pods | is-empty) and ($k8s_data.nodes | is-empty) { print $"(_ansi yellow)⚠ live cluster data unavailable \(CP: ($ssh_srv | default 'unresolved'))\) — 'live' column reflects the failed fetch, not pod absence(_ansi reset)" } # Server info: cache by default, hcloud query when --live. let srv_info = if ($taskserv_rows | is-not-empty) or ($cluster_rows | is-not-empty) { if $is_live { comp-load-server-info $ws_root_l $infra_l --live } else { comp-load-server-info $ws_root_l $infra_l } } else { {} } if $json { let components_out = ($filtered_rows | reject pod_selector live_check) let servers_out = ($srv_info | items {|k v| {key: $k, value: $v}} | reduce --fold {} {|it acc| $acc | insert $it.key $it.value}) {components: $components_out, servers: $servers_out} | to json | print return } # ── Cluster section ────────────────────────────────────────── if ($cluster_rows | is-not-empty) { let _op_ws = (comp-ncl-export $ws_root_l $"infra/($infra_l)/settings.ncl" | get -o settings | default {} | get -o operator_workspace | default "") let _cluster_header = if ($_op_ws | is-not-empty) { $"(_ansi white_bold)CLUSTER COMPONENTS(_ansi reset) (_ansi dark_gray)→ hosted on(_ansi reset) (_ansi cyan)($_op_ws)(_ansi reset)" } else { $"(_ansi white_bold)CLUSTER COMPONENTS(_ansi reset)" } print $_cluster_header print $"(_ansi dark_gray)────────────────────────────────────────────────────(_ansi reset)" let cl_version = (comp-settings-k0s-version $ws_root_l $infra_l) let cl_summary = if $is_live { comp-cluster-summary $ws_root_l $infra_l $k8s_data $cl_version --live } else { comp-cluster-summary $ws_root_l $infra_l {} $cl_version } let cl_badge = (comp-cluster-badge $cl_summary) if ($cl_badge | is-not-empty) { print $" (_ansi dark_gray)($cl_badge)(_ansi reset)" } if $is_live { $cluster_rows | each {|row| let check = (comp-live-check $row.name $row $k8s_data) let live_val = if $check.live == "—" { $"(_ansi dark_gray)($check.detail)(_ansi reset)" } else { $check.detail } $row | select name namespace version class resources placement state | update state {|r| state-color $r.state} | rename --column {state: deploy} | insert live $live_val } | table | print } else { $cluster_rows | select name namespace version class resources placement state | update state {|r| state-color $r.state} | rename --column {state: deploy} | table | print } print "" } # ── Taskserv section ───────────────────────────────────────── if ($taskserv_rows | is-not-empty) { print $"(_ansi white_bold)TASKSERV COMPONENTS(_ansi reset)" let servers = ($taskserv_rows | get server | uniq | sort) for srv in $servers { print "" let info = ($srv_info | get -o $srv | default {}) let badge = (comp-server-badge $info) if ($badge | is-not-empty) { print $" (_ansi white_bold)($srv)(_ansi reset) (_ansi dark_gray)($badge)(_ansi reset)" } else { print $" (_ansi white_bold)($srv)(_ansi reset)" } print $" (_ansi dark_gray)──────────────────────────────────────────────(_ansi reset)" let srv_rows = ($taskserv_rows | where server == $srv | sort-by name) if $is_live { # Use this server's own systemd state (gathered per-server above) # so a taskserv unit is checked on the node it runs on, not the CP. let srv_k8s = ($k8s_data | upsert systemd (if ($srv == $ssh_srv) { ($k8s_data.systemd? | default {}) } else { ($extra_systemd | get -o $srv | default {}) })) $srv_rows | each {|row| let check = (comp-live-check $row.name $row $srv_k8s) let live_val = if $check.live == "—" { $"(_ansi dark_gray)($check.detail)(_ansi reset)" } else { $check.detail } { name: $row.name, deploy: (state-color $row.state), live: $live_val } } | table | str trim | lines | each {|ln| print $" ($ln)"} } else { $srv_rows | select name state | update state {|r| state-color $r.state} | rename --column {state: deploy} | table | str trim | lines | each {|ln| print $" ($ln)"} } } print "" } if not $is_live { let ts = (comp-cache-last-sync $ws_root_l $infra_l) if ($ts | is-not-empty) { print $"(_ansi dark_gray)from cache updated ($ts)(_ansi reset)" } } } "status" | "st" | "s" => { if ($name | is-empty) { print "Error: component status requires a name" print "Usage: prvng component status [--workspace ]" return } let ws_root_s = if ($ws | is-not-empty) { comp-ws-path $ws } else { comp-ws-path "" } let infra_s = (comp-ws-infra $ws_root_s) let info = (comp-load-component-record $name $ws_root_s $infra_s) let dag = ($info.dag? | default []) let overall = if ($dag | is-empty) { "— (no DAG)" } else if ($dag | all { $in.state == "completed" }) { "✅ deployed" } else if ($dag | any { $in.state == "running" }) { "🔄 running" } else if ($dag | any { $in.state == "failed" }) { "❌ failed" } else if ($dag | any { $in.state == "blocked" }) { "⊘ blocked" } else if ($dag | any { $in.state == "unknown" }) { "? unknown (reset/reprovisioned)" } else { "⏳ pending" } print $"component: ($name)" print $"state: ($overall)" print $"mode: ($info.mode)" print $"target: (if ($info.target | is-not-empty) { $info.target } else { '—' })" print $"namespace: (if ($info.namespace | is-not-empty) { $info.namespace } else { '—' })" print $"version: (if ($info.version | is-not-empty) { $info.version } else { '—' })" let req_count = ($info.requires | length) let ops_count = ($info.operations | length) if $req_count > 0 { print $"requires: ($info.requires | str join ', ')" } if $ops_count > 0 { print $"operations: ($ops_count) defined" } if $live { print "" let dag_server = ($dag | get 0? | default null | get -o server | default "") let k8s_data = (comp-ssh-k8s-data $ws_root_s $infra_s $dag_server) let check = (comp-live-check $name $info $k8s_data) let live_icon = match $check.live { "healthy" => "✅", "degraded" => "❌", "starting" => "🔄", "partial" => "⚠", "not deployed" => "⊘", _ => "—", } let live_label = if $check.live == "—" { $"($live_icon) ($check.detail)" } else { $"($live_icon) ($check.live) ($check.detail)" } print $"live check: ($live_label)" } if ($dag | is-not-empty) { print "" print "DAG execution:" for entry in $dag { let state_icon = match $entry.state { "completed" => "✅", "running" => "🔄", "failed" => "❌", "blocked" => "⊘", "unknown" => "?", _ => "⏳", } let ts = if ($entry.ended_at | is-not-empty) { $entry.ended_at | str replace "T" " " | str replace "Z" "" } else if ($entry.started_at | is-not-empty) { $"started ($entry.started_at | str replace 'T' ' ' | str replace 'Z' '')" } else { "" } let blocker_suffix = if ($entry.blocker | is-not-empty) { $" ← blocked by ($entry.blocker)" } else { "" } print $" ($state_icon) ($entry.formula | fill -a l -w 24) server: ($entry.server) ($entry.state | fill -a l -w 10) ($ts)($blocker_suffix)" } } } "show" | "info" | "sh" => { if ($name | is-empty) { print "Error: component show requires a name" print "Usage: prvng component show [--workspace ]" return } if ($ws | is-not-empty) { main component info $name --workspace $ws } else { main component info $name } } "install" => { if ($name | is-empty) { print "Error: component install requires a name"; return } let task_ids = if ($ws | is-not-empty) { main component install $name --workspace $ws --server $server --check=$effective_check --wait=$wait } else { main component install $name --server $server --check=$effective_check --wait=$wait } $task_ids | each {|id| print $"task: ($id)"} } "delete" => { if ($name | is-empty) { print "Error: component delete requires a name"; return } let task_ids = if ($ws | is-not-empty) { main component delete $name --workspace $ws --server $server --check=$effective_check --wait=$wait } else { main component delete $name --server $server --check=$effective_check --wait=$wait } $task_ids | each {|id| print $"task: ($id)"} } "update" => { if ($name | is-empty) { print "Error: component update requires a name"; return } let task_ids = if ($ws | is-not-empty) { main component update $name --workspace $ws --server $server --check=$effective_check --wait=$wait } else { main component update $name --server $server --check=$effective_check --wait=$wait } $task_ids | each {|id| print $"task: ($id)"} } "reinstall" => { if ($name | is-empty) { print "Error: component reinstall requires a name"; return } let task_ids = if ($ws | is-not-empty) { main component reinstall $name --workspace $ws --server $server --check=$effective_check --wait=$wait } else { main component reinstall $name --server $server --check=$effective_check --wait=$wait } $task_ids | each {|id| print $"task: ($id)"} } "restart" => { if ($name | is-empty) { print "Error: component restart requires a name"; return } let task_ids = if ($ws | is-not-empty) { main component restart $name --workspace $ws --server $server --wait=$wait } else { main component restart $name --server $server --wait=$wait } $task_ids | each {|id| print $"task: ($id)"} } "backup" => { if ($name | is-empty) { print "Error: component backup requires a name"; return } let task_ids = if ($ws | is-not-empty) { main component backup $name --workspace $ws --server $server --wait=$wait } else { main component backup $name --server $server --wait=$wait } $task_ids | each {|id| print $"task: ($id)"} } "restore" => { if ($name | is-empty) { print "Error: component restore requires a name"; return } let task_ids = if ($ws | is-not-empty) { main component restore $name --workspace $ws --server $server --check=$effective_check --wait=$wait } else { main component restore $name --server $server --check=$effective_check --wait=$wait } $task_ids | each {|id| print $"task: ($id)"} } "check-updates" => { if ($name | is-empty) { print "Error: component check-updates requires a name"; return } let task_ids = if ($ws | is-not-empty) { main component check-updates $name --workspace $ws --server $server } else { main component check-updates $name --server $server } $task_ids | each {|id| print $"task: ($id)"} } "mark-complete" | "mc" => { if ($name | is-empty) { print "Error: component mark-complete requires a name"; return } if ($ws | is-not-empty) { main component mark-complete $name --workspace $ws --server $server } else { main component mark-complete $name --server $server } } "mark-complete-live" | "mcl" => { let ws_root = if ($ws | is-not-empty) { comp-ws-path $ws } else { comp-ws-path "" } let infra = (comp-ws-infra $ws_root) let state_root = if (($ws_root | path join "infra" $infra ".provisioning-state.ncl") | path exists) { $ws_root | path join "infra" $infra } else { $ws_root } let rows = with-env { PROVISIONING_NO_CACHE: "true" } { if ($ws | is-not-empty) { main component list --workspace $ws --mode $mode } else { main component list --mode $mode } } let ssh_server = (comp-state-server $state_root) let k8s_data = (comp-ssh-k8s-data $ws_root $infra $ssh_server) let candidates = ( $rows | where { $in.state in ["running", "failed"] } | each {|row| let check = (comp-live-check $row.name $row $k8s_data) if $check.live == "healthy" { [{ name: $row.name, server: ($row.server | default ""), prior_state: $row.state }] } else { [] } } | flatten ) if ($candidates | is-empty) { print "No components with state=running/failed and live=healthy found." return } print $"Marking ($candidates | length) components complete:" for c in $candidates { print $" → ($c.name) [($c.prior_state) → completed]" let srv = if ($c.server | is-not-empty) { $c.server } else { $server } try { if ($ws | is-not-empty) { main component mark-complete $c.name --workspace $ws --server $srv } else { main component mark-complete $c.name --server $srv } } catch {|e| print $" (_ansi yellow)skipped: ($e.msg)(_ansi reset)" } } } "mark-complete-all" | "mca" => { let ws_root = if ($ws | is-not-empty) { comp-ws-path $ws } else { comp-ws-path "" } let infra = (comp-ws-infra $ws_root) let state_root = if (($ws_root | path join "infra" $infra ".provisioning-state.ncl") | path exists) { $ws_root | path join "infra" $infra } else { $ws_root } let rows = with-env { PROVISIONING_NO_CACHE: "true" } { if ($ws | is-not-empty) { main component list --workspace $ws --mode $mode } else { main component list --mode $mode } } let ssh_server = (comp-state-server $state_root) let candidates = ( $rows | where { $in.state == "running" } | each {|row| [{ name: $row.name, server: ($row.server | default "") }] } | flatten ) if ($candidates | is-empty) { print "No components with state=running found." return } print $"Marking ($candidates | length) components complete \(no live check\):" for c in $candidates { print $" → ($c.name)" let srv = if ($c.server | is-not-empty) { $c.server } else { $ssh_server } try { if ($ws | is-not-empty) { main component mark-complete $c.name --workspace $ws --server $srv } else { main component mark-complete $c.name --server $srv } } catch {|e| print $" (_ansi yellow)skipped: ($e.msg)(_ansi reset)" } } } "bulk-mark-complete" | "bmc" => { let include_failed = ($rest | any { $in == "--include-failed" }) let is_dry = ($dry_run or $check or ($rest | any { $in == "--dry-run" })) if $is_dry { if ($ws | is-not-empty) { main component bulk-mark-complete --workspace $ws --infra $infra --dry-run } else { main component bulk-mark-complete --infra $infra --dry-run } } else if $include_failed { if ($ws | is-not-empty) { main component bulk-mark-complete --workspace $ws --infra $infra --include-failed } else { main component bulk-mark-complete --infra $infra --include-failed } } else { if ($ws | is-not-empty) { main component bulk-mark-complete --workspace $ws --infra $infra } else { main component bulk-mark-complete --infra $infra } } } "state-check" | "sc" => { if ($ws | is-not-empty) { main component state-check --workspace $ws --infra $infra --server $server } else { main component state-check --infra $infra --server $server } } "state-normalize" | "sn" => { let is_dry = ($dry_run or $check) if $is_dry { if ($ws | is-not-empty) { main component state-normalize --workspace $ws --infra $infra --dry-run } else { main component state-normalize --infra $infra --dry-run } } else { if ($ws | is-not-empty) { main component state-normalize --workspace $ws --infra $infra } else { main component state-normalize --infra $infra } } } "drop-server" | "ds" => { if ($name | is-empty) { print "Error: drop-server requires a hostname"; return } let is_dry = ($dry_run or $check) if $is_dry { if ($ws | is-not-empty) { main component drop-server $name --workspace $ws --infra $infra --dry-run } else { main component drop-server $name --infra $infra --dry-run } } else { if ($ws | is-not-empty) { main component drop-server $name --workspace $ws --infra $infra } else { main component drop-server $name --infra $infra } } } "drop-key" | "dk" => { if ($name | is-empty) { print "Error: drop-key requires a key name"; return } if ($server | is-empty) { print "Error: drop-key requires --server "; return } let is_dry = ($dry_run or $check) if $is_dry { if ($ws | is-not-empty) { main component drop-key $name --server $server --workspace $ws --infra $infra --dry-run } else { main component drop-key $name --server $server --infra $infra --dry-run } } else { if ($ws | is-not-empty) { main component drop-key $name --server $server --workspace $ws --infra $infra } else { main component drop-key $name --server $server --infra $infra } } } "render" => { if ($name | is-empty) { print "Error: component render requires a name"; return } if $bundle { main component render $name --op $op --workspace $ws --infra $infra --out $out --bundle } else { main component render $name --op $op --workspace $ws --infra $infra --out $out } } "help" | "h" | "-h" | "--help" => { do $help_text } _ => { print $"Unknown component subcommand: ($sub)" print "Run: prvng component help" exit 1 } } }