246 lines
8.7 KiB
Text
246 lines
8.7 KiB
Text
|
|
#!/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"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|