39 lines
4.1 KiB
Python
39 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Publish 1 of 0.3.15 on the standing live-devnet boxes (the shipper's set, 6 October 2026, 21:4xZ): the Linux igneumd 7f0bde70
|
|
(release-0.3.15-node f1ea7a38; the first two cuts 713ef876 and 7961c5f1 failed the canary), igneum-miner e827d9db, the generator-4 workers (cuda 97e036e2, opencl ef9fbd03); the override
|
|
file stays the live thirteen-field object (digest b18ed271 on the new binary). Per box, through lib.box: the node binary to
|
|
/root/fleet/in/igneumd-<sha16> (sha-checked), the miner and workers over /opt/igneum/pkg/bin (the old ones kept as *.0314),
|
|
/root/fleet/standing.node rewritten so the supervisor restarts the node on it (data dir kept), the miner killed once so
|
|
the supervisor's loop restarts it on the new miner and worker. Then the read-back table: version line, digest, worker sha.
|
|
publish-0315.py [labels...] default: every standing box with role live
|
|
publish-0315.py --readback [labels...]
|
|
"""
|
|
import sys, os, hashlib
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))); from lib import Box, Registry, standing
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
SHIP = os.environ["IGNEUM_SHIP_TREE"] # the shipper's worktree with the built binaries; no default: another worktree is never assumed
|
|
NODE = (SHIP + "/vendor/igneum-node-0315/target-remote/release/igneumd", "7f0bde70cf2e72a3a9479ba6421f2b37162c3dd4391bf413717c99e6f168668d") # release-0.3.15-node f1ea7a38 (the stamp gate, the receive-side version rule, the pruned-node sync fix)
|
|
MINER = (SHIP + "/vendor/igneum-node-0315/target-remote/release/igneum-miner", "3976c1b270761218")
|
|
CUDA = (SHIP + "/infra/cross/out-workers/igneum-worker-cuda", "97e036e23f4ace66")
|
|
OCL = (SHIP + "/infra/cross/out-workers/igneum-worker-opencl", "ef9fbd03e7ce14d1")
|
|
def sha16(p): return hashlib.sha256(open(p, "rb").read()).hexdigest()[:16]
|
|
def publish(label):
|
|
b = Box.from_registry(label)
|
|
for p, s in (MINER, CUDA, OCL):
|
|
if sha16(p) != s: return label, f"local {os.path.basename(p)} sha {sha16(p)} is not {s}"
|
|
path = b.install_payload(NODE[0], NODE[1])
|
|
b.put([MINER[0], CUDA[0], OCL[0]], "/root/fleet/in/")
|
|
rc, out, err = b.run(f"""set -e; cd /opt/igneum/pkg/bin; for f in igneum-miner igneum-worker-cuda igneum-worker-opencl; do [ -e $f.0314 ] || cp $f $f.0314; cp /root/fleet/in/$f $f.new && chmod +x $f.new && mv $f.new $f; done
|
|
echo {path} > /root/fleet/standing.node
|
|
pkill -9 -f '[/](opt/igneum/pkg/bin|root/fleet/in)/igneumd(-0313|-[0-9a-f]{16})? --devnet --appdir=' 2>/dev/null; pkill -9 -f '[/]opt/igneum/pkg/bin/igneum-miner mine' 2>/dev/null; pkill -9 -f '[/]opt/igneum/pkg/bin/igneum-worker-cuda --serve' 2>/dev/null; true # the supervisor restarts the node on the new pointer within a minute, and the miner with it
|
|
echo swapped""", 120)
|
|
return label, out.strip() or err[:120]
|
|
def readback(label):
|
|
b = Box.from_registry(label)
|
|
rc, out, err = b.run(r"""P=$(pgrep -af '[/](opt/igneum/pkg/bin|root/fleet/in)/igneumd[^ ]* ' | head -1 | awk '{print $2}'); echo bin=$P sha=$(sha256sum $P 2>/dev/null | cut -c1-16) version="$($P --version 2>&1 | head -1)" vline=$(grep -oE 'igneumd/2\.1\.0-[0-9a-f]+' /root/fleet/node.log | tail -1) digest=$(grep -o 'digest: [0-9a-f]*' /root/fleet/node.log | tail -1 | awk '{print substr($2,1,16)}') miner=$(sha256sum /opt/igneum/pkg/bin/igneum-miner | cut -c1-16) cuda=$(sha256sum /opt/igneum/pkg/bin/igneum-worker-cuda | cut -c1-16) ocl=$(sha256sum /opt/igneum/pkg/bin/igneum-worker-opencl | cut -c1-16) $(/opt/igneum/pkg/bin/igneum-miner watch 1 grpc://127.0.0.1:26610 2>/dev/null | grep -oE 'blocks=[0-9]+|synced=[a-z]+' | tr '\n' ' ') miner_up=$(pgrep -fc '[/]opt/igneum/pkg/bin/igneum-miner mine') prover_up=$(pgrep -fc '[p]ython3 -u /root/fleet/in/box-prover.py') rej=$(grep -c 'PoW rejected' /root/fleet/node.log)""", 60)
|
|
return label, out.strip() or err[:120]
|
|
if __name__ == "__main__":
|
|
a = sys.argv[1:]; rb = a and a[0] == "--readback"; a = a[1:] if rb else a
|
|
labels = a or [r["label"] for r in standing.roster() if r["role"] == "live"]
|
|
with ThreadPoolExecutor(13) as ex:
|
|
for l, o in ex.map(readback if rb else publish, labels): print(l, "::", o, flush=True)
|