From ccfa6e6cb4bf73b5b4ea2e67c0e8b7971610647c Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Tue, 6 Oct 2026 23:28:55 +0000 Subject: [PATCH] Fleet scripts find their helpers inside their own tree (the CI checks, the Discord hook, the publish script), env-overridable; the library's node patterns inline Co-Authored-By: Claude Fable 5.1 --- tools/fleet/deploy-gate.sh | 2 +- tools/fleet/disk-sweep.py | 2 +- tools/fleet/lib/box.py | 4 ++-- tools/fleet/lib/standing.py | 2 +- tools/fleet/page.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/fleet/deploy-gate.sh b/tools/fleet/deploy-gate.sh index 4bebe1298..7e7c03417 100755 --- a/tools/fleet/deploy-gate.sh +++ b/tools/fleet/deploy-gate.sh @@ -2,7 +2,7 @@ # The fleet's deploy gate (coordinator, 6 October 2026 23:0xZ, after three comment-on-a-line faults): every script pushed to a box # passes master's defaults-line check and a syntax check first; lib/box.py's put() calls this for any .sh/.py source under # tools/fleet and refuses the push on red. Usage: deploy-gate.sh (exit 0 = clean) -set -u; rc=0; HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; MASTER_CI="${IGNEUM_MASTER_CI:-/Users/joshm/Projects/igneum/tools/ci}" +set -u; rc=0; HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; MASTER_CI="${IGNEUM_MASTER_CI:-$HERE/../ci}" # the same tree's CI checks (master's once merged) for f in "$@"; do case "$f" in *.sh) bash -n "$f" 2>&1 | head -2 | sed "s|^|$f: |" ; [ "${PIPESTATUS[0]}" = 0 ] || rc=1 ;; diff --git a/tools/fleet/disk-sweep.py b/tools/fleet/disk-sweep.py index 33b95be2f..5e9840a1a 100644 --- a/tools/fleet/disk-sweep.py +++ b/tools/fleet/disk-sweep.py @@ -8,7 +8,7 @@ import sys, os, json, time, subprocess sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))); from lib import Box, Registry from concurrent.futures import ThreadPoolExecutor ROOT = os.path.expanduser("~/Desktop/fleet"); OUT = os.path.join(ROOT, "disk.json"); ALERTS = os.path.join(ROOT, "disk-alerts.json") -HOOKS = "/Users/joshm/Projects/igneum/tools/community/discord-hooks.mjs" +HOOKS = os.environ.get("IGNEUM_DISCORD_HOOKS", os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "community", "discord-hooks.mjs")) # this tree's hook script def now(): return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) def one(item): iid, b = item diff --git a/tools/fleet/lib/box.py b/tools/fleet/lib/box.py index 67889b67a..05f06a1d6 100644 --- a/tools/fleet/lib/box.py +++ b/tools/fleet/lib/box.py @@ -78,7 +78,7 @@ class Box: return f"{F}/in/{name}" # ---- node ---- def stop_node(self): - self.run(f"pkill -f '{NODE_PATH_PATTERN}'; sleep 3; pkill -9 -f '{NODE_PATH_PATTERN}' 2>/dev/null; true", 40) + self.run(f"pkill -f '[/]root/fleet/in/igneumd|[/]opt/igneum/pkg/bin/igneumd'; sleep 3; pkill -9 -f '[/]root/fleet/in/igneumd|[/]opt/igneum/pkg/bin/igneumd' 2>/dev/null; true", 40) def start_node(self, binary, override_local, peers, devnet_suffix=None, verifier=None, extra=(), appdir=None, log=None, unsynced_mining=False): """Writes the override file, kills the old node (anchored on its path), starts the new one; returns the digest it prints.""" appdir = appdir or (f"{F}/dn2" if devnet_suffix else f"{F}/node"); log = log or (f"{F}/dn2-node.log" if devnet_suffix else f"{F}/node.log") @@ -118,7 +118,7 @@ class Box: b = self.rpc("eth_getBlockByNumber", [hex(height), False]) or {} return b.get("stateRoot") def node_version(self): - return self.run(f"v=$(pgrep -fa '{NODE_PATH_PATTERN}' | head -1 | awk '{{print $2}}'); [ -n \"$v\" ] && echo \"$(sha256sum \"$v\" | cut -c1-16) $($v --version 2>&1 | head -1)\"", 30)[1].strip() + return self.run(f"v=$(pgrep -fa '[/]root/fleet/in/igneumd|[/]opt/igneum/pkg/bin/igneumd' | head -1 | awk '{{print $2}}'); [ -n \"$v\" ] && echo \"$(sha256sum \"$v\" | cut -c1-16) $($v --version 2>&1 | head -1)\"", 30)[1].strip() def rejects_since(self, since_utc, log=None): log = log or f"{F}/node.log" out = self.run(f"awk -v s='{since_utc}' 'substr($0,1,19) >= s' {log} 2>/dev/null | grep -cE 'got reject message|PoW rejected|block rejected|invalid block'", 30)[1].strip() diff --git a/tools/fleet/lib/standing.py b/tools/fleet/lib/standing.py index 1bfc81523..1db2fad6a 100644 --- a/tools/fleet/lib/standing.py +++ b/tools/fleet/lib/standing.py @@ -54,7 +54,7 @@ def digest_alert(label, got, expected): st = json.load(open(DIGEST_ALERTS)) if os.path.exists(DIGEST_ALERTS) else {} if time.time() - st.get(label, 0) < 3600: return what = f"Fleet box {label} answers handshakes with consensus digest {got[:8]}, the live object is {expected[:8]}: peers on the live object refuse it and a node whose only peer it is sits on a frozen tip" - subprocess.run(["node", "/Users/joshm/Projects/igneum/tools/community/discord-hooks.mjs", "incident", "open", "--what", what, "--affected", "nodes that peer only with that box (a home miner reads synced on a frozen tip)", "--doing", "the fleet agent restarts the box's node on the live object; the supervisor's five-minute check found it", "--id", f"digest-{label}-{int(time.time())}", "--live"], capture_output=True, text=True, timeout=60) + subprocess.run(["node", os.environ.get("IGNEUM_DISCORD_HOOKS", os.path.join(HERE, "..", "community", "discord-hooks.mjs")), "incident", "open", "--what", what, "--affected", "nodes that peer only with that box (a home miner reads synced on a frozen tip)", "--doing", "the fleet agent restarts the box's node on the live object; the supervisor's five-minute check found it", "--id", f"digest-{label}-{int(time.time())}", "--live"], capture_output=True, text=True, timeout=60) st[label] = time.time(); json.dump(st, open(DIGEST_ALERTS, "w")); print(now(), "digest alert", label, got[:8], "expected", expected[:8], flush=True) def check_one(r): b = Box.from_registry(r["label"]); row = dict(r); row["t"] = now() diff --git a/tools/fleet/page.py b/tools/fleet/page.py index e646f89e8..644e5900f 100755 --- a/tools/fleet/page.py +++ b/tools/fleet/page.py @@ -10,7 +10,7 @@ result rows (results.json: {card_key: {...}}), the night rows (night.json: [..]) """ import time, json, os, sys, time, datetime, subprocess ROOT = os.path.expanduser("~/Desktop/fleet"); OUT = os.path.join(ROOT, "fleet.json") -PUBLISH = "/Users/joshm/Projects/igneum/tools/fleet/publish-fleet.sh" +PUBLISH = os.environ.get("IGNEUM_FLEET_PUBLISH", os.path.join(os.path.dirname(os.path.abspath(__file__)), "publish-fleet.sh")) # this tree's publish script (master's once merged) def now(): return datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") def jl(name, default): p = os.path.join(ROOT, name)