provisioning-platform/scripts/start-provisioning-daemon.nu

246 lines
8.7 KiB
Text
Raw Normal View History

#!/usr/bin/env nu
# provisioning-daemon lifecycle — start, stop, status, logs, restart.
# Targets the new HTTP+NATS daemon (provisioning-daemon binary, port 9014).
const DAEMON_BINARY = "provisioning-daemon"
const DAEMON_BIND = "0.0.0.0:9014"
const HEALTH_URL = "http://127.0.0.1:9014/health"
const PID_FILE = "/tmp/provisioning-daemon.pid"
const LOG_FILE = "/tmp/provisioning-daemon.log"
const STARTUP_WAIT_S = 5 # seconds to poll before giving up
def read-pid []: nothing -> string {
if ($PID_FILE | path exists) { open $PID_FILE | str trim } else { "" }
}
def pid-alive [pid: string]: nothing -> bool {
if ($pid | is-empty) { return false }
let r = (do { ^kill -0 ($pid | into int) } | complete)
$r.exit_code == 0
}
def write-pid [pid: string]: nothing -> nothing {
$pid | save --force $PID_FILE
}
def remove-pid []: nothing -> nothing {
if ($PID_FILE | path exists) { rm $PID_FILE }
}
def wait-healthy []: nothing -> bool {
let attempts = (seq 1 ($STARTUP_WAIT_S * 2))
let ok = ($attempts | any {|_|
let r = (do { ^curl -sf $HEALTH_URL } | complete)
if $r.exit_code == 0 { true } else { sleep 500ms; false }
})
$ok
}
def start-cmd []: nothing -> nothing {
let pid = (read-pid)
if (pid-alive $pid) {
print $"provisioning-daemon already running \(PID ($pid)\)"
return
}
let log_dir = ($LOG_FILE | path dirname)
if not ($log_dir | path exists) { ^mkdir -p $log_dir }
# Resolve binary — check release build first, then PATH
let repo_root = (do { ^git rev-parse --show-toplevel } | complete)
let release = (
if $repo_root.exit_code == 0 {
$"($repo_root.stdout | str trim)/provisioning/platform/target/release/($DAEMON_BINARY)"
} else { "" }
)
let bin_path = (if (not ($release | is-empty)) and ($release | path exists) { $release } else { $DAEMON_BINARY })
# Resolve the repo root for NICKEL_IMPORT_PATH and config path.
let repo_provisioning = (
if $repo_root.exit_code == 0 {
$"($repo_root.stdout | str trim)/provisioning"
} else { "" }
)
let ontoref_path = "/Users/Akasha/Development/ontoref"
let nickel_ip = (
if not ($repo_provisioning | is-empty) {
if ($ontoref_path | path exists) {
$"($repo_provisioning):($ontoref_path)"
} else {
$repo_provisioning
}
} else { "" }
)
let config_file = $"($env.HOME)/Library/Application Support/provisioning/platform/config/provisioning-daemon.ncl"
let config_arg = (if ($config_file | path exists) { $"--config '($config_file)'" } else { "" })
let env_prefix = (if not ($nickel_ip | is-empty) { $"NICKEL_IMPORT_PATH='($nickel_ip)'" } else { "" })
let daemon_pid = (do {
^bash -c $"($env_prefix) nohup ($bin_path) ($config_arg) >> ($LOG_FILE) 2>&1 & echo $!"
} | complete)
if $daemon_pid.exit_code != 0 {
print $"Failed to start daemon: ($daemon_pid.stderr)"
return
}
let pid_str = ($daemon_pid.stdout | str trim)
write-pid $pid_str
if (wait-healthy) {
print $"provisioning-daemon started \(PID ($pid_str)\) → ($HEALTH_URL)"
} else {
print $"provisioning-daemon did not become healthy in ($STARTUP_WAIT_S)s — check ($LOG_FILE)"
}
}
def stop-cmd []: nothing -> nothing {
let pid = (read-pid)
if not (pid-alive $pid) {
print "provisioning-daemon is not running"
return
}
let r = (do { ^kill ($pid | into int) } | complete)
if $r.exit_code != 0 {
print $"Failed to send SIGTERM: ($r.stderr)"
return
}
sleep 500ms
if (pid-alive $pid) {
let r2 = (do { ^kill -9 ($pid | into int) } | complete)
if $r2.exit_code != 0 { print "Force-kill failed"; return }
}
remove-pid
print "provisioning-daemon stopped"
}
def status-cmd []: nothing -> nothing {
let pid = (read-pid)
if not (pid-alive $pid) {
print "provisioning-daemon is not running"
return
}
let r = (do { ^curl -sf $HEALTH_URL } | complete)
if $r.exit_code == 0 {
let h = ($r.stdout | from json)
print $"provisioning-daemon running \(PID ($pid)\)"
print $" status: ($h.status)"
print $" tools: ($h.tools)"
print $" calls: ($h.invocations)"
} else {
print $"provisioning-daemon running \(PID ($pid)\) but not responding at ($HEALTH_URL)"
}
}
def logs-cmd []: nothing -> nothing {
if not ($LOG_FILE | path exists) {
print $"Log file not found: ($LOG_FILE)"
return
}
^tail -n 100 $LOG_FILE
}
def restart-cmd []: nothing -> nothing {
stop-cmd
sleep 1sec
start-cmd
}
# ── External services: jj + Radicle ──────────────────────────────────────────
def jj-init-workspace [ws_root: string]: nothing -> nothing {
let jj_dir = ($ws_root | path join ".jj")
if ($jj_dir | path exists) {
print $" [✓] jj already initialized at ($ws_root)"
return
}
let r = (do { ^jj git init --colocate $ws_root } | complete)
if $r.exit_code == 0 {
print $" [+] jj initialized at ($ws_root)"
} else {
print $" [!] jj init failed: ($r.stderr | str trim)"
}
}
def rad-ensure-running [passphrase: string = ""]: nothing -> nothing {
let status_r = (do { ^rad node status } | complete)
let is_running = ($status_r.exit_code == 0 and not ($status_r.stdout | str contains "stopped"))
if $is_running {
print " [✓] Radicle node already running"
return
}
let env_extra = if ($passphrase | is-not-empty) {
{ RAD_PASSPHRASE: $passphrase }
} else { {} }
let start_r = (do { with-env $env_extra { ^rad node start } } | complete)
if $start_r.exit_code == 0 {
print " [+] Radicle node started"
} else {
let err = $start_r.stderr | str trim
if ($err | str contains "Passphrase") or ($err | str contains "passphrase") {
print " [!] Radicle keystore locked — set RAD_PASSPHRASE env var before starting services"
} else {
print $" [!] rad node start failed: ($err)"
}
}
}
def services-start-cmd [ws_root: string, rad_passphrase: string]: nothing -> nothing {
print "── External services ─────────────────────────────────────────"
let target_ws = if ($ws_root | is-not-empty) { $ws_root } else { $env.PWD }
if (^which jj | complete).exit_code == 0 {
jj-init-workspace $target_ws
} else {
print " [skip] jj not found in PATH"
}
if (^which rad | complete).exit_code == 0 {
rad-ensure-running $rad_passphrase
} else {
print " [skip] rad not found in PATH"
}
}
def services-status-cmd []: nothing -> nothing {
print "── External services status ───────────────────────────────────"
let jj_r = (do { ^jj --version } | complete)
print (if $jj_r.exit_code == 0 { $" jj : ($jj_r.stdout | str trim)" } else { " jj : not found" })
let rad_r = (do { ^rad node status } | complete)
let rad_status = if $rad_r.exit_code == 0 {
if ($rad_r.stdout | str contains "stopped") { "stopped" } else { "running" }
} else { "not found" }
print $" rad : ($rad_status)"
}
def main [
command: string = "help"
--workspace (-w): string = ""
--rad-passphrase: string = ""
]: nothing -> nothing {
match $command {
"start" => { start-cmd }
"stop" => { stop-cmd }
"status" => { status-cmd }
"logs" => { logs-cmd }
"restart" => { restart-cmd }
"services" => { services-start-cmd $workspace $rad_passphrase }
"services-status" => { services-status-cmd }
_ => {
print "Usage: start-provisioning-daemon.nu <start|stop|status|logs|restart|services|services-status>"
print ""
print " start Start provisioning-daemon"
print " stop Stop provisioning-daemon"
print " status Health check"
print " logs Tail daemon log"
print " restart Stop + start"
print " services Init jj workspace + start Radicle node"
print " services-status Show jj + Radicle status"
print ""
print " --workspace (-w) <path> Workspace root for jj init (default: PWD)"
print " --rad-passphrase <passphrase> Radicle keystore passphrase"
}
}
}