Files
glpi-neuroforge-mega/scripts/check-compose-env.py
jbergner 8c67c7a7fa
All checks were successful
release-tag / release-image (push) Successful in 10m52s
Update 1.5.0
2026-08-27 07:51:39 +02:00

61 lines
2.3 KiB
Python

#!/usr/bin/env python3
"""Fail if production Compose broadens Agent secrets or misses runtime env knobs."""
from __future__ import annotations
import re
import sys
from pathlib import Path
try:
import yaml
except Exception as exc: # PyYAML is intentionally only a release-dev dependency.
print(f"compose-env-check: PyYAML unavailable: {exc}", file=sys.stderr)
raise SystemExit(2)
root = Path(__file__).resolve().parents[1]
compose = yaml.safe_load((root / "docker-compose.yml").read_text())
agent = compose["services"]["agent"]
if "env_file" in agent:
raise SystemExit("compose-env-check: production Agent must not use env_file")
actual = set((agent.get("environment") or {}).keys())
# Canonical host-config variables are those documented in .env.example and
# literally referenced by non-test Agent runtime source. Internal service values
# are added separately below.
documented: set[str] = set()
for line in (root / ".env.example").read_text().splitlines():
match = re.match(r"([A-Z][A-Z0-9_]+)=", line)
if match:
documented.add(match.group(1))
source = "\n".join(
p.read_text(errors="ignore")
for p in (root / "services/agent").rglob("*.go")
if not p.name.endswith("_test.go")
)
expected = {name for name in documented if re.search(rf'"{re.escape(name)}"', source)}
expected.update({"NEUROFORGE_URL", "NEUROFORGE_API_KEY", "BRAIN_ACTIVITY_URL", "BRAIN_ACTIVITY_API_KEY"})
missing = sorted(expected - actual)
if missing:
print("compose-env-check: Agent runtime variables missing from explicit environment:", file=sys.stderr)
print("\n".join(f" {name}" for name in missing), file=sys.stderr)
raise SystemExit(1)
forbidden = {
"NEUROFORGE_ADMIN_TOKEN",
"NEUROFORGE_WORKER_TOKEN",
"NEUROFORGE_METRICS_TOKEN",
"NEUROFORGE_CLUSTER_TOKEN",
"NEUROFORGE_CONTROL_READ_TOKEN",
"KB_INTEGRATION_TOKEN",
"BASIC_AUTH_PASSWORD",
"CONTROL_BASIC_AUTH_PASSWORD",
}
leaked = sorted(forbidden & actual)
if leaked:
print("compose-env-check: unrelated privileged secrets exposed to Agent: " + ", ".join(leaked), file=sys.stderr)
raise SystemExit(1)
knowledge = compose["services"]["knowledge"]
if "env_file" in knowledge:
raise SystemExit("compose-env-check: production Knowledge must not use env_file")
print(f"compose-env-check: passed ({len(actual)} explicit Agent environment entries)")