Less stack-centric and location-centric model.
This commit is contained in:
@@ -1,39 +1,72 @@
|
||||
"""castle run - run a component in the foreground."""
|
||||
"""castle run - run a program or service in the foreground.
|
||||
|
||||
Unified: if the target is a program with a declared `run` command, run that in
|
||||
the foreground (dev-run). Otherwise fall back to the deployed-service run from
|
||||
the registry.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from castle_core.registry import REGISTRY_PATH, load_registry
|
||||
|
||||
from castle_cli.config import load_config
|
||||
|
||||
|
||||
def _run_program(name: str, extra: list[str]) -> int | None:
|
||||
"""Run a program's declared `run` command in the foreground.
|
||||
|
||||
Returns the exit code, or None if the program has no declared run command
|
||||
(so the caller can fall back to the deployed-service path).
|
||||
"""
|
||||
config = load_config()
|
||||
prog = config.programs.get(name)
|
||||
if prog is None or prog.commands is None or prog.commands.run is None:
|
||||
return None
|
||||
if not prog.source:
|
||||
print(f"Error: program '{name}' has no source directory.")
|
||||
return 1
|
||||
cwd = Path(prog.source)
|
||||
cmds = prog.commands.run
|
||||
rc = 0
|
||||
for i, argv in enumerate(cmds):
|
||||
# Append extra args to the final command in the sequence.
|
||||
full = list(argv) + (extra if i == len(cmds) - 1 else [])
|
||||
print(f"Running {name}: {' '.join(full)}")
|
||||
rc = subprocess.run(full, cwd=cwd).returncode
|
||||
if rc != 0:
|
||||
break
|
||||
return rc
|
||||
|
||||
|
||||
def run_run(args: argparse.Namespace) -> int:
|
||||
"""Run a component in the foreground using the registry."""
|
||||
"""Run a program (declared run) or a deployed service in the foreground."""
|
||||
name = args.name
|
||||
extra_args = getattr(args, "extra", []) or []
|
||||
|
||||
# 1. Program with a declared run command.
|
||||
prog_rc = _run_program(name, extra_args)
|
||||
if prog_rc is not None:
|
||||
return prog_rc
|
||||
|
||||
# 2. Deployed service from the registry.
|
||||
if not REGISTRY_PATH.exists():
|
||||
print("Error: no registry found. Run 'castle deploy' first.")
|
||||
return 1
|
||||
|
||||
registry = load_registry()
|
||||
name = args.name
|
||||
|
||||
if name not in registry.deployed:
|
||||
print(f"Error: component '{name}' not found in registry.")
|
||||
print("Run 'castle deploy' to update the registry.")
|
||||
print(f"Error: '{name}' is not a runnable program or deployed service.")
|
||||
print("Declare a `run` command in castle.yaml, or run 'castle deploy'.")
|
||||
return 1
|
||||
|
||||
deployed = registry.deployed[name]
|
||||
|
||||
# Build command with any extra args
|
||||
extra_args = getattr(args, "extra", []) or []
|
||||
cmd = list(deployed.run_cmd) + extra_args
|
||||
|
||||
# Merge environment
|
||||
env = dict(os.environ)
|
||||
env.update(deployed.env)
|
||||
|
||||
# Run in foreground (no cwd — registry-based, no repo dependency)
|
||||
print(f"Running {name}: {' '.join(cmd)}")
|
||||
result = subprocess.run(cmd, env=env)
|
||||
return result.returncode
|
||||
return subprocess.run(cmd, env=env).returncode
|
||||
|
||||
Reference in New Issue
Block a user