Discover independent camera instances and prepare their versioned runtime from Node or remote Core. Add isolated SDK workers, camera controls, raw dual-fisheye WebRTC preview, and shared action/region loading states. Recover existing Node bindings over known Tailscale addresses after a Core LAN address change. Preserve identities and trust, pin both peers, migrate endpoints with revision checks, and require real heartbeats for online status. Fix the Python client certificate profile for Go X509 verification. Pin Design Guideline 8c53f73 and retain installer/build/acceptance history. Node 0.8.19 is installed; X4 0.1.3-3 is bundled but hardware activation is pending. Validation: qualified DG/Node builds and Go race tests; 31 fleet tests; Python-to-Go certificate interoperability and live tailnet recovery with five fresh heartbeats; prior 38 X4 tests and bounded remote WebRTC acceptance. Clean-OS, replug/power autonomy, local X4 video and long-run stability remain open.
66 lines
3.6 KiB
Python
66 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Engineering-only, sequential build. Never run by an Ubuntu operator."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from build_deb import build, VERSION, BRAND_SHA256
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
DG_COMMIT = "8c53f73ee521561a1cc7944a7f7cf113702f40b5"
|
|
|
|
|
|
def guideline_sources():
|
|
dg = ROOT.parents[2] / "NODEDC_DESIGN_GUIDELINE"
|
|
paths = list((dg / "packages/ui-react/src").glob("*"))
|
|
paths += list((dg / "packages/ui-react/dist").glob("*"))
|
|
paths += list((dg / "packages/ui-core/src").glob("*"))
|
|
paths += list((dg / "packages/ui-core/dist").glob("*"))
|
|
paths += [dg / "packages/ui-core/styles.css", dg / "packages/tokens/tokens.css", dg / "packages/tokens/themes.css"]
|
|
return {str(p.relative_to(dg)): hashlib.sha256(p.read_bytes()).hexdigest()
|
|
for p in sorted(paths) if p.is_file()}
|
|
|
|
|
|
def provenance():
|
|
files = {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest()
|
|
for p in sorted(ROOT.rglob("*")) if p.is_file()
|
|
and not any(x in p.relative_to(ROOT).parts for x in ("node_modules", "build", "__pycache__"))}
|
|
return {"package": "mission-core-node", "version": VERSION,
|
|
"brand_mark_sha256": BRAND_SHA256,
|
|
"base_commit": subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip(),
|
|
"design_guideline_commit": DG_COMMIT,
|
|
"design_guideline_files": guideline_sources(),
|
|
"shared_sensor_ui_files": {str(p.relative_to(ROOT.parents[1])): hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted((ROOT.parents[1] / "packages/sensor-ui/src").rglob("*")) if p.is_file()},
|
|
"shared_spatial_ui_files": {str(p.relative_to(ROOT.parents[1])): hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted((ROOT.parents[1] / "packages/spatial-ui/src").rglob("*")) if p.is_file()},
|
|
"k1_frontend_files": {str(p.relative_to(ROOT.parents[1])): hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted((ROOT.parents[1] / "plugins/xgrids-k1/frontend/src").rglob("*")) if p.is_file()},
|
|
"toolchain": json.loads((ROOT / "toolchain.json").read_text()), "files": files}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--go", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
go = args.go.resolve()
|
|
expected = json.loads((ROOT / "toolchain.json").read_text())["version"]
|
|
if subprocess.check_output([str(go), "version"], text=True).split()[2] != expected:
|
|
sys.exit("Go version does not match toolchain.json")
|
|
dg = ROOT.parents[2] / "NODEDC_DESIGN_GUIDELINE"
|
|
if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=dg, text=True).strip() != DG_COMMIT:
|
|
sys.exit("Design Guideline revision does not match the admitted build")
|
|
subprocess.run(["npm", "run", "build"], cwd=ROOT / "ui", check=True)
|
|
assets = ROOT / "web/dist"
|
|
if assets.exists():
|
|
shutil.rmtree(assets)
|
|
shutil.copytree(ROOT / "ui/dist", assets)
|
|
output = ROOT / "build"
|
|
output.mkdir(exist_ok=True)
|
|
env = dict(os.environ, GOMAXPROCS="2", CGO_ENABLED="0", GOOS="linux", GOARCH="amd64")
|
|
subprocess.run([str(go), "build", "-trimpath", f"-ldflags=-s -w -X main.version={VERSION}", "-o",
|
|
str(output / "node-agent-linux-amd64"), "./cmd/node-agent"], cwd=ROOT, env=env, check=True)
|
|
(output / "provenance.json").write_text(json.dumps(provenance(), indent=2) + "\n")
|
|
build(output / "node-agent-linux-amd64", output / f"mission-core-node_{VERSION}_amd64.deb")
|