#!/usr/bin/env nu # rollback.nu — teardown for a failed bootstrap. Order: daoshi → wuji → ops-vm. # Radicle repos are NOT deleted (they are the governance ledger and must survive). def main [ --workspace: string = "" --hetzner-context: string = "" --do-context: string = "" --dry-run ]: nothing -> nothing { let ws = if ($workspace | is-not-empty) { $workspace } else { $env.PLAYBOOK_PARAM_WORKSPACE? | default "libre-wuji" } let htz_ctx = if ($hetzner_context | is-not-empty) { $hetzner_context } else { $env.PLAYBOOK_PARAM_HETZNER_CONTEXT? | default "" } let do_ctx = if ($do_context | is-not-empty) { $do_context } else { $env.PLAYBOOK_PARAM_DO_CONTEXT? | default "" } print "bootstrap_initial ROLLBACK — tearing down provisioned infrastructure" print "NOTE: Radicle repos are preserved. Re-run bootstrap after fixing the root cause." print "" if $dry_run { print "[dry-run] would destroy: libre-daoshi cluster, libre-wuji cluster, ops-vm" return } for info in [ { workspace: "libre-daoshi", ctx_env: "HCLOUD_CONTEXT", ctx_val: $htz_ctx }, { workspace: $ws, ctx_env: "HCLOUD_CONTEXT", ctx_val: $htz_ctx }, { workspace: "ops-vm", ctx_env: "DIGITALOCEAN_CONTEXT", ctx_val: $do_ctx }, ] { if ($info.ctx_val | is-empty) { print $" SKIP: ($info.workspace) — no context provided" } else { print $" destroying ($info.workspace)..." let destroy_r = do { with-env { ($info.ctx_env): $info.ctx_val } { ^prvng cluster destroy --workspace $info.workspace --confirm } } | complete if $destroy_r.exit_code != 0 { print $" WARNING: destroy failed for ($info.workspace): ($destroy_r.stderr | str trim)" } else { print $" destroyed: ($info.workspace)" } } } print "Rollback complete. Fix root cause and re-run bootstrap_initial." }