feat: Add new tools and configurations for Castle platform
- Introduced `uv.lock` for dependency management with various packages including `pytest`, `colorama`, and `pluggy`. - Added `pyrightconfig.json` for Python type checking configuration. - Expanded `recommendations.md` with detailed scaling recommendations and project management strategies. - Created shared `ruff.toml` for consistent linting across projects. - Developed `Castle Tools` with various utilities including Android backup, browser automation, document conversion, and search tools. - Implemented `backup-collect` and `schedule` tools for system administration tasks. - Enhanced `search` functionality with indexing and querying capabilities using Tantivy. - Added comprehensive documentation for each tool, including usage examples and installation instructions.
This commit is contained in:
121
cli/src/castle_cli/commands/create.py
Normal file
121
cli/src/castle_cli/commands/create.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""castle create - scaffold a new project from templates."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
|
||||
from castle_cli.config import load_config, save_config
|
||||
from castle_cli.manifest import (
|
||||
CaddySpec,
|
||||
ComponentManifest,
|
||||
ExposeSpec,
|
||||
HttpExposeSpec,
|
||||
HttpInternal,
|
||||
InstallSpec,
|
||||
ManageSpec,
|
||||
PathInstallSpec,
|
||||
ProxySpec,
|
||||
RunPythonUvTool,
|
||||
SystemdSpec,
|
||||
)
|
||||
from castle_cli.templates.scaffold import scaffold_project
|
||||
|
||||
|
||||
def next_available_port(config: object) -> int:
|
||||
"""Find the next available port starting from 9001 (9000 is reserved for gateway)."""
|
||||
used_ports = set()
|
||||
for manifest in config.components.values():
|
||||
if manifest.expose and manifest.expose.http:
|
||||
used_ports.add(manifest.expose.http.internal.port)
|
||||
# Also reserve gateway port
|
||||
used_ports.add(config.gateway.port)
|
||||
|
||||
port = 9001
|
||||
while port in used_ports:
|
||||
port += 1
|
||||
return port
|
||||
|
||||
|
||||
def run_create(args: argparse.Namespace) -> int:
|
||||
"""Create a new project."""
|
||||
config = load_config()
|
||||
name = args.name
|
||||
proj_type = args.type
|
||||
|
||||
if name in config.components:
|
||||
print(f"Error: component '{name}' already exists in castle.yaml")
|
||||
return 1
|
||||
|
||||
project_dir = config.root / name
|
||||
if project_dir.exists():
|
||||
print(f"Error: directory '{name}' already exists")
|
||||
return 1
|
||||
|
||||
# Determine port for services
|
||||
port = args.port
|
||||
if proj_type == "service" and port is None:
|
||||
port = next_available_port(config)
|
||||
|
||||
# Package name: convert kebab-case to snake_case
|
||||
package_name = name.replace("-", "_")
|
||||
|
||||
# Scaffold the project files
|
||||
scaffold_project(
|
||||
project_dir=project_dir,
|
||||
name=name,
|
||||
package_name=package_name,
|
||||
proj_type=proj_type,
|
||||
description=args.description or f"A castle {proj_type}",
|
||||
port=port,
|
||||
)
|
||||
|
||||
# Build manifest entry
|
||||
env_prefix = package_name.upper()
|
||||
|
||||
if proj_type == "service":
|
||||
manifest = ComponentManifest(
|
||||
id=name,
|
||||
description=args.description or f"A castle {proj_type}",
|
||||
run=RunPythonUvTool(
|
||||
runner="python_uv_tool",
|
||||
tool=name,
|
||||
cwd=name,
|
||||
env={f"{env_prefix}_DATA_DIR": f"/data/castle/{name}"},
|
||||
),
|
||||
expose=ExposeSpec(
|
||||
http=HttpExposeSpec(
|
||||
internal=HttpInternal(port=port),
|
||||
health_path="/health",
|
||||
)
|
||||
),
|
||||
proxy=ProxySpec(caddy=CaddySpec(path_prefix=f"/{name}")),
|
||||
manage=ManageSpec(systemd=SystemdSpec()),
|
||||
)
|
||||
elif proj_type == "tool":
|
||||
manifest = ComponentManifest(
|
||||
id=name,
|
||||
description=args.description or f"A castle {proj_type}",
|
||||
install=InstallSpec(path=PathInstallSpec(alias=name)),
|
||||
)
|
||||
else:
|
||||
# library or other
|
||||
manifest = ComponentManifest(
|
||||
id=name,
|
||||
description=args.description or f"A castle {proj_type}",
|
||||
)
|
||||
|
||||
config.components[name] = manifest
|
||||
save_config(config)
|
||||
|
||||
print(f"Created {proj_type} '{name}' at {project_dir}")
|
||||
if port:
|
||||
print(f" Port: {port}")
|
||||
print(" Registered in castle.yaml")
|
||||
print("\nNext steps:")
|
||||
print(f" cd {name}")
|
||||
print(" uv sync")
|
||||
if proj_type == "service":
|
||||
print(f" uv run {name} # starts on port {port}")
|
||||
print(f" castle test {name}")
|
||||
|
||||
return 0
|
||||
Reference in New Issue
Block a user