#!/usr/bin/env nu # lian_build catalog CLI — ephemeral build runner management via hcloud. # Entry: prvng runners [name] export-env { let lib_dirs_raw = ($env.NU_LIB_DIRS? | default "") let current_lib_dirs = if ($lib_dirs_raw | describe) == "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 cli/flags.nu [parse_common_flags] def hcloud-servers []: nothing -> list { let r = (do { ^hcloud server list --output json } | complete) if $r.exit_code != 0 { error make { msg: $"hcloud server list failed: ($r.stderr | str trim)" } } $r.stdout | from json } def format-server [s: record]: nothing -> record { let priv = ($s.private_net | get 0? | default {} | get ip? | default "—") { name: $s.name status: $s.status public_ip: $s.public_net.ipv4.ip private_ip: $priv type: $s.server_type.name location: $s.datacenter.location.name age: $s.created } } def runners-list []: nothing -> nothing { let servers = (hcloud-servers) let runners = ($servers | where { |s| ($s.name | str starts-with "runner-") or ($s.name | str starts-with "buildkit-runner-") }) if ($runners | is-empty) { print "no active runners" return } $runners | each { |s| format-server $s } | table } def runners-status [name: string]: nothing -> nothing { if ($name | is-empty) { error make { msg: "runners status requires a server name" } } let r = (do { ^hcloud server describe $name --output json } | complete) if $r.exit_code != 0 { error make { msg: $"hcloud server describe failed: ($r.stderr | str trim)" } } $r.stdout | from json | table } def runners-kill [name: string, confirmed: bool]: nothing -> nothing { if ($name | is-empty) { error make { msg: "runners kill requires a server name" } } if not $confirmed { let answer = (input $"Delete runner '($name)'? [y/N]: " | str downcase | str trim) if $answer != "y" { print "aborted"; return } } let r = (do { ^hcloud server delete $name } | complete) if $r.exit_code != 0 { error make { msg: $"hcloud server delete failed: ($r.stderr | str trim)" } } print $"deleted ($name)" } def runners-gc [confirmed: bool]: nothing -> nothing { let servers = (hcloud-servers) let orphans = ($servers | where { |s| (($s.name | str starts-with "runner-") or ($s.name | str starts-with "buildkit-runner-")) and $s.status != "running" }) if ($orphans | is-empty) { print "no stopped/errored runners to collect" return } print $"Found ($orphans | length) orphaned runner(s):" $orphans | each { |s| format-server $s } | table if not $confirmed { let answer = (input "Delete all? [y/N]: " | str downcase | str trim) if $answer != "y" { print "aborted"; return } } $orphans | each { |s| let r = (do { ^hcloud server delete $s.name } | complete) if $r.exit_code == 0 { print $"deleted ($s.name)" } else { print $"error deleting ($s.name): ($r.stderr | str trim)" } } } def runners-help []: nothing -> nothing { print "Usage: prvng runners [name]" print "" print "Commands:" print " list List all active build runners" print " status Show runner details" print " kill Delete a runner" print " gc Delete all stopped/errored runners" } def main [ command: string = "" ...rest: string --yes (-y) --debug (-x) --notitles ]: nothing -> nothing { if $debug { $env.PROVISIONING_DEBUG = true } let name = ($rest | get 0? | default "") let flags = (parse_common_flags { yes: $yes, debug: $debug, notitles: $notitles }) let confirmed = ($flags.auto_confirm? | default false) match $command { "list" | "l" | "" => { runners-list } "status" | "s" => { runners-status $name } "kill" | "k" | "d" => { runners-kill $name $confirmed } "gc" => { runners-gc $confirmed } _ => { runners-help } } }