Files
recipe-maintainer/lib/config.py
autonomic-bot f283a371bb recipe-maintainer: public snapshot (secrets + deployment plans removed, single commit)
Sanitized single-commit public mirror of recipe-maintainer.
- Removed test-ssh/.testenv (live creds); added test-ssh/.testenv.example placeholders.
- Removed plans/ and planned-updates/ (deployment-planning docs) so no client/
  deployment domains appear in the public repo.
- All other secret stores were already gitignored.
- docs.coopcloud.tech retained as a submodule (public upstream).
2026-06-16 20:18:24 +00:00

176 lines
4.9 KiB
Python

"""Central configuration for the recipe-maintainer-3 project.
Resolves the workspace root, provides paths to key directories,
and loads instance/deployment configuration from TOML files.
All paths are resolved lazily on first access and cached.
"""
import os
import tomllib
from pathlib import Path
# ---------------------------------------------------------------------------
# Workspace root detection
# ---------------------------------------------------------------------------
_workspace: Path | None = None
def get_workspace() -> Path:
"""Auto-detect the workspace root by walking up to find AGENTS.md."""
global _workspace
if _workspace is not None:
return _workspace
# Start from this file's location (lib/config.py → lib/ → workspace root)
candidate = Path(__file__).resolve().parent.parent
if (candidate / "AGENTS.md").exists():
_workspace = candidate
return _workspace
# Fallback: walk up from cwd
candidate = Path.cwd()
for _ in range(10):
if (candidate / "AGENTS.md").exists():
_workspace = candidate
return _workspace
parent = candidate.parent
if parent == candidate:
break
candidate = parent
raise RuntimeError(
"Could not find workspace root (no AGENTS.md found). "
"Are you running from within the recipe-maintainer-3 directory?"
)
# ---------------------------------------------------------------------------
# Path constants (lazy)
# ---------------------------------------------------------------------------
def _ws() -> Path:
return get_workspace()
class _Paths:
"""Lazy path accessors for key project directories."""
@property
def WORKSPACE(self) -> Path:
return _ws()
@property
def LIB_DIR(self) -> Path:
return _ws() / "lib"
@property
def SCRIPTS_DIR(self) -> Path:
return _ws() / "scripts"
@property
def RECIPE_INFO_DIR(self) -> Path:
return _ws() / "recipe-info"
@property
def TESTSECRETS_DIR(self) -> Path:
return _ws() / "recipe-info" / "testsecrets"
@property
def LOGS_DIR(self) -> Path:
return _ws() / "logs"
@property
def PLANS_DIR(self) -> Path:
return _ws() / "plans"
@property
def PLANNED_UPDATES_DIR(self) -> Path:
return _ws() / "planned-updates"
@property
def TERRAFORM_DIR(self) -> Path:
return _ws() / "terraform"
@property
def ABRA_DIR(self) -> Path:
env = os.environ.get("ABRA_DIR")
if env:
return Path(env)
return Path.home() / ".abra"
@property
def ABRA_RECIPES_DIR(self) -> Path:
return self.ABRA_DIR / "recipes"
@property
def ABRA_SERVERS_DIR(self) -> Path:
return self.ABRA_DIR / "servers"
paths = _Paths()
# ---------------------------------------------------------------------------
# Settings
# ---------------------------------------------------------------------------
def load_settings() -> dict:
"""Load settings.toml from the workspace root."""
p = paths.WORKSPACE / "settings.toml"
if not p.exists():
return {}
with open(p, "rb") as f:
return tomllib.load(f)
# ---------------------------------------------------------------------------
# Instance loading (from settings.toml [instances.*])
# ---------------------------------------------------------------------------
def _load_instances_from_settings() -> dict:
"""Load the [instances] section from settings.toml."""
settings = load_settings()
return settings.get("instances", {})
def get_instance_names() -> list[str]:
"""Return all instance names from settings.toml [instances.*]."""
return list(_load_instances_from_settings().keys())
def get_default_instance_name() -> str:
"""Return the default instance from settings.toml."""
settings = load_settings()
default = settings.get("default_instance")
if not default:
raise RuntimeError("No default_instance set in settings.toml")
if default not in get_instance_names():
raise RuntimeError(
f"default_instance '{default}' from settings.toml "
f"not found in settings.toml [instances]"
)
return default
def load_instance_toml(name: str) -> dict:
"""Load raw TOML data for a specific instance from settings.toml."""
instances = _load_instances_from_settings()
if name not in instances:
raise ValueError(f"Instance '{name}' not found in settings.toml [instances]")
data = dict(instances[name])
data["name"] = name
return data
def load_recipe_toml(recipe_name: str) -> dict:
"""Load recipe-info/<recipe>/recipe.toml."""
p = paths.RECIPE_INFO_DIR / recipe_name / "recipe.toml"
if not p.exists():
return {"name": recipe_name}
with open(p, "rb") as f:
data = tomllib.load(f)
data.setdefault("name", recipe_name)
return data