refactor: separate standalone and managed room configs

- veins → shunts rename
- add cfg/standalone/ and cfg/<room>/ structure
- remove old data/*.json (moved to cfg/<room>/data/)
- update build.py and ctrl scripts
This commit is contained in:
buenosairesam
2026-01-02 17:09:58 -03:00
parent 46dc78db0e
commit 9e5cbbad1f
57 changed files with 1788 additions and 150 deletions

172
build.py
View File

@@ -8,18 +8,23 @@ Both dev and deploy modes copy files (no symlinks) for Docker compatibility.
After editing source files, re-run `python build.py dev` to update gen/.
Usage:
python build.py dev # Build gen/ from source
python build.py dev --cfg amar # Include amar room config
python build.py dev # Build gen/standalone/
python build.py dev --cfg amar # Build gen/amar/
python build.py dev --all # Build all (standalone + rooms)
python build.py deploy --output /path/ # Build for production
python build.py models # Only regenerate models
Examples:
# Set up dev environment
# Set up dev environment (standalone)
python build.py dev
cd gen && .venv/bin/python run.py
cd gen/standalone && .venv/bin/python run.py
# With room config
python build.py dev --cfg amar
cd gen/amar && .venv/bin/python run.py
# Build all targets
python build.py dev --all
# Build for deployment
python build.py deploy --output ../deploy/soleprint/
@@ -72,13 +77,15 @@ def count_files(path: Path) -> int:
return sum(1 for _ in path.rglob("*") if _.is_file())
def generate_models(output_dir: Path):
def generate_models(output_dir: Path, cfg_name: str | None = None):
"""Generate models using modelgen tool.
Args:
output_dir: Directory where models/pydantic/__init__.py will be created
cfg_name: Room config name (e.g., 'amar'), or None for standalone
"""
config_path = SPR_ROOT / "cfg" / "soleprint.config.json"
room = cfg_name or "standalone"
config_path = SPR_ROOT / "cfg" / room / "config.json"
if not config_path.exists():
log.warning(f"Config not found at {config_path}")
@@ -111,62 +118,64 @@ def copy_cfg(output_dir: Path, cfg_name: str | None):
Args:
output_dir: Target directory
cfg_name: Name of room config (e.g., 'amar'), or None for base only
cfg_name: Name of room config (e.g., 'amar'), or None for standalone
"""
room = cfg_name or "standalone"
room_cfg = SPR_ROOT / "cfg" / room
if not room_cfg.exists():
log.warning(f"Room config '{room}' not found at {room_cfg}")
return
log.info(f"\nCopying {room} room config...")
# Copy config.json to cfg/
cfg_dir = output_dir / "cfg"
ensure_dir(cfg_dir)
room_config = room_cfg / "config.json"
if room_config.exists():
copy_path(room_config, cfg_dir / "config.json")
# Always copy base config
base_config = SPR_ROOT / "cfg" / "soleprint.config.json"
if base_config.exists():
copy_path(base_config, cfg_dir / "soleprint.config.json")
# Copy data/ to output data/
room_data = room_cfg / "data"
if room_data.exists():
log.info(f" Copying {room} data files...")
copy_path(room_data, output_dir / "data")
# Copy room-specific config if specified
if cfg_name:
room_cfg = SPR_ROOT / "cfg" / cfg_name
if room_cfg.exists() and room_cfg.is_dir():
log.info(f"\nCopying {cfg_name} room config...")
for item in room_cfg.iterdir():
if item.name == ".env.example":
# Copy .env.example to output root as template
copy_path(item, output_dir / ".env.example")
elif item.is_dir():
copy_path(item, cfg_dir / cfg_name / item.name)
else:
ensure_dir(cfg_dir / cfg_name)
copy_path(item, cfg_dir / cfg_name / item.name)
# Copy .env.example to output root
env_example = room_cfg / ".env.example"
if env_example.exists():
copy_path(env_example, output_dir / ".env.example")
# Copy room-specific databrowse depot if exists
room_databrowse = room_cfg / "databrowse" / "depot"
if room_databrowse.exists():
log.info(f" Copying {cfg_name} databrowse depot...")
target = output_dir / "station" / "monitors" / "databrowse" / "depot"
copy_path(room_databrowse, target)
# Copy room-specific databrowse depot if exists
room_databrowse = room_cfg / "databrowse" / "depot"
if room_databrowse.exists():
log.info(f" Copying {room} databrowse depot...")
target = output_dir / "station" / "monitors" / "databrowse" / "depot"
copy_path(room_databrowse, target)
# Copy room-specific tester tests if exists
room_tests = room_cfg / "tester" / "tests"
if room_tests.exists():
log.info(f" Copying {cfg_name} tester tests...")
target = output_dir / "station" / "tools" / "tester" / "tests"
copy_path(room_tests, target)
# Copy room-specific tester tests if exists
room_tests = room_cfg / "tester" / "tests"
if room_tests.exists():
log.info(f" Copying {room} tester tests...")
target = output_dir / "station" / "tools" / "tester" / "tests"
copy_path(room_tests, target)
# Copy room-specific monitors if exists
room_monitors = room_cfg / "monitors"
if room_monitors.exists():
log.info(f" Copying {cfg_name} monitors...")
for monitor in room_monitors.iterdir():
if monitor.is_dir():
target = output_dir / "station" / "monitors" / monitor.name
copy_path(monitor, target)
# Copy room-specific monitors if exists
room_monitors = room_cfg / "monitors"
if room_monitors.exists():
log.info(f" Copying {room} monitors...")
for monitor in room_monitors.iterdir():
if monitor.is_dir():
target = output_dir / "station" / "monitors" / monitor.name
copy_path(monitor, target)
# Copy room-specific models if exists
room_models = room_cfg / "models"
if room_models.exists():
log.info(f" Copying {cfg_name} models...")
target = output_dir / "models" / cfg_name
copy_path(room_models, target)
else:
log.warning(f"Room config '{cfg_name}' not found at {room_cfg}")
# Copy room-specific models if exists
room_models = room_cfg / "models"
if room_models.exists():
log.info(f" Copying {room} models...")
target = output_dir / "models" / room
copy_path(room_models, target)
def build_dev(output_dir: Path, cfg_name: str | None = None):
@@ -174,7 +183,7 @@ def build_dev(output_dir: Path, cfg_name: str | None = None):
Build for development using copies (Docker-compatible).
Structure:
gen/
gen/standalone/ or gen/<room>/
├── main.py
├── run.py
├── index.html
@@ -189,7 +198,7 @@ def build_dev(output_dir: Path, cfg_name: str | None = None):
├── .env.example # From cfg/<room>/.env.example
└── models/ # Generated
After editing source files, re-run `python build.py dev` to update gen/.
After editing source files, re-run `python build.py dev` to update.
"""
log.info("\n=== Building DEV environment ===")
log.info(f"SPR root: {SPR_ROOT}")
@@ -217,17 +226,13 @@ def build_dev(output_dir: Path, cfg_name: str | None = None):
if source.exists():
copy_path(source, output_dir / system)
# Data directory
log.info("\nCopying data...")
copy_path(SPR_ROOT / "data", output_dir / "data")
# Config
# Config (includes data/ from room)
log.info("\nCopying config...")
copy_cfg(output_dir, cfg_name)
# Models (generated)
log.info("\nGenerating models...")
if not generate_models(output_dir):
if not generate_models(output_dir, cfg_name):
log.warning("Model generation failed, you may need to run it manually")
log.info("\n✓ Dev build complete!")
@@ -236,7 +241,12 @@ def build_dev(output_dir: Path, cfg_name: str | None = None):
log.info(f" python3 -m venv .venv")
log.info(f" .venv/bin/pip install -r requirements.txt")
log.info(f" .venv/bin/python run.py # Single-port bare-metal dev")
log.info(f"\nAfter editing source, rebuild with: python build.py dev")
if cfg_name:
log.info(
f"\nAfter editing source, rebuild with: python build.py dev --cfg {cfg_name}"
)
else:
log.info(f"\nAfter editing source, rebuild with: python build.py dev")
def build_deploy(output_dir: Path, cfg_name: str | None = None):
@@ -276,19 +286,16 @@ def build_deploy(output_dir: Path, cfg_name: str | None = None):
if source.exists():
copy_path(source, output_dir / system)
# Data directory (copy)
log.info("\nCopying data...")
copy_path(SPR_ROOT / "data", output_dir / "data")
# Config (copy)
# Config (includes data/ from room)
log.info("\nCopying config...")
copy_cfg(output_dir, cfg_name)
# Models (generate fresh) - pass output_dir, modelgen adds models/pydantic
log.info("\nGenerating models...")
if not generate_models(output_dir):
if not generate_models(output_dir, cfg_name):
# Fallback: copy from gen if exists
existing = SPR_ROOT / "gen" / "models"
room = cfg_name or "standalone"
existing = SPR_ROOT / "gen" / room / "models"
if existing.exists():
log.info(" Using existing models from gen/")
copy_path(existing, output_dir / "models")
@@ -354,8 +361,8 @@ def main():
"--output",
"-o",
type=Path,
default=SPR_ROOT / "gen",
help="Output directory (default: gen/)",
default=None,
help="Output directory (default: gen/standalone/ or gen/<cfg>/)",
)
dev_parser.add_argument(
"--cfg",
@@ -364,6 +371,11 @@ def main():
default=None,
help="Room config to include (e.g., 'amar')",
)
dev_parser.add_argument(
"--all",
action="store_true",
help="Build all configs (standalone + all rooms in cfg/)",
)
# deploy command
deploy_parser = subparsers.add_parser(
@@ -390,7 +402,23 @@ def main():
args = parser.parse_args()
if args.command == "dev":
build_dev(args.output.resolve(), args.cfg)
if getattr(args, "all", False):
# Build standalone
build_dev(SPR_ROOT / "gen" / "standalone", None)
# Build all room configs
cfg_dir = SPR_ROOT / "cfg"
for room in cfg_dir.iterdir():
if room.is_dir() and room.name not in ("__pycache__",):
build_dev(SPR_ROOT / "gen" / room.name, room.name)
else:
# Determine output directory
if args.output:
output_dir = args.output.resolve()
elif args.cfg:
output_dir = SPR_ROOT / "gen" / args.cfg
else:
output_dir = SPR_ROOT / "gen" / "standalone"
build_dev(output_dir, args.cfg)
elif args.command == "deploy":
build_deploy(args.output.resolve(), args.cfg)
elif args.command == "models":