Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 24 additions & 20 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def _normalize_arch(machine: str) -> Optional[str]:

def _normalize_os() -> Optional[str]:
system = platform.system().lower()
if system == "linux":
if system in {"linux", "android"}:
return "linux"
if system == "darwin":
return "macos"
Expand Down Expand Up @@ -288,28 +288,32 @@ def build_module(
return False, time.time() - start, f"npm install failed:\n{install_result.stderr}"
except subprocess.TimeoutExpired:
return False, time.time() - start, "npm install TIMEOUT (120s)"
except FileNotFoundError as e:
return False, time.time() - start, f"Command not found: {e}"

if module.name == "engine":

build_type = "Release" if release else "Debug"
cfg_result = subprocess.run(
["cmake", "-S", ".", "-B", "build",
f"-DCMAKE_BUILD_TYPE={build_type}"],
cwd=str(module.dir),
capture_output=True,
text=True,
timeout=120,
env=env,
)
if cfg_result.returncode != 0:
return False, time.time() - start, (
f"CMake configure failed:\n{cfg_result.stderr}")
if verbose:
print(f" {color('cmake configured', Colors.GRAY)}")
cmd = ["cmake", "--build", "build"]
if release:
cmd.append("--config")
cmd.append("Release")
try:
cfg_result = subprocess.run(
["cmake", "-S", ".", "-B", "build",
f"-DCMAKE_BUILD_TYPE={build_type}"],
cwd=str(module.dir),
capture_output=True,
text=True,
timeout=120,
env=env,
)
if cfg_result.returncode != 0:
return False, time.time() - start, (
f"CMake configure failed:\n{cfg_result.stderr}")
if verbose:
print(f" {color('cmake configured', Colors.GRAY)}")
cmd = ["cmake", "--build", "build"]
if release:
cmd.append("--config")
cmd.append("Release")
except FileNotFoundError as e:
return False, time.time() - start, f"Command not found: {e}"
else:
cmd = list(module.build_cmd)
if release and module.name == "backend":
Expand Down
75 changes: 75 additions & 0 deletions diagnostic/build-bf2147ac-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"generated_at": "2026-06-19T17:28:00.923428+00:00",
"commit": "bf2147ac",
"diagnostic_logd": "diagnostic/build-bf2147ac.logd",
"chunked": false,
"chunk_size_bytes": null,
"password": "Packed /data/data/com.termux/files/home/.cache/tent-of-trials/logd-workspace into /data/data/com.termux/files/home/weilixiong-tentoftrials/diagnostic/build-bf2147ac.logd",
"decrypt_command": "encryptly unpack diagnostic/build-bf2147ac.logd <outdir> --password Packed /data/data/com.termux/files/home/.cache/tent-of-trials/logd-workspace into /data/data/com.termux/files/home/weilixiong-tentoftrials/diagnostic/build-bf2147ac.logd",
"total_modules": 10,
"passed": 1,
"failed": 9,
"modules": [
{
"name": "backend",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "frontend",
"status": "FAIL",
"elapsed_seconds": 1.205,
"artifact": null
},
{
"name": "market",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "frailbox",
"status": "PASS",
"elapsed_seconds": 0.051,
"artifact": "/data/data/com.termux/files/home/weilixiong-tentoftrials/frailbox/frailbox"
},
{
"name": "engine",
"status": "FAIL",
"elapsed_seconds": 0.004,
"artifact": null
},
{
"name": "compliance",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "v2-market-stream",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "nfc-scanner",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "openapi-haskell",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "openapi-tools",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
}
],
"pr_note": "Include this metadata and diagnostic/build-bf2147ac.logd in your PR. Maintainers may ask you to remove these diagnostic artifacts before merging."
}
Binary file added diagnostic/build-bf2147ac.logd
Binary file not shown.
Binary file modified tools/encryptly/linux-arm64/encryptly
Binary file not shown.
129 changes: 101 additions & 28 deletions tools/health_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,22 +151,84 @@ def check_disk_usage(path: str = "/") -> Tuple[str, str, float]:

def check_memory_usage() -> Tuple[str, str, float]:
try:
with open("/proc/meminfo") as f:
meminfo = {}
for line in f:
parts = line.split(":")
if len(parts) == 2:
key = parts[0].strip()
value = parts[1].strip().replace(" kB", "")
try:
meminfo[key] = int(value) * 1024
except ValueError:
pass

total = meminfo.get("MemTotal", 0)
available = meminfo.get("MemAvailable", 0)
used = total - available
pct = (used / total) * 100 if total > 0 else 0
import platform
# 1. Try /proc/meminfo first (Linux/Android)
if os.path.exists("/proc/meminfo"):
with open("/proc/meminfo") as f:
meminfo = {}
for line in f:
parts = line.split(":")
if len(parts) == 2:
key = parts[0].strip()
value = parts[1].strip().replace(" kB", "")
try:
meminfo[key] = int(value) * 1024
except ValueError:
pass
total = meminfo.get("MemTotal", 0)
available = meminfo.get("MemAvailable", 0)
if total > 0 and available == 0:
free = meminfo.get("MemFree", 0)
buffers = meminfo.get("Buffers", 0)
cached = meminfo.get("Cached", 0)
available = free + buffers + cached
used = total - available
pct = (used / total) * 100 if total > 0 else 0
else:
# 2. Non-Linux fallbacks
system = platform.system().lower()
if "darwin" in system:
# macOS standard library fallback using subprocess to run sysctl and vm_stat
import subprocess
total_str = subprocess.check_output(["sysctl", "-n", "hw.memsize"]).strip()
total = int(total_str)
vm_stat_out = subprocess.check_output(["vm_stat"]).decode("utf-8")
vm_stats = {}
page_size = 4096
for line in vm_stat_out.splitlines():
if "page size of" in line:
parts = line.split("page size of")
if len(parts) > 1:
page_size = int(parts[1].split()[0])
elif ":" in line:
parts = line.split(":")
key = parts[0].strip()
val = parts[1].strip().rstrip(".")
try:
vm_stats[key] = int(val)
except ValueError:
pass

free_pages = vm_stats.get("Pages free", 0)
inactive_pages = vm_stats.get("Pages inactive", 0)
speculative_pages = vm_stats.get("Pages speculative", 0)
available = (free_pages + inactive_pages + speculative_pages) * page_size
used = total - available
pct = (used / total) * 100 if total > 0 else 0
elif "windows" in system:
# Windows standard library fallback using ctypes (kernel32.GlobalMemoryStatusEx)
import ctypes
class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
("dwLength", ctypes.c_ulong),
("dwMemoryLoad", ctypes.c_ulong),
("ullTotalPhys", ctypes.c_ulonglong),
("ullAvailPhys", ctypes.c_ulonglong),
("ullTotalPageFile", ctypes.c_ulonglong),
("ullAvailPageFile", ctypes.c_ulonglong),
("ullTotalVirtual", ctypes.c_ulonglong),
("ullAvailVirtual", ctypes.c_ulonglong),
("ullAvailExtendedVirtual", ctypes.c_ulonglong),
]
stat = MEMORYSTATUSEX()
stat.dwLength = ctypes.sizeof(MEMORYSTATUSEX)
ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
total = stat.ullTotalPhys
available = stat.ullAvailPhys
used = total - available
pct = float(stat.dwMemoryLoad)
else:
raise NotImplementedError("Platform not supported for memory check fallback")

if pct < MEMORY_THRESHOLD_WARNING:
return "OK", f"{pct:.1f}% used ({used // (1024**3)}GB/{total // (1024**3)}GB)", pct
Expand All @@ -180,18 +242,29 @@ def check_memory_usage() -> Tuple[str, str, float]:

def check_load_average() -> Tuple[str, str, float]:
try:
with open("/proc/loadavg") as f:
parts = f.read().strip().split()
load = float(parts[0])
cpu_count = os.cpu_count() or 1
load_pct = (load / cpu_count) * 100

if load_pct < 70:
return "OK", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
elif load_pct < 90:
return "WARNING", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
else:
return "CRITICAL", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
load = None
# 1. Try /proc/loadavg first (Linux/Android)
if os.path.exists("/proc/loadavg"):
with open("/proc/loadavg") as f:
parts = f.read().strip().split()
load = float(parts[0])

# 2. Fallback to os.getloadavg() if available
if load is None and hasattr(os, "getloadavg"):
load = os.getloadavg()[0]

if load is None:
raise NotImplementedError("Load average not supported on this platform")

cpu_count = os.cpu_count() or 1
load_pct = (load / cpu_count) * 100

if load_pct < 70:
return "OK", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
elif load_pct < 90:
return "WARNING", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
else:
return "CRITICAL", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
except Exception as e:
return "WARNING", f"Cannot check: {e}", 0

Expand Down
108 changes: 108 additions & 0 deletions tools/test_health_check_fallback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import unittest
from unittest.mock import patch, MagicMock
import os
import platform
import sys

# Add parent directory to path so we can import health_check
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import tools.health_check as hc

class TestHealthCheckFallback(unittest.TestCase):

@patch('os.path.exists')
@patch('platform.system')
@patch('subprocess.check_output')
def test_darwin_memory_fallback(self, mock_check_output, mock_system, mock_exists):
# Setup mocks
mock_exists.return_value = False
mock_system.return_value = "Darwin"

# Mock sysctl hw.memsize returning 16GB
# Mock vm_stat returning statistics
def check_output_side_effect(cmd, *args, **kwargs):
if "sysctl" in cmd:
return b"17179869184\n"
elif "vm_stat" in cmd:
return (
b"Mach Virtual Memory Statistics: (page size of 4096 bytes)\n"
b"Pages free: 1000000.\n"
b"Pages active: 2000000.\n"
b"Pages inactive: 1000000.\n"
b"Pages speculative: 200000.\n"
)
raise ValueError(f"Unexpected subprocess call: {cmd}")

mock_check_output.side_effect = check_output_side_effect

status, detail, val = hc.check_memory_usage()

self.assertEqual(status, "OK")
# 47.5% used (used memory = 16GB - 2.2M pages * 4096 = 17179869184 - 9011200000 = 8168669184 = 7.6GB used out of 16GB)
self.assertIn("47.5% used", detail)
self.assertAlmostEqual(val, 47.548, places=2)

@patch('os.path.exists')
@patch('platform.system')
@patch('os.cpu_count')
def test_darwin_load_fallback(self, mock_cpu_count, mock_system, mock_exists):
mock_exists.return_value = False
mock_system.return_value = "Darwin"
mock_cpu_count.return_value = 4

# Mock os.getloadavg to return load values
with patch('os.getloadavg', return_value=(1.5, 1.2, 1.0), create=True):
status, detail, val = hc.check_load_average()

self.assertEqual(status, "OK")
self.assertIn("Load: 1.5 (38% of 4 cores)", detail)
self.assertEqual(val, 1.5)

@patch('os.path.exists')
@patch('platform.system')
def test_windows_memory_fallback(self, mock_system, mock_exists):
mock_exists.return_value = False
mock_system.return_value = "Windows"

# Let's mock ctypes completely
mock_ctypes = MagicMock()

# Dummy structure fields
class MockStructure:
dwLength = 0
dwMemoryLoad = 40
ullTotalPhys = 17179869184
ullAvailPhys = 10307921510

mock_ctypes.Structure = object

with patch.dict('sys.modules', {'ctypes': mock_ctypes}):
# Set structure return value
mock_ctypes.sizeof.return_value = 64

# Since MEMORYSTATUSEX is defined inside check_memory_usage, we will mock ctypes
# in sys.modules. We need to make sure MEMORYSTATUSEX instantiation returns our mocked struct
# and windll.kernel32.GlobalMemoryStatusEx is called.
# To do this cleanly, we'll configure mock_ctypes:
# MEMORYSTATUSEX is defined as a subclass of ctypes.Structure
# When MEMORYSTATUSEX() is called, it returns a new instance.
# We can capture the instance or mock the __new__ or __init__ of Structure
# Or even simpler, mock_ctypes.Structure can be a class that has the fields we want!
class DummyStructure(object):
def __init__(self, *args, **kwargs):
self.dwLength = 0
self.dwMemoryLoad = 40
self.ullTotalPhys = 17179869184
self.ullAvailPhys = 10307921510

mock_ctypes.Structure = DummyStructure
mock_ctypes.sizeof.return_value = 64

status, detail, val = hc.check_memory_usage()

# Verify status code is OK and uses 40% memory load from dwMemoryLoad
self.assertEqual(status, "OK")
self.assertEqual(val, 40.0)

if __name__ == '__main__':
unittest.main()