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
49 changes: 30 additions & 19 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,24 +292,27 @@ def build_module(
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:
return False, time.time() - start, "Command not found: cmake"
else:
cmd = list(module.build_cmd)
if release and module.name == "backend":
Expand Down Expand Up @@ -502,7 +505,15 @@ def generate_logd(
text=True,
timeout=300,
)
if sr.returncode != 0:
if sr.returncode != 0 and not (logd_path.exists() and len(sr.stdout.strip()) == 20):
print(f"DEBUG: encryptly exit code: {sr.returncode}")
print(f"DEBUG: encryptly stdout: {sr.stdout.strip()}")
print(f"DEBUG: encryptly stderr: {sr.stderr.strip()}")
import os
for root, dirs, files in os.walk(str(workspace)):
for f in files:
fp = os.path.join(root, f)
print(f"DEBUG: File: {fp} (size: {os.path.getsize(fp)})")
print(
f" {color('✗', Colors.RED)} {logd_path.relative_to(ROOT)} creation failed: "
f"{sr.stderr.strip() or sr.stdout.strip()}"
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-07-13T01:08:18.790265+00:00",
"commit": "bf2147ac",
"diagnostic_logd": "diagnostic/build-bf2147ac.logd",
"chunked": false,
"chunk_size_bytes": null,
"password": "41fdae28e4457c8f3b02",
"decrypt_command": "encryptly unpack diagnostic/build-bf2147ac.logd <outdir> --password 41fdae28e4457c8f3b02",
"total_modules": 10,
"passed": 2,
"failed": 8,
"modules": [
{
"name": "backend",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "frontend",
"status": "PASS",
"elapsed_seconds": 1.853,
"artifact": "/Users/mariosantos/.gemini/antigravity/scratch/TentOfTrials/frontend/dist"
},
{
"name": "market",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "frailbox",
"status": "FAIL",
"elapsed_seconds": 0.062,
"artifact": null
},
{
"name": "engine",
"status": "FAIL",
"elapsed_seconds": 0.001,
"artifact": null
},
{
"name": "compliance",
"status": "FAIL",
"elapsed_seconds": 0.009,
"artifact": null
},
{
"name": "v2-market-stream",
"status": "PASS",
"elapsed_seconds": 0.049,
"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.
4 changes: 4 additions & 0 deletions tests/fixtures/json.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{"timestamp": "2026-07-13T01:00:00Z", "level": "info", "service": "auth", "message": "User login successful"}
{"time": "2026-07-13T01:05:00Z", "severity": "error", "logger": "db", "msg": "Connection timeout to postgres"}
{"@timestamp": "2026-07-13T01:10:00Z", "lvl": "warn", "app": "payment", "event": "Stripe API response delayed"}
{"invalid_json": true
4 changes: 4 additions & 0 deletions tests/fixtures/nginx.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
127.0.0.1 - - [13/Jul/2026:01:00:00 +0000] "GET /index.html HTTP/1.1" 200 1043 "https://google.com" "Mozilla/5.0"
192.168.1.1 - - [13/Jul/2026:01:05:00 +0000] "POST /login HTTP/1.1" 401 234 "https://example.com" "Mozilla/5.0"
10.0.0.1 - - [13/Jul/2026:01:10:00 +0000] "GET /api/v1/users HTTP/1.1" 500 45 "https://example.com" "Mozilla/5.0"
invalid nginx line here
4 changes: 4 additions & 0 deletions tests/fixtures/text.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
2026-07-13 01:00:00 [auth] INFO: User login successful
2026-07-13T01:05:00 ERROR: Connection timeout to postgres
[db] WARN: slow query detected
not a log line at all
111 changes: 111 additions & 0 deletions tests/test_log_aggregator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import sys
import unittest
from pathlib import Path

# Add tools directory to path to import log_aggregator
ROOT = Path(__file__).resolve().parent.parent
sys.path.append(str(ROOT / "tools"))

from log_aggregator import JSONLogParser, TextLogParser, NginxLogParser

class TestLogParsers(unittest.TestCase):
def setUp(self):
self.fixtures_dir = ROOT / "tests" / "fixtures"

def test_json_parser(self):
parser = JSONLogParser()
lines = (self.fixtures_dir / "json.log").read_text().splitlines()

# Line 1: Standard JSON
res1 = parser.parse(lines[0])
self.assertIsNotNone(res1)
self.assertEqual(res1["timestamp"], "2026-07-13T01:00:00Z")
self.assertEqual(res1["level"], "info")
self.assertEqual(res1["service"], "auth")
self.assertEqual(res1["message"], "User login successful")
self.assertEqual(res1["format"], "json")

# Line 2: Alternate key mappings (time, severity, logger, msg)
res2 = parser.parse(lines[1])
self.assertIsNotNone(res2)
self.assertEqual(res2["timestamp"], "2026-07-13T01:05:00Z")
self.assertEqual(res2["level"], "error")
self.assertEqual(res2["service"], "db")
self.assertEqual(res2["message"], "Connection timeout to postgres")

# Line 3: Alternate key mappings (@timestamp, lvl, app, event)
res3 = parser.parse(lines[2])
self.assertIsNotNone(res3)
self.assertEqual(res3["timestamp"], "2026-07-13T01:10:00Z")
self.assertEqual(res3["level"], "warn")
self.assertEqual(res3["service"], "payment")
self.assertEqual(res3["message"], "Stripe API response delayed")

# Line 4: Malformed JSON (should return None, not crash)
res4 = parser.parse(lines[3])
self.assertNullOrNone(res4)

def test_text_parser(self):
parser = TextLogParser()
lines = (self.fixtures_dir / "text.log").read_text().splitlines()

# Line 1: standard timestamp, level, service
res1 = parser.parse(lines[0])
self.assertIsNotNone(res1)
self.assertEqual(res1["level"], "info")
self.assertEqual(res1["service"], "auth")
self.assertEqual(res1["format"], "text")

# Line 2: ISO8601 timestamp and error level
res2 = parser.parse(lines[1])
self.assertIsNotNone(res2)
self.assertEqual(res2["level"], "error")
self.assertIsNone(res2["service"])

# Line 3: No timestamp, bracketed service, level
res3 = parser.parse(lines[2])
self.assertIsNotNone(res3)
self.assertEqual(res3["level"], "warn")
self.assertEqual(res3["service"], "db")

# Line 4: Completely unsupported plain text line (should parse message but no tags)
res4 = parser.parse(lines[3])
self.assertIsNotNone(res4)
self.assertEqual(res4["level"], "unknown")
self.assertIsNone(res4["service"])
self.assertEqual(res4["message"], "not a log line at all")

def test_nginx_parser(self):
parser = NginxLogParser()
lines = (self.fixtures_dir / "nginx.log").read_text().splitlines()

# Line 1: Status 200 -> level info
res1 = parser.parse(lines[0])
self.assertIsNotNone(res1)
self.assertEqual(res1["level"], "info")
self.assertEqual(res1["service"], "nginx")
self.assertEqual(res1["format"], "nginx")
self.assertEqual(res1["fields"]["remote_addr"], "127.0.0.1")
self.assertEqual(res1["fields"]["status"], 200)

# Line 2: Status 401 -> level warn
res2 = parser.parse(lines[1])
self.assertIsNotNone(res2)
self.assertEqual(res2["level"], "warn")
self.assertEqual(res2["fields"]["status"], 401)

# Line 3: Status 500 -> level error
res3 = parser.parse(lines[2])
self.assertIsNotNone(res3)
self.assertEqual(res3["level"], "error")
self.assertEqual(res3["fields"]["status"], 500)

# Line 4: Malformed nginx (should return None, not crash)
res4 = parser.parse(lines[3])
self.assertNullOrNone(res4)

def assertNullOrNone(self, val):
self.assertTrue(val is None)

if __name__ == "__main__":
unittest.main()
Binary file modified tools/encryptly/macos-arm64/encryptly
Binary file not shown.
6 changes: 4 additions & 2 deletions tools/log_aggregator.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,11 @@ def extract_service(self, line: str) -> Optional[str]:
match = re.search(r'\[(\w+)\]', line)
if match:
return match.group(1)
match = re.search(r'(\w+)\s*:', line)
match = re.search(r'\b([A-Za-z_][A-Za-z0-9_]*)\s*:', line)
if match and match.group(1).isupper():
return match.group(1)
service = match.group(1)
if service.upper() not in {"ERROR", "FATAL", "CRITICAL", "WARN", "WARNING", "INFO", "NOTICE", "DEBUG", "TRACE"}:
return service
return None


Expand Down