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
27 changes: 17 additions & 10 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,19 +288,26 @@ 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"npm install 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,
)
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,
)
except subprocess.TimeoutExpired:
return False, time.time() - start, "CMake configure TIMEOUT (120s)"
except FileNotFoundError as e:
return False, time.time() - start, f"CMake command not found: {e}"
if cfg_result.returncode != 0:
return False, time.time() - start, (
f"CMake configure failed:\n{cfg_result.stderr}")
Expand Down Expand Up @@ -659,7 +666,7 @@ def main():
print(f"\n {color('⚠ Some tools missing - will try anyway:', Colors.YELLOW)}")
for m in missing:
print(f" {m}")
print(f" {color('Not all modules will build. That\'s fine.', Colors.GRAY)}")
print(" " + color("Not all modules will build. That's fine.", Colors.GRAY))
else:
print(f" {color('✓ All prerequisites found', Colors.GREEN)}")

Expand Down
75 changes: 75 additions & 0 deletions diagnostic/build-a17cdea5-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"generated_at": "2026-07-21T03:12:17.207157+00:00",
"commit": "a17cdea5",
"diagnostic_logd": "diagnostic\\build-a17cdea5.logd",
"chunked": false,
"chunk_size_bytes": null,
"password": "0455a341b7f73333ead7",
"decrypt_command": "encryptly unpack diagnostic\\build-a17cdea5.logd <outdir> --password 0455a341b7f73333ead7",
"total_modules": 10,
"passed": 2,
"failed": 8,
"modules": [
{
"name": "backend",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "frontend",
"status": "FAIL",
"elapsed_seconds": 0.006,
"artifact": null
},
{
"name": "market",
"status": "PASS",
"elapsed_seconds": 3.221,
"artifact": "C:\\Users\\5325\\Documents\\Codex\\2026-07-16\\goal-generame-5-dolares-y-haz\\work\\tentoftrials-log-fixtures\\market\\market"
},
{
"name": "frailbox",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null
},
{
"name": "engine",
"status": "FAIL",
"elapsed_seconds": 0.013,
"artifact": null
},
{
"name": "compliance",
"status": "PASS",
"elapsed_seconds": 1.466,
"artifact": "C:\\Users\\5325\\Documents\\Codex\\2026-07-16\\goal-generame-5-dolares-y-haz\\work\\tentoftrials-log-fixtures\\compliance\\build"
},
{
"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-a17cdea5.logd in your PR. Maintainers may ask you to remove these diagnostic artifacts before merging."
}
Binary file added diagnostic/build-a17cdea5.logd
Binary file not shown.
4 changes: 2 additions & 2 deletions tools/log_aggregator.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def parse(self, line: str) -> Optional[Dict[str, Any]]:
'message': match.group(5),
'fields': {
'remote_addr': match.group(1),
'remote_user': match.group(2),
'remote_user': match.group(3),
'request': match.group(5),
'status': status_code,
'body_bytes': match.group(7),
Expand All @@ -204,7 +204,7 @@ def parse(self, line: str) -> Optional[Dict[str, Any]]:

class LogAggregator:
def __init__(self):
self.parsers = [JSONLogParser(), TextLogParser(), NginxLogParser()]
self.parsers = [JSONLogParser(), NginxLogParser(), TextLogParser()]
self.entries: List[Dict[str, Any]] = []
self.level_counts: Counter = Counter()
self.service_counts: Counter = Counter()
Expand Down
2 changes: 2 additions & 0 deletions tools/tests/fixtures/log_aggregator/json.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"timestamp":"2026-07-20T18:42:11Z","level":"ERROR","service":"billing-api","message":"payment capture failed","request_id":"req_42","amount_cents":1250}
{"time":"2026-07-20T18:43:01Z","severity":"INFO","logger":"worker","msg":"queue drained","jobs":3}
2 changes: 2 additions & 0 deletions tools/tests/fixtures/log_aggregator/malformed.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"timestamp":"2026-07-20T18:47:00Z","level":
this is not json and not nginx, but the aggregator should keep it as plain text
2 changes: 2 additions & 0 deletions tools/tests/fixtures/log_aggregator/nginx.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
203.0.113.10 - alice [20/Jul/2026:18:45:03 +0000] "GET /api/lessons?q=dco HTTP/1.1" 404 321 "https://example.test/" "Mozilla/5.0"
198.51.100.22 - - [20/Jul/2026:18:46:15 +0000] "POST /feedback HTTP/1.1" 502 42 "-" "curl/8.8.0"
2 changes: 2 additions & 0 deletions tools/tests/fixtures/log_aggregator/text.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
2026-07-20 18:44:22 WARN [inventory] stock low sku=SKU-42 remaining=2
AUTH: ERROR token rejected for user=demo ip=192.0.2.44
120 changes: 120 additions & 0 deletions tools/tests/test_log_aggregator_fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import importlib.util
import json
import subprocess
import sys
import tempfile
import unittest
from datetime import datetime, timezone
from pathlib import Path


TOOLS_DIR = Path(__file__).resolve().parents[1]
FIXTURE_DIR = Path(__file__).resolve().parent / "fixtures" / "log_aggregator"
MODULE_PATH = TOOLS_DIR / "log_aggregator.py"

spec = importlib.util.spec_from_file_location("log_aggregator", MODULE_PATH)
log_aggregator = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(log_aggregator)


def fixture_line(name: str, line_number: int = 0) -> str:
return (FIXTURE_DIR / name).read_text(encoding="utf-8").splitlines()[line_number]


class IndependentLogParserFixtureTests(unittest.TestCase):
def test_json_fixture_preserves_key_structured_fields(self):
parser = log_aggregator.JSONLogParser()

first = parser.parse(fixture_line("json.log", 0))
self.assertIsNotNone(first)
self.assertEqual(first["format"], "json")
self.assertEqual(first["timestamp"], "2026-07-20T18:42:11Z")
self.assertEqual(first["level"], "ERROR")
self.assertEqual(first["service"], "billing-api")
self.assertEqual(first["message"], "payment capture failed")
self.assertEqual(first["fields"]["request_id"], "req_42")

second = parser.parse(fixture_line("json.log", 1))
self.assertIsNotNone(second)
self.assertEqual(second["timestamp"], "2026-07-20T18:43:01Z")
self.assertEqual(second["level"], "INFO")
self.assertEqual(second["service"], "worker")
self.assertEqual(second["message"], "queue drained")

def test_plain_text_fixture_extracts_timestamp_level_and_service(self):
parser = log_aggregator.TextLogParser()

entry = parser.parse(fixture_line("text.log", 0))
self.assertIsNotNone(entry)
self.assertEqual(entry["format"], "text")
self.assertEqual(entry["timestamp"], int(datetime(2026, 7, 20, 18, 44, 22, tzinfo=timezone.utc).timestamp()))
self.assertEqual(entry["level"], "warn")
self.assertEqual(entry["service"], "inventory")
self.assertIn("stock low", entry["message"])

service_entry = parser.parse(fixture_line("text.log", 1))
self.assertIsNotNone(service_entry)
self.assertEqual(service_entry["level"], "error")
self.assertEqual(service_entry["service"], "AUTH")

def test_nginx_fixture_uses_nginx_parser_before_plain_text_fallback(self):
aggregator = log_aggregator.LogAggregator()

self.assertTrue(aggregator._parse_line(fixture_line("nginx.log", 0)))
first = aggregator.entries[0]
self.assertEqual(first["format"], "nginx")
self.assertEqual(first["timestamp"], int(datetime(2026, 7, 20, 18, 45, 3, tzinfo=timezone.utc).timestamp()))
self.assertEqual(first["level"], "warn")
self.assertEqual(first["service"], "nginx")
self.assertEqual(first["message"], "GET /api/lessons?q=dco HTTP/1.1")
self.assertEqual(first["fields"]["remote_addr"], "203.0.113.10")
self.assertEqual(first["fields"]["remote_user"], "alice")
self.assertEqual(first["fields"]["status"], 404)
self.assertEqual(first["fields"]["body_bytes"], "321")

self.assertTrue(aggregator._parse_line(fixture_line("nginx.log", 1)))
second = aggregator.entries[1]
self.assertEqual(second["format"], "nginx")
self.assertEqual(second["level"], "error")
self.assertEqual(second["fields"]["status"], 502)

def test_malformed_or_unsupported_lines_do_not_crash_parsers(self):
self.assertIsNone(log_aggregator.JSONLogParser().parse(fixture_line("malformed.log", 0)))
self.assertIsNone(log_aggregator.NginxLogParser().parse(fixture_line("malformed.log", 1)))

aggregator = log_aggregator.LogAggregator()
self.assertFalse(aggregator._parse_line(""))
self.assertTrue(aggregator._parse_line(fixture_line("malformed.log", 1)))
self.assertEqual(aggregator.entries[0]["format"], "text")
self.assertEqual(aggregator.entries[0]["level"], "unknown")

def test_cli_processes_independent_nginx_fixture_without_changing_output_contract(self):
with tempfile.TemporaryDirectory() as tmpdir:
output = Path(tmpdir) / "report.json"
result = subprocess.run(
[
sys.executable,
str(MODULE_PATH),
"--input",
str(FIXTURE_DIR / "nginx.log"),
"--output",
str(output),
],
capture_output=True,
text=True,
check=False,
)

self.assertEqual(result.returncode, 0, result.stderr)
report = json.loads(output.read_text(encoding="utf-8"))
self.assertEqual(report["summary"]["total_entries"], 2)
self.assertEqual(report["summary"]["by_service"], {"nginx": 2})
self.assertEqual(report["summary"]["by_level"], {"warn": 1, "error": 1})
self.assertEqual(report["service_breakdown"]["nginx"]["total"], 2)
self.assertEqual(report["service_breakdown"]["nginx"]["errors"], 1)
self.assertEqual(report["service_breakdown"]["nginx"]["warns"], 1)


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