"""castle sync - sync submodules and install dependencies.""" from __future__ import annotations import argparse import shutil import subprocess from pathlib import Path from castle_cli.config import ensure_dirs, load_config def run_sync(args: argparse.Namespace) -> int: """Sync submodules and install dependencies in all projects.""" config = load_config() ensure_dirs() # Update git submodules print("Updating git submodules...") result = subprocess.run( ["git", "submodule", "update", "--init", "--recursive"], cwd=config.root, ) if result.returncode != 0: print("Warning: git submodule update failed (may not be a git repo)") # Sync dependencies in each component's source directory all_ok = True synced_dirs: set[Path] = set() for name, comp in config.programs.items(): source_dir = comp.source_dir if not source_dir: continue project_dir = config.root / source_dir if project_dir in synced_dirs or not project_dir.is_dir(): continue # Determine sync command based on project type cmd = None if (project_dir / "pyproject.toml").exists(): cmd = ["uv", "sync"] elif (project_dir / "package.json").exists(): pm = "pnpm" if (project_dir / "pnpm-lock.yaml").exists() else "npm" cmd = [pm, "install"] if cmd is None: continue label = cmd[0] print(f"\nSyncing {name} ({label})...") result = subprocess.run(cmd, cwd=project_dir) if result.returncode != 0: print(f" Warning: sync failed for {name}") all_ok = False else: print(" OK") synced_dirs.add(project_dir) # Install python-runner services as uv tools uv_path = shutil.which("uv") or "uv" installed_dirs: set[Path] = set() for name, svc in config.services.items(): if svc.run.runner != "python": continue # Find source from component reference source = None if svc.component and svc.component in config.programs: source = config.programs[svc.component].source_dir if not source: continue source_dir = config.root / source if source_dir in installed_dirs: continue if (source_dir / "pyproject.toml").exists(): print(f"\nInstalling {name}...") prog = config.programs.get(svc.component or "") if svc.component else None extras = prog.install_extras if prog else [] pkg_spec = str(source_dir) if extras: pkg_spec += "[" + ",".join(extras) + "]" result = subprocess.run( [uv_path, "tool", "install", "--editable", pkg_spec, "--force"], capture_output=True, text=True, ) if result.returncode != 0: if "already installed" in result.stderr.lower(): print(f" {name}: already installed") else: print(f" Warning: {result.stderr.strip()}") all_ok = False else: print(f" {name}: OK") installed_dirs.add(source_dir) if all_ok: print("\nAll projects synced.") else: print("\nSync completed with warnings.") return 0