All checks were successful
release-tag / release-image (push) Successful in 10m52s
61 lines
2.3 KiB
Python
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)")
|