Add system dependencies support for tools
Tools can now declare system-level package dependencies (apt, brew, pacman, dnf) that get checked and optionally installed when the tool is installed or run. Features: - SystemDependency dataclass with short form (string) and long form (dict) - New system_deps.py module with platform detection and installation - `cmdforge system-deps <tool>` command to check/install system packages - `cmdforge check` now shows both CmdForge and system dependencies - `cmdforge registry install` prompts for system dep installation - GUI: System Dependencies section in Tool Builder with add/edit dialog - Runner warns about missing system deps before execution - Integration with project install (manifest and lock-based) Also includes: - Quote paths in wrapper scripts for spaces support - Tests for value type preservation in code steps - Unskip invalid tool name test Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
@@ -148,6 +148,7 @@ def cmd_install_deps(args):
|
||||
from ..resolver import ToolResolver, install_from_registry, ToolNotFoundError
|
||||
from ..registry_client import RegistryError
|
||||
from ..lockfile import Lockfile
|
||||
from ..system_deps import prompt_install_missing
|
||||
|
||||
manifest = load_manifest()
|
||||
|
||||
@@ -252,9 +253,13 @@ def cmd_install_deps(args):
|
||||
print(f"Installing {node.qualified_name}...", end=" ", flush=True)
|
||||
spec = node.qualified_name
|
||||
install_version = node.resolved_version or node.version_constraint
|
||||
install_from_registry(spec, version=install_version)
|
||||
resolved = install_from_registry(spec, version=install_version)
|
||||
print(f"v{node.resolved_version or 'latest'}")
|
||||
installed += 1
|
||||
if resolved.tool.system_dependencies:
|
||||
print()
|
||||
tool_ref = f"{resolved.owner}/{resolved.tool.name}" if resolved.owner else resolved.tool.name
|
||||
prompt_install_missing(resolved.tool.system_dependencies, tool_ref)
|
||||
except RegistryError as e:
|
||||
print(f"FAILED: {e.message}")
|
||||
failed.append((node.qualified_name, e.message))
|
||||
@@ -278,6 +283,7 @@ def _install_from_lock(lock: "Lockfile", args) -> int:
|
||||
"""Install exact versions from lock file."""
|
||||
from ..resolver import ToolResolver, ToolNotFoundError, install_from_registry
|
||||
from ..registry_client import RegistryError
|
||||
from ..system_deps import prompt_install_missing
|
||||
|
||||
verbose = getattr(args, 'verbose', False)
|
||||
dry_run = getattr(args, 'dry_run', False)
|
||||
@@ -333,9 +339,13 @@ def _install_from_lock(lock: "Lockfile", args) -> int:
|
||||
try:
|
||||
print(f"Installing {pkg.name}@{pkg.version}...", end=" ", flush=True)
|
||||
# Use correct API: install_from_registry(spec, version)
|
||||
install_from_registry(pkg.name, version=pkg.version)
|
||||
resolved = install_from_registry(pkg.name, version=pkg.version)
|
||||
print("OK")
|
||||
installed += 1
|
||||
if resolved.tool.system_dependencies:
|
||||
print()
|
||||
tool_ref = f"{resolved.owner}/{resolved.tool.name}" if resolved.owner else resolved.tool.name
|
||||
prompt_install_missing(resolved.tool.system_dependencies, tool_ref)
|
||||
except RegistryError as e:
|
||||
print(f"FAILED: {e.message}")
|
||||
failed.append((pkg.name, e.message))
|
||||
|
||||
Reference in New Issue
Block a user