#!/usr/bin/env nu # dr_daoshi_lost/run.nu — daoshi cluster DR: provision → forgejo restore → CI keys → Radicle reconnect → verify def main [ --workspace: string = "" --hetzner-context: string = "" --wuji-nats-url: string = "" --ops-vm-host: string = "" --skip-provision: string = "false" --dry-run ]: nothing -> nothing { let ws = if ($workspace | is-not-empty) { $workspace } else { $env.PLAYBOOK_PARAM_WORKSPACE? | default "libre-daoshi" } let hetzner_ctx = if ($hetzner_context | is-not-empty) { $hetzner_context } else { $env.PLAYBOOK_PARAM_HETZNER_CONTEXT? | default "" } let wuji_nats = if ($wuji_nats_url | is-not-empty) { $wuji_nats_url } else { $env.PLAYBOOK_PARAM_WUJI_NATS_URL? | default "" } let vm_host = if ($ops_vm_host | is-not-empty) { $ops_vm_host } else { $env.PLAYBOOK_PARAM_OPS_VM_HOST? | default "" } let skip_prov = if ($skip_provision | is-not-empty) { $skip_provision } else { $env.PLAYBOOK_PARAM_SKIP_PROVISION? | default "false" } if ($ws | is-empty) { error make { msg: "workspace is required" } } if ($vm_host | is-empty) { error make { msg: "ops-vm-host is required" } } let playbook_dir = $env.PLAYBOOK_DIR? | default ($env.FILE_PWD | path expand) let dry_flag = if $dry_run { "--dry-run" } else { "" } print $"[dr_daoshi_lost] workspace=($ws) dry-run=($dry_run)" print "WARNING: This is a disaster recovery playbook. In-flight builds at time of failure are lost." print "" print "[step 1/7] verifying wuji accessibility..." let r1 = do { ^nu ($playbook_dir | path join "steps/verify_wuji_accessible.nu") --wuji-nats-url $wuji_nats $dry_flag } | complete if $r1.exit_code != 0 { error make { msg: $"wuji accessibility check failed: ($r1.stderr | str trim)" } } print $r1.stdout print "[step 2/7] provisioning new cluster..." let r2 = do { ^nu ($playbook_dir | path join "steps/provision_new_cluster.nu") --workspace $ws --hetzner-context $hetzner_ctx --skip-provision $skip_prov $dry_flag } | complete if $r2.exit_code != 0 { error make { msg: $"Cluster provision failed: ($r2.stderr | str trim)" } } print $r2.stdout print "[step 3/7] restoring forgejo from Radicle mirrors..." let r3 = do { ^nu ($playbook_dir | path join "steps/restore_forgejo_from_radicle.nu") --workspace $ws $dry_flag } | complete if $r3.exit_code != 0 { print $" WARNING: forgejo restore reported errors: ($r3.stderr | str trim)" } else { print $r3.stdout } print "[step 4/7] reissuing daoshi-CI NATS JWT..." let r4 = do { ^nu ($playbook_dir | path join "steps/reissue_ci_keys.nu") --workspace $ws --ops-vm-host $vm_host $dry_flag } | complete if $r4.exit_code != 0 { error make { msg: $"CI key reissuance failed: ($r4.stderr | str trim)" } } print $r4.stdout print "[step 5/7] reconnecting Radicle seed..." let r5 = do { ^nu ($playbook_dir | path join "steps/reconnect_radicle_seed.nu") --workspace $ws $dry_flag } | complete if $r5.exit_code != 0 { print $" WARNING: Radicle reconnect reported errors: ($r5.stderr | str trim)" } else { print $r5.stdout } print "[step 6/7] verifying CI pipeline..." let r6 = do { ^nu ($playbook_dir | path join "steps/verify_ci_pipeline.nu") --workspace $ws --wuji-nats-url $wuji_nats $dry_flag } | complete if $r6.exit_code != 0 { print $" WARNING: CI pipeline verification failed: ($r6.stderr | str trim)" } else { print $r6.stdout } print "[step 7/7] emitting dr-daoshi-recovered audit event..." let r7 = do { ^nu ($playbook_dir | path join "steps/emit_audit.nu") --workspace $ws --event-type "dr_daoshi_recovered" $dry_flag } | complete if $r7.exit_code != 0 { print $" WARNING: audit emit failed: ($r7.stderr | str trim)" } print "" print "[dr_daoshi_lost] recovery complete" print "Verify: kubectl get pods -n libre-daoshi; nats auth info --account libre-daoshi; woodpecker trigger test build" }