fix: Improve command resolution in deploy and sync processes, adding warnings for missing tools
This commit is contained in:
@@ -175,8 +175,13 @@ def _build_run_cmd(run: object, env: dict[str, str]) -> list[str]:
|
|||||||
"""Build a run command list from a RunSpec."""
|
"""Build a run command list from a RunSpec."""
|
||||||
match run.runner:
|
match run.runner:
|
||||||
case "python_uv_tool":
|
case "python_uv_tool":
|
||||||
uv = shutil.which("uv") or "uv"
|
resolved = shutil.which(run.tool)
|
||||||
cmd = [uv, "run", run.tool]
|
if not resolved:
|
||||||
|
print(
|
||||||
|
f" Warning: '{run.tool}' not on PATH. "
|
||||||
|
f"Install with: uv tool install --editable <source>"
|
||||||
|
)
|
||||||
|
cmd = [resolved or run.tool]
|
||||||
if run.args:
|
if run.args:
|
||||||
cmd.extend(run.args)
|
cmd.extend(run.args)
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -8,6 +8,26 @@ import subprocess
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from castle_cli.config import ensure_dirs, load_config
|
from castle_cli.config import ensure_dirs, load_config
|
||||||
|
from castle_cli.manifest import ComponentManifest
|
||||||
|
|
||||||
|
|
||||||
|
def _sync_cmd(manifest: ComponentManifest) -> list[str] | None:
|
||||||
|
"""Derive the sync command from the manifest's runner."""
|
||||||
|
run = manifest.run
|
||||||
|
if run is None:
|
||||||
|
# No runner — check for build commands (frontends)
|
||||||
|
if manifest.build and manifest.build.commands:
|
||||||
|
# Frontends declare build commands; infer from source dir at call site
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
|
match run.runner:
|
||||||
|
case "python_uv_tool" | "python_module":
|
||||||
|
return ["uv", "sync"]
|
||||||
|
case "node":
|
||||||
|
return [run.package_manager, "install"]
|
||||||
|
case _:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def run_sync(args: argparse.Namespace) -> int:
|
def run_sync(args: argparse.Namespace) -> int:
|
||||||
@@ -24,35 +44,49 @@ def run_sync(args: argparse.Namespace) -> int:
|
|||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
print("Warning: git submodule update failed (may not be a git repo)")
|
print("Warning: git submodule update failed (may not be a git repo)")
|
||||||
|
|
||||||
# Run uv sync in each project that has a pyproject.toml
|
# Sync dependencies in each project
|
||||||
all_ok = True
|
all_ok = True
|
||||||
synced_dirs: set[Path] = set()
|
synced_dirs: set[Path] = set()
|
||||||
for name, manifest in config.components.items():
|
for name, manifest in config.components.items():
|
||||||
working_dir = manifest.source_dir or name
|
source_dir = manifest.source_dir
|
||||||
project_dir = config.root / working_dir
|
if not source_dir:
|
||||||
pyproject = project_dir / "pyproject.toml"
|
continue
|
||||||
|
project_dir = config.root / source_dir
|
||||||
|
|
||||||
if not pyproject.exists() or project_dir in synced_dirs:
|
if project_dir in synced_dirs or not project_dir.is_dir():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print(f"\nSyncing {name}...")
|
cmd = _sync_cmd(manifest)
|
||||||
result = subprocess.run(["uv", "sync"], cwd=project_dir)
|
if cmd is None:
|
||||||
|
# No runner — check if it's a frontend with a package.json
|
||||||
|
if manifest.build and (project_dir / "package.json").exists():
|
||||||
|
pm = "pnpm" if (project_dir / "pnpm-lock.yaml").exists() else "npm"
|
||||||
|
cmd = [pm, "install"]
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
label = cmd[0]
|
||||||
|
print(f"\nSyncing {name} ({label})...")
|
||||||
|
result = subprocess.run(cmd, cwd=project_dir)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
print(f" Warning: uv sync failed for {name}")
|
print(f" Warning: sync failed for {name}")
|
||||||
all_ok = False
|
all_ok = False
|
||||||
else:
|
else:
|
||||||
print(" OK")
|
print(" OK")
|
||||||
synced_dirs.add(project_dir)
|
synced_dirs.add(project_dir)
|
||||||
|
|
||||||
# Install tools — infer method from project structure
|
# Install components as uv tools or symlinks
|
||||||
uv_path = shutil.which("uv") or "uv"
|
uv_path = shutil.which("uv") or "uv"
|
||||||
installed_dirs: set[Path] = set()
|
installed_dirs: set[Path] = set()
|
||||||
|
|
||||||
for name, manifest in config.components.items():
|
for name, manifest in config.components.items():
|
||||||
if not manifest.tool:
|
# Determine source directory — from tool.source or manifest.source
|
||||||
continue
|
source = None
|
||||||
|
if manifest.tool and manifest.tool.source:
|
||||||
source = manifest.tool.source
|
source = manifest.tool.source
|
||||||
|
elif manifest.run and manifest.run.runner == "python_uv_tool" and manifest.source_dir:
|
||||||
|
source = manifest.source_dir
|
||||||
|
|
||||||
if not source:
|
if not source:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user