149 lines
4.8 KiB
Python
Executable file
149 lines
4.8 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""
|
|
@image Workspace Script Runner
|
|
|
|
Unified command runner for the @image workspace.
|
|
|
|
Usage:
|
|
./run install # Install all dependencies
|
|
./run build # Build TypeScript packages
|
|
./run test # Run tests (default: unit tests)
|
|
./run dev <service> # Start development server
|
|
./run clean # Clean build artifacts and caches
|
|
./run publish # Publish packages to registry
|
|
./run lint # Run linters
|
|
./run format # Format code
|
|
./run check # Type checking validation
|
|
./run --help # Show all available commands
|
|
./run <command> --help # Show command-specific help
|
|
|
|
Quick start:
|
|
./run install --build --test # Full setup and validation
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
class ScriptRunner:
|
|
"""Main script runner that dispatches to command handlers."""
|
|
|
|
def __init__(self):
|
|
# Get the actual script location (resolves symlinks)
|
|
script_path = Path(__file__).resolve()
|
|
# workspace_root is 3 levels up from script_runner.py
|
|
# script_runner.py -> run/ -> scripts/ -> workspace/
|
|
self.workspace_root = script_path.parent.parent.parent
|
|
self.commands = {}
|
|
|
|
def register_command(self, name: str, handler, help_text: str):
|
|
"""Register a command handler."""
|
|
self.commands[name] = {
|
|
"handler": handler,
|
|
"help": help_text,
|
|
}
|
|
|
|
def run(self, args=None):
|
|
"""Parse arguments and dispatch to command handler."""
|
|
parser = argparse.ArgumentParser(
|
|
prog="./run",
|
|
description="@image workspace script runner",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog=self._build_command_list(),
|
|
)
|
|
|
|
parser.add_argument(
|
|
"command",
|
|
choices=list(self.commands.keys()),
|
|
help="Command to run",
|
|
)
|
|
|
|
# Parse just the command first
|
|
if args is None:
|
|
args = sys.argv[1:]
|
|
|
|
if not args or args[0] in ["-h", "--help"]:
|
|
parser.print_help()
|
|
return 0
|
|
|
|
command_name = args[0]
|
|
command_args = args[1:]
|
|
|
|
if command_name not in self.commands:
|
|
parser.print_help()
|
|
return 1
|
|
|
|
# Dispatch to command handler
|
|
command = self.commands[command_name]
|
|
try:
|
|
return command["handler"](command_args, self.workspace_root)
|
|
except KeyboardInterrupt:
|
|
print("\n\nInterrupted by user")
|
|
return 130
|
|
except Exception as e:
|
|
print(f"\nError: {e}", file=sys.stderr)
|
|
return 1
|
|
|
|
def _build_command_list(self):
|
|
"""Build formatted command list for help text."""
|
|
if not self.commands:
|
|
return ""
|
|
|
|
lines = ["\nAvailable commands:"]
|
|
max_len = max(len(name) for name in self.commands.keys())
|
|
|
|
for name, cmd in sorted(self.commands.items()):
|
|
padding = " " * (max_len - len(name))
|
|
lines.append(f" {name}{padding} {cmd['help']}")
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
|
def load_command(command_path: Path):
|
|
"""Dynamically load a command module."""
|
|
import importlib.util
|
|
spec = importlib.util.spec_from_file_location(command_path.stem, command_path)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
runner = ScriptRunner()
|
|
|
|
# Import and register commands
|
|
# Use importlib to avoid relative import issues
|
|
# Resolve the actual file location (handles symlinks)
|
|
script_path = Path(__file__).resolve()
|
|
|
|
# Register all commands
|
|
commands = [
|
|
("install_command.py", "register_install_command"),
|
|
("test_command.py", "register_test_command"),
|
|
("test_command.py", "register_tests_command"), # Alias: ./run tests
|
|
("build_command.py", "register_build_command"),
|
|
("dev_command.py", "register_dev_command"),
|
|
("prod_command.py", "register_prod_command"),
|
|
("clean_command.py", "register_clean_command"),
|
|
("publish_command.py", "register_publish_command"),
|
|
("lint_command.py", "register_lint_command"),
|
|
("format_command.py", "register_format_command"),
|
|
("check_command.py", "register_check_command"),
|
|
("setup_gpu_command.py", "register_setup_gpu_command"),
|
|
("generate_command.py", "register_generate_command"),
|
|
]
|
|
|
|
for cmd_file, register_func in commands:
|
|
cmd_path = script_path.parent / cmd_file
|
|
if cmd_path.exists():
|
|
cmd_module = load_command(cmd_path)
|
|
getattr(cmd_module, register_func)(runner)
|
|
|
|
# Run
|
|
sys.exit(runner.run())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|