almosther isolating soleprint
This commit is contained in:
304
build.py
Normal file
304
build.py
Normal file
@@ -0,0 +1,304 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Soleprint Build Tool
|
||||
|
||||
Builds the soleprint instance using modelgen for model generation.
|
||||
|
||||
Modes:
|
||||
- dev: Uses symlinks for quick development (edit source, run from gen/)
|
||||
- deploy: Copies everything for production deployment (no symlinks)
|
||||
|
||||
Usage:
|
||||
python build.py dev
|
||||
python build.py deploy --output /path/to/deploy/
|
||||
python build.py models
|
||||
|
||||
Examples:
|
||||
# Set up dev environment
|
||||
python build.py dev
|
||||
|
||||
# Build for deployment
|
||||
python build.py deploy --output ../deploy/soleprint/
|
||||
|
||||
# Only regenerate models
|
||||
python build.py models
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# SPR root is where this script lives
|
||||
SPR_ROOT = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def ensure_dir(path: Path):
|
||||
"""Create directory if it doesn't exist."""
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def create_symlink(source: Path, target: Path):
|
||||
"""Create a symlink, removing existing if present."""
|
||||
if target.exists() or target.is_symlink():
|
||||
if target.is_symlink():
|
||||
target.unlink()
|
||||
elif target.is_dir():
|
||||
shutil.rmtree(target)
|
||||
else:
|
||||
target.unlink()
|
||||
|
||||
# Make relative symlink
|
||||
rel_source = os.path.relpath(source, target.parent)
|
||||
target.symlink_to(rel_source)
|
||||
print(f" Linked: {target.name} -> {rel_source}")
|
||||
|
||||
|
||||
def copy_path(source: Path, target: Path):
|
||||
"""Copy file or directory, resolving symlinks."""
|
||||
if target.exists():
|
||||
if target.is_dir():
|
||||
shutil.rmtree(target)
|
||||
else:
|
||||
target.unlink()
|
||||
|
||||
if source.is_dir():
|
||||
shutil.copytree(source, target, symlinks=False)
|
||||
print(f" Copied: {target.name}/ ({count_files(target)} files)")
|
||||
else:
|
||||
shutil.copy2(source, target)
|
||||
print(f" Copied: {target.name}")
|
||||
|
||||
|
||||
def count_files(path: Path) -> int:
|
||||
"""Count files in directory recursively."""
|
||||
return sum(1 for _ in path.rglob("*") if _.is_file())
|
||||
|
||||
|
||||
def generate_models(output_dir: Path):
|
||||
"""Generate models using modelgen tool.
|
||||
|
||||
Args:
|
||||
output_dir: Directory where models/pydantic/__init__.py will be created
|
||||
"""
|
||||
config_path = SPR_ROOT / "config" / "soleprint.config.json"
|
||||
|
||||
if not config_path.exists():
|
||||
print(f"Warning: Config not found at {config_path}")
|
||||
return False
|
||||
|
||||
# Soleprint-specific: models go in models/pydantic/__init__.py
|
||||
models_file = output_dir / "models" / "pydantic" / "__init__.py"
|
||||
models_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Run modelgen as subprocess
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"station.tools.modelgen",
|
||||
"from-config",
|
||||
"--config",
|
||||
str(config_path),
|
||||
"--output",
|
||||
str(models_file),
|
||||
"--format",
|
||||
"pydantic",
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, cwd=SPR_ROOT)
|
||||
return result.returncode == 0
|
||||
|
||||
|
||||
def build_dev(output_dir: Path):
|
||||
"""
|
||||
Build for development using symlinks.
|
||||
|
||||
Structure:
|
||||
gen/
|
||||
├── main.py -> ../hub/main.py
|
||||
├── index.html -> ../hub/index.html
|
||||
├── requirements.txt -> ../hub/requirements.txt
|
||||
├── dataloader/ -> ../hub/dataloader/
|
||||
├── artery/ -> ../artery/
|
||||
├── atlas/ -> ../atlas/
|
||||
├── station/ -> ../station/
|
||||
├── data/ -> ../data/
|
||||
└── models/ # Generated
|
||||
"""
|
||||
print(f"\n=== Building DEV environment ===")
|
||||
print(f"SPR root: {SPR_ROOT}")
|
||||
print(f"Output: {output_dir}")
|
||||
|
||||
ensure_dir(output_dir)
|
||||
|
||||
# Hub files (symlinks)
|
||||
print("\nLinking hub files...")
|
||||
hub = SPR_ROOT / "hub"
|
||||
create_symlink(hub / "main.py", output_dir / "main.py")
|
||||
create_symlink(hub / "index.html", output_dir / "index.html")
|
||||
create_symlink(hub / "requirements.txt", output_dir / "requirements.txt")
|
||||
create_symlink(hub / "dataloader", output_dir / "dataloader")
|
||||
|
||||
# System directories (symlinks)
|
||||
print("\nLinking systems...")
|
||||
for system in ["artery", "atlas", "station"]:
|
||||
source = SPR_ROOT / system
|
||||
if source.exists():
|
||||
create_symlink(source, output_dir / system)
|
||||
|
||||
# Data directory (symlink)
|
||||
print("\nLinking data...")
|
||||
create_symlink(SPR_ROOT / "data", output_dir / "data")
|
||||
|
||||
# Models (generated) - pass output_dir, modelgen adds models/pydantic
|
||||
print("\nGenerating models...")
|
||||
if not generate_models(output_dir):
|
||||
print(" Warning: Model generation failed, you may need to run it manually")
|
||||
|
||||
print("\n✓ Dev build complete!")
|
||||
print(f"\nTo run:")
|
||||
print(f" cd {output_dir}")
|
||||
print(f" python3 -m venv .venv")
|
||||
print(f" .venv/bin/pip install -r requirements.txt")
|
||||
print(f" .venv/bin/python main.py")
|
||||
|
||||
|
||||
def build_deploy(output_dir: Path):
|
||||
"""
|
||||
Build for deployment by copying all files (no symlinks).
|
||||
"""
|
||||
print(f"\n=== Building DEPLOY package ===")
|
||||
print(f"SPR root: {SPR_ROOT}")
|
||||
print(f"Output: {output_dir}")
|
||||
|
||||
if output_dir.exists():
|
||||
response = input(f"\nOutput directory exists. Overwrite? [y/N] ")
|
||||
if response.lower() != "y":
|
||||
print("Aborted.")
|
||||
return
|
||||
shutil.rmtree(output_dir)
|
||||
|
||||
ensure_dir(output_dir)
|
||||
|
||||
# Hub files (copy)
|
||||
print("\nCopying hub files...")
|
||||
hub = SPR_ROOT / "hub"
|
||||
copy_path(hub / "main.py", output_dir / "main.py")
|
||||
copy_path(hub / "index.html", output_dir / "index.html")
|
||||
copy_path(hub / "requirements.txt", output_dir / "requirements.txt")
|
||||
copy_path(hub / "dataloader", output_dir / "dataloader")
|
||||
|
||||
# System directories (copy)
|
||||
print("\nCopying systems...")
|
||||
for system in ["artery", "atlas", "station"]:
|
||||
source = SPR_ROOT / system
|
||||
if source.exists():
|
||||
copy_path(source, output_dir / system)
|
||||
|
||||
# Data directory (copy)
|
||||
print("\nCopying data...")
|
||||
copy_path(SPR_ROOT / "data", output_dir / "data")
|
||||
|
||||
# Models (generate fresh) - pass output_dir, modelgen adds models/pydantic
|
||||
print("\nGenerating models...")
|
||||
if not generate_models(output_dir):
|
||||
# Fallback: copy from gen if exists
|
||||
existing = SPR_ROOT / "gen" / "models"
|
||||
if existing.exists():
|
||||
print(" Using existing models from gen/")
|
||||
copy_path(existing, output_dir / "models")
|
||||
|
||||
# Copy schema.json for reference
|
||||
print("\nCopying schema...")
|
||||
copy_path(SPR_ROOT / "schema.json", output_dir / "schema.json")
|
||||
|
||||
# Create run script
|
||||
run_script = output_dir / "run.sh"
|
||||
run_script.write_text("""#!/bin/bash
|
||||
# Soleprint runner
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
if [ ! -d ".venv" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -r requirements.txt
|
||||
fi
|
||||
|
||||
echo "Starting soleprint on http://localhost:12000"
|
||||
.venv/bin/python main.py
|
||||
""")
|
||||
run_script.chmod(0o755)
|
||||
print(" Created: run.sh")
|
||||
|
||||
total_files = count_files(output_dir)
|
||||
print(f"\n✓ Deploy build complete! ({total_files} files)")
|
||||
print(f"\nTo run:")
|
||||
print(f" cd {output_dir}")
|
||||
print(f" ./run.sh")
|
||||
print(f"\nOr deploy to server:")
|
||||
print(f" rsync -av {output_dir}/ server:/app/soleprint/")
|
||||
print(f" ssh server 'cd /app/soleprint && ./run.sh'")
|
||||
|
||||
|
||||
def build_models():
|
||||
"""Only regenerate models."""
|
||||
print(f"\n=== Generating models only ===")
|
||||
|
||||
output_dir = SPR_ROOT / "gen"
|
||||
ensure_dir(output_dir)
|
||||
|
||||
if generate_models(output_dir):
|
||||
print("\n✓ Models generated!")
|
||||
else:
|
||||
print("\nError: Model generation failed", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Soleprint Build Tool",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=__doc__,
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
# dev command
|
||||
dev_parser = subparsers.add_parser("dev", help="Build for development (symlinks)")
|
||||
dev_parser.add_argument(
|
||||
"--output",
|
||||
"-o",
|
||||
type=Path,
|
||||
default=SPR_ROOT / "gen",
|
||||
help="Output directory (default: gen/)",
|
||||
)
|
||||
|
||||
# deploy command
|
||||
deploy_parser = subparsers.add_parser(
|
||||
"deploy", help="Build for deployment (copies)"
|
||||
)
|
||||
deploy_parser.add_argument(
|
||||
"--output",
|
||||
"-o",
|
||||
type=Path,
|
||||
required=True,
|
||||
help="Output directory for deployment package",
|
||||
)
|
||||
|
||||
# models command
|
||||
subparsers.add_parser("models", help="Only regenerate models")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "dev":
|
||||
build_dev(args.output.resolve())
|
||||
elif args.command == "deploy":
|
||||
build_deploy(args.output.resolve())
|
||||
elif args.command == "models":
|
||||
build_models()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user