wip
This commit is contained in:
parent
6649488b0b
commit
c974d319a7
|
@ -28,40 +28,52 @@ def ensure(version: tuple[int, int, int]):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def setupLogger(verbose: bool):
|
class LoggerArgs:
|
||||||
if verbose:
|
verbose = cli.Arg[bool]("v", "verbose", "Enable verbose logging", default=False)
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.DEBUG,
|
|
||||||
format=f"{vt100.CYAN}%(asctime)s{vt100.RESET} {vt100.YELLOW}%(levelname)s{vt100.RESET} %(name)s: %(message)s",
|
|
||||||
datefmt="%Y-%m-%d %H:%M:%S",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
projectRoot = model.Project.topmost()
|
|
||||||
logFile = const.GLOBAL_LOG_FILE
|
|
||||||
if projectRoot is not None:
|
|
||||||
logFile = os.path.join(projectRoot.dirname(), const.PROJECT_LOG_FILE)
|
|
||||||
|
|
||||||
shell.mkdir(os.path.dirname(logFile))
|
|
||||||
|
|
||||||
logging.basicConfig(
|
class logger:
|
||||||
level=logging.INFO,
|
@staticmethod
|
||||||
filename=logFile,
|
def setup(args: LoggerArgs):
|
||||||
filemode="w",
|
if args.verbose:
|
||||||
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
logging.basicConfig(
|
||||||
datefmt="%Y-%m-%d %H:%M:%S",
|
level=logging.DEBUG,
|
||||||
)
|
format=f"{vt100.CYAN}%(asctime)s{vt100.RESET} {vt100.YELLOW}%(levelname)s{vt100.RESET} %(name)s: %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
projectRoot = model.Project.topmost()
|
||||||
|
logFile = const.GLOBAL_LOG_FILE
|
||||||
|
if projectRoot is not None:
|
||||||
|
logFile = os.path.join(projectRoot.dirname(), const.PROJECT_LOG_FILE)
|
||||||
|
|
||||||
|
shell.mkdir(os.path.dirname(logFile))
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
filename=logFile,
|
||||||
|
filemode="w",
|
||||||
|
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MainArgs(pods.PodArgs, plugins.PluginArgs, LoggerArgs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command(None, "/", const.DESCRIPTION)
|
||||||
|
def _(args: MainArgs):
|
||||||
|
shell.mkdir(const.GLOBAL_CK_DIR)
|
||||||
|
logger.setup(args)
|
||||||
|
const.setup()
|
||||||
|
plugins.setup(args)
|
||||||
|
pods.setup(args, sys.argv[1:])
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
try:
|
try:
|
||||||
shell.mkdir(const.GLOBAL_CK_DIR)
|
cli.exec(sys.argv)
|
||||||
args = cli.parse(sys.argv[1:], cli.CutekitArgs)
|
|
||||||
setupLogger(args.verbose)
|
|
||||||
|
|
||||||
const.setup()
|
|
||||||
plugins.setup(args)
|
|
||||||
pods.setup(args, sys.argv[1:])
|
|
||||||
cli.exec(args.cmd, sys.argv[1:])
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
|
|
|
@ -350,25 +350,24 @@ def _(args: Any):
|
||||||
build(scope, component if component is not None else "all")[0]
|
build(scope, component if component is not None else "all")[0]
|
||||||
|
|
||||||
|
|
||||||
class RunArgs:
|
class RunArgs(model.RegistryArgs, shell.DebuggerArgs):
|
||||||
debug: cli.Arg[bool] = cli.Arg("d", "debug", "Enable debug mode", default=False)
|
debug = cli.Arg[bool]("d", "debug", "Enable debug mode", default=False)
|
||||||
profile: cli.Arg[bool] = cli.Arg("p", "profile", "Enable profiling", default=False)
|
profile = cli.Arg[bool]("p", "profile", "Enable profiling", default=False)
|
||||||
wait: cli.Arg[bool] = cli.Arg("w", "wait", "Wait for debugger to attach", default=False)
|
component = cli.FreeFormArg(
|
||||||
debugger: cli.Arg[str] = cli.Arg("g", "debugger", "Debugger to use", default="lldb")
|
"component", "Component to run", default="__main__"
|
||||||
mixins: cli.Arg[str] = cli.Arg("m", "mixins", "Mixins to apply", default="")
|
)
|
||||||
|
|
||||||
componentSpec: cli.FreeFormArg[str] = cli.FreeFormArg("Component to run", default="__main__")
|
|
||||||
extra: cli.RawArg
|
extra: cli.RawArg
|
||||||
|
|
||||||
|
|
||||||
@cli.command("r", "builder/run", "Run a component")
|
@cli.command("r", "builder/run", "Run a component")
|
||||||
def runCmd(args: RunArgs):
|
def runCmd(args: RunArgs):
|
||||||
scope = TargetScope.use(args, {"debug": args.debug})
|
scope = TargetScope.use(args, {"debug": args.debug})
|
||||||
|
|
||||||
component = scope.registry.lookup(
|
component = scope.registry.lookup(
|
||||||
args.componentSpec, model.Component, includeProvides=True
|
args.component, model.Component, includeProvides=True
|
||||||
)
|
)
|
||||||
if component is None:
|
if component is None:
|
||||||
raise RuntimeError(f"Component {args.componentSpec} not found")
|
raise RuntimeError(f"Component {args.component} not found")
|
||||||
|
|
||||||
product = build(scope, component)[0]
|
product = build(scope, component)[0]
|
||||||
|
|
||||||
|
@ -393,8 +392,7 @@ def runCmd(args: RunArgs):
|
||||||
def _(args: RunArgs):
|
def _(args: RunArgs):
|
||||||
# This is just a wrapper around the `run` command that try
|
# This is just a wrapper around the `run` command that try
|
||||||
# to run a special hook component named __tests__.
|
# to run a special hook component named __tests__.
|
||||||
|
args.component = "__tests__"
|
||||||
args.componentSpec = "__tests__"
|
|
||||||
runCmd(args)
|
runCmd(args)
|
||||||
|
|
||||||
|
|
||||||
|
|
128
cutekit/cli.py
128
cutekit/cli.py
|
@ -13,6 +13,7 @@ from typing import (
|
||||||
Union,
|
Union,
|
||||||
Callable,
|
Callable,
|
||||||
Generic,
|
Generic,
|
||||||
|
cast,
|
||||||
get_origin,
|
get_origin,
|
||||||
get_args,
|
get_args,
|
||||||
)
|
)
|
||||||
|
@ -40,6 +41,9 @@ class Arg(Generic[utils.T]):
|
||||||
|
|
||||||
return instance.__dict__.get(self.longName, self.default)
|
return instance.__dict__.get(self.longName, self.default)
|
||||||
|
|
||||||
|
def __set__(self, instance, value: utils.T):
|
||||||
|
instance.__dict__[self.longName] = value
|
||||||
|
|
||||||
|
|
||||||
@dt.dataclass
|
@dt.dataclass
|
||||||
class FreeFormArg(Generic[utils.T]):
|
class FreeFormArg(Generic[utils.T]):
|
||||||
|
@ -53,23 +57,30 @@ class FreeFormArg(Generic[utils.T]):
|
||||||
|
|
||||||
return instance.__dict__.get(self.longName, self.default)
|
return instance.__dict__.get(self.longName, self.default)
|
||||||
|
|
||||||
|
def __set__(self, instance, value: utils.T):
|
||||||
|
instance.__dict__[self.longName] = value
|
||||||
|
|
||||||
|
|
||||||
|
@dt.dataclass
|
||||||
|
class RawArg:
|
||||||
|
longName: str
|
||||||
|
description: str
|
||||||
|
|
||||||
|
def __get__(self, instance, owner) -> list[str]:
|
||||||
|
if instance is None:
|
||||||
|
return self # type: ignore
|
||||||
|
|
||||||
|
return instance.__dict__.get(self.longName, [])
|
||||||
|
|
||||||
|
def __set__(self, instance, value):
|
||||||
|
instance.__dict__[self.longName] = value
|
||||||
|
|
||||||
|
|
||||||
class ParserState(enum.Enum):
|
class ParserState(enum.Enum):
|
||||||
FreeForm = enum.auto()
|
FreeForm = enum.auto()
|
||||||
ShortArg = enum.auto()
|
ShortArg = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
RawArg = NewType("RawArg", str)
|
|
||||||
|
|
||||||
|
|
||||||
class CutekitArgs:
|
|
||||||
cmd: FreeFormArg[str] = FreeFormArg("Command to execute")
|
|
||||||
verbose: Arg[bool] = Arg("v", "verbose", "Enable verbose logging")
|
|
||||||
safemode: Arg[bool] = Arg("s", "safe", "Enable safe mode")
|
|
||||||
pod: Arg[bool] = Arg("p", "enable-pod", "Enable pod", default=False)
|
|
||||||
podName: Arg[str] = Arg("n", "pod-name", "The name of the pod", default="")
|
|
||||||
|
|
||||||
|
|
||||||
def parse(argv: list[str], argType: type[utils.T]) -> utils.T:
|
def parse(argv: list[str], argType: type[utils.T]) -> utils.T:
|
||||||
def set_value(options: dict[str, Any], name: str, value: Any):
|
def set_value(options: dict[str, Any], name: str, value: Any):
|
||||||
if name is not options:
|
if name is not options:
|
||||||
|
@ -183,6 +194,7 @@ def parse(argv: list[str], argType: type[utils.T]) -> utils.T:
|
||||||
raise RuntimeError(f"Missing arguments: {', '.join(missing)}")
|
raise RuntimeError(f"Missing arguments: {', '.join(missing)}")
|
||||||
|
|
||||||
for key, value in options.items():
|
for key, value in options.items():
|
||||||
|
# print(getattr(argType, key).type())
|
||||||
field_type = get_args(argType.__annotations__[key])[0]
|
field_type = get_args(argType.__annotations__[key])[0]
|
||||||
setattr(result, key, field_type(value))
|
setattr(result, key, field_type(value))
|
||||||
|
|
||||||
|
@ -210,34 +222,56 @@ class Command:
|
||||||
|
|
||||||
subcommands: dict[str, "Command"] = dt.field(default_factory=dict)
|
subcommands: dict[str, "Command"] = dt.field(default_factory=dict)
|
||||||
|
|
||||||
|
def help(self):
|
||||||
|
print(f"{self.longName} - {self.helpText}")
|
||||||
|
print()
|
||||||
|
self.usage()
|
||||||
|
|
||||||
|
def usage(self):
|
||||||
|
usage = f"Usage: {self.longName}"
|
||||||
|
|
||||||
|
if self.argType is not None:
|
||||||
|
usage += " [args...]"
|
||||||
|
|
||||||
|
if len(self.subcommands) > 0:
|
||||||
|
usage += " <command> [args...]"
|
||||||
|
|
||||||
|
print(usage)
|
||||||
|
|
||||||
|
|
||||||
commands: dict[str, Command] = {}
|
commands: dict[str, Command] = {}
|
||||||
|
rootCommand: Optional[Command] = None
|
||||||
|
|
||||||
|
|
||||||
def command(shortName: Optional[str], longName: str, helpText: str):
|
def command(shortName: Optional[str], longName: str, helpText: str):
|
||||||
curframe = inspect.currentframe()
|
curframe = inspect.currentframe()
|
||||||
calframe = inspect.getouterframes(curframe, 2)
|
calframe = inspect.getouterframes(curframe, 2)
|
||||||
|
|
||||||
def wrap(fn: Callback):
|
def wrap(fn: utils.T) -> utils.T:
|
||||||
_logger.debug(f"Registering command {longName}")
|
_logger.debug(f"Registering command {longName}")
|
||||||
if len(fn.__annotations__) == 0:
|
if len(fn.__annotations__) == 0:
|
||||||
argType = None
|
argType = None
|
||||||
else:
|
else:
|
||||||
argType = list(fn.__annotations__.values())[0]
|
argType = list(fn.__annotations__.values())[0]
|
||||||
|
|
||||||
path = longName.split("/")
|
path = longName.split("/")
|
||||||
parent = commands
|
command = Command(
|
||||||
for p in path[:-1]:
|
|
||||||
parent = parent[p].subcommands
|
|
||||||
parent[path[-1]] = Command(
|
|
||||||
shortName,
|
shortName,
|
||||||
path[-1],
|
path[-1] if longName != "/" else "/",
|
||||||
helpText,
|
helpText,
|
||||||
Path(calframe[1].filename).parent != Path(__file__).parent,
|
Path(calframe[1].filename).parent != Path(__file__).parent,
|
||||||
fn,
|
cast(Callback, fn),
|
||||||
argType,
|
argType,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if longName == "/":
|
||||||
|
global rootCommand
|
||||||
|
rootCommand = command
|
||||||
|
else:
|
||||||
|
parent = commands
|
||||||
|
for p in path[:-1]:
|
||||||
|
parent = parent[p].subcommands
|
||||||
|
parent[path[-1]] = command
|
||||||
|
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
return wrap
|
return wrap
|
||||||
|
@ -327,17 +361,49 @@ def versionCmd():
|
||||||
print(f"CuteKit v{const.VERSION_STR}")
|
print(f"CuteKit v{const.VERSION_STR}")
|
||||||
|
|
||||||
|
|
||||||
def exec(cmd: str, args: list[str], cmds: dict[str, Command] = commands):
|
def _exec(args: list[str], cmd: Command):
|
||||||
for c in cmds.values():
|
# let's slice the arguments for this command and the sub command
|
||||||
if c.shortName == cmd or c.longName == cmd:
|
# [-a -b] [sub-cmd -c -d]
|
||||||
if len(c.subcommands) > 0:
|
|
||||||
exec(args[0], args[1:], c.subcommands)
|
selfArgs = []
|
||||||
return
|
if len(cmd.subcommands) > 0:
|
||||||
else:
|
while len(args) > 0 and args[0].startswith("-"):
|
||||||
if c.argType is not None:
|
selfArgs.append(args.pop(0))
|
||||||
c.callback(parse(args[1:], c.argType)) # type: ignore
|
else:
|
||||||
else:
|
selfArgs = args
|
||||||
c.callback() # type: ignore
|
|
||||||
|
if "-h" in selfArgs or "--help" in selfArgs:
|
||||||
|
cmd.help()
|
||||||
|
return
|
||||||
|
|
||||||
|
if "-u" in selfArgs or "--usage" in selfArgs:
|
||||||
|
cmd.usage()
|
||||||
|
return
|
||||||
|
|
||||||
|
if cmd.argType is not None:
|
||||||
|
parsedArgs = parse(selfArgs, cmd.argType)
|
||||||
|
cmd.callback(parsedArgs) # type: ignore
|
||||||
|
else:
|
||||||
|
cmd.callback() # type: ignore
|
||||||
|
|
||||||
|
if cmd.subcommands:
|
||||||
|
if len(args) == 0:
|
||||||
|
error("Missing subcommand")
|
||||||
|
cmd.usage()
|
||||||
|
return
|
||||||
|
|
||||||
|
for c in cmd.subcommands.values():
|
||||||
|
if c.shortName == args[0] or c.longName == args[0]:
|
||||||
|
_exec(args, c)
|
||||||
return
|
return
|
||||||
|
|
||||||
raise RuntimeError(f"Unknown command {cmd}")
|
raise RuntimeError(f"Unknown command {args[0]}")
|
||||||
|
|
||||||
|
|
||||||
|
def exec(args: list[str]):
|
||||||
|
if not rootCommand:
|
||||||
|
raise RuntimeError("No root command")
|
||||||
|
|
||||||
|
rootCommand.longName = Path(args[0]).name
|
||||||
|
rootCommand.subcommands = commands
|
||||||
|
_exec(args[1:], rootCommand)
|
||||||
|
|
|
@ -83,15 +83,20 @@ def view(
|
||||||
g.view(filename=os.path.join(target.builddir, "graph.gv"))
|
g.view(filename=os.path.join(target.builddir, "graph.gv"))
|
||||||
|
|
||||||
|
|
||||||
class GraphCmd:
|
class GraphCmd(model.TargetArgs):
|
||||||
mixins: cli.Arg[str] = cli.Arg("m", "mixins", "Mixins to apply", default="")
|
scope = cli.Arg("s", "scope", "Scope to show", default="")
|
||||||
scope: cli.Arg[str] = cli.Arg("s", "scope", "Scope to show", default="")
|
onlyLibs = cli.Arg("l", "only-libs", "Only show libraries", default=False)
|
||||||
onlyLibs: cli.Arg[bool] = cli.Arg("l", "only-libs", "Only show libraries", default=False)
|
showDisabled = cli.Arg(
|
||||||
showDisabled: cli.Arg[bool] = cli.Arg("d", "show-disabled", "Show disabled components", default=False)
|
"d", "show-disabled", "Show disabled components", default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@cli.command("g", "graph", "Show the dependency graph")
|
@cli.command("g", "graph", "Show the dependency graph")
|
||||||
def _(args: GraphCmd):
|
def _(args: GraphCmd):
|
||||||
registry = model.Registry.use(args)
|
view(
|
||||||
target = model.Target.use(args)
|
model.Registry.use(args),
|
||||||
|
model.Target.use(args),
|
||||||
view(registry, target, scope=args.scope, showExe=not args.onlyLibs, showDisabled=args.showDisabled)
|
scope=args.scope,
|
||||||
|
showExe=not args.onlyLibs,
|
||||||
|
showDisabled=args.showDisabled,
|
||||||
|
)
|
||||||
|
|
|
@ -190,18 +190,15 @@ def _():
|
||||||
|
|
||||||
|
|
||||||
class InitArgs:
|
class InitArgs:
|
||||||
repo: cli.Arg[str] = cli.Arg(
|
repo = cli.Arg(
|
||||||
"r",
|
"r",
|
||||||
"repo",
|
"repo",
|
||||||
"Repository to use for templates",
|
"Repository to use for templates",
|
||||||
default=const.DEFAULT_REPO_TEMPLATES,
|
default=const.DEFAULT_REPO_TEMPLATES,
|
||||||
)
|
)
|
||||||
list: cli.Arg[bool] = cli.Arg(
|
list = cli.Arg("l", "list", "List available templates", default=False)
|
||||||
"l", "list", "List available templates", default=False
|
template = cli.FreeFormArg("template", "Template to use")
|
||||||
)
|
name = cli.FreeFormArg("name", "Name of the project")
|
||||||
|
|
||||||
template: cli.FreeFormArg[str] = cli.FreeFormArg("Template to use")
|
|
||||||
name: cli.FreeFormArg[Optional[str]] = cli.FreeFormArg("Name of the project")
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("I", "model/init", "Initialize a new project")
|
@cli.command("I", "model/init", "Initialize a new project")
|
||||||
|
@ -274,8 +271,8 @@ DEFAULT_TOOLS: Tools = {
|
||||||
|
|
||||||
|
|
||||||
class RegistryArgs:
|
class RegistryArgs:
|
||||||
mixins: cli.Arg[str] = cli.Arg("m", "mixins", "Mixins to apply", default="")
|
mixins = cli.Arg[list[str]]("m", "mixins", "Mixins to apply", default=[])
|
||||||
# props: cli.Arg[dict[str]] = cli.Arg("p", "props", "Properties to set", default="")
|
# props = cli.Arg[dict[str]]("p", "props", "Properties to set", default="")
|
||||||
|
|
||||||
|
|
||||||
class TargetArgs(RegistryArgs):
|
class TargetArgs(RegistryArgs):
|
||||||
|
|
|
@ -51,6 +51,10 @@ def loadAll():
|
||||||
load(os.path.join(pluginDir, files))
|
load(os.path.join(pluginDir, files))
|
||||||
|
|
||||||
|
|
||||||
def setup(args: cli.CutekitArgs):
|
class PluginArgs:
|
||||||
|
safemode = cli.Arg[bool]("s", "safemode", "Skip loading plugins", default=False)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(args: PluginArgs):
|
||||||
if not args.safemode:
|
if not args.safemode:
|
||||||
loadAll()
|
loadAll()
|
||||||
|
|
|
@ -77,7 +77,12 @@ IMAGES: dict[str, Image] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def setup(args: cli.CutekitArgs, argv: list[str]):
|
class PodArgs:
|
||||||
|
pod = cli.Arg[bool]("p", "enable-pod", "Enable pod", default=False)
|
||||||
|
podName = cli.Arg[str]("n", "pod-name", "The name of the pod", default="")
|
||||||
|
|
||||||
|
|
||||||
|
def setup(args: PodArgs, argv: list[str]):
|
||||||
"""
|
"""
|
||||||
Reincarnate cutekit within a docker container, this is
|
Reincarnate cutekit within a docker container, this is
|
||||||
useful for cross-compiling
|
useful for cross-compiling
|
||||||
|
@ -118,12 +123,15 @@ def tryDecode(data: Optional[bytes], default: str = "") -> str:
|
||||||
return data.decode()
|
return data.decode()
|
||||||
|
|
||||||
|
|
||||||
class PodArgs:
|
class PodCreateArgs:
|
||||||
name: cli.Arg[str] = cli.Arg("n", "name", "The name of the pod to use", default=defaultPodName)
|
name: cli.Arg[str] = cli.Arg(
|
||||||
image = cli.Arg("i", "image", "The image to use", default=defaultPodImage)
|
"n", "name", "The name of the pod to use", default="default"
|
||||||
|
)
|
||||||
|
image = cli.Arg[str]("i", "image", "The image to use", default=defaultPodImage)
|
||||||
|
|
||||||
|
|
||||||
@cli.command("c", "pod/create", "Create a new pod")
|
@cli.command("c", "pod/create", "Create a new pod")
|
||||||
def _(args: PodArgs):
|
def _(args: PodCreateArgs):
|
||||||
"""
|
"""
|
||||||
Create a new development pod with cutekit installed and the current
|
Create a new development pod with cutekit installed and the current
|
||||||
project mounted at /project
|
project mounted at /project
|
||||||
|
@ -177,7 +185,10 @@ def _(args: PodArgs):
|
||||||
|
|
||||||
|
|
||||||
class KillPodArgs:
|
class KillPodArgs:
|
||||||
name: cli.Arg[str] = cli.Arg("n", "name", "The name of the pod to kill", default=defaultPodName)
|
name: cli.Arg[str] = cli.Arg(
|
||||||
|
"n", "name", "The name of the pod to kill", default=defaultPodName
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@cli.command("k", "pod/kill", "Stop and remove a pod")
|
@cli.command("k", "pod/kill", "Stop and remove a pod")
|
||||||
def _(args: KillPodArgs):
|
def _(args: KillPodArgs):
|
||||||
|
@ -211,9 +222,10 @@ def _():
|
||||||
|
|
||||||
|
|
||||||
class PodExecArgs:
|
class PodExecArgs:
|
||||||
name: cli.Arg[str] = cli.Arg("n", "name", "The name of the pod to use", default=defaultPodName)
|
name = cli.Arg("n", "name", "The name of the pod to use", default=defaultPodName)
|
||||||
cmd: cli.FreeFormArg[str] = cli.FreeFormArg("The command to execute")
|
cmd = cli.FreeFormArg("cmd", "The command to execute", default="/bin/bash")
|
||||||
extra: cli.RawArg
|
args = cli.FreeFormArg("args", "The arguments to pass to the command")
|
||||||
|
|
||||||
|
|
||||||
@cli.command("e", "pod/exec", "Execute a command in a pod")
|
@cli.command("e", "pod/exec", "Execute a command in a pod")
|
||||||
def podExecCmd(args: PodExecArgs):
|
def podExecCmd(args: PodExecArgs):
|
||||||
|
@ -223,7 +235,7 @@ def podExecCmd(args: PodExecArgs):
|
||||||
name = args.name
|
name = args.name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shell.exec("docker", "exec", "-it", name, args.cmd, *args.extra)
|
shell.exec("docker", "exec", "-it", name, args.cmd, *args.args)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise RuntimeError(f"Pod '{name[len(podPrefix):]}' does not exist")
|
raise RuntimeError(f"Pod '{name[len(podPrefix):]}' does not exist")
|
||||||
|
|
||||||
|
|
|
@ -395,35 +395,51 @@ def compress(path: str, dest: Optional[str] = None, format: str = "zstd") -> str
|
||||||
|
|
||||||
|
|
||||||
@cli.command("s", "scripts", "Manage scripts")
|
@cli.command("s", "scripts", "Manage scripts")
|
||||||
def _(args: cli.Args):
|
def _():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DebugArgs:
|
class DebuggerArgs:
|
||||||
debugger = cli.Arg[str](
|
debugger = cli.Arg[str](
|
||||||
"d", "debugger", "Debugger to use (lldb, gdb)", default="lldb"
|
"d", "debugger", "Debugger to use (lldb, gdb)", default="lldb"
|
||||||
)
|
)
|
||||||
wait = cli.Arg[bool]("w", "wait", "Wait for debugger to attach")
|
wait = cli.Arg[bool]("w", "wait", "Wait for debugger to attach")
|
||||||
extra = cli.RawArg
|
|
||||||
|
|
||||||
|
class DebugArgs(DebuggerArgs):
|
||||||
|
cmd = cli.FreeFormArg[str]("cmd", "Command to debug", default="a.out")
|
||||||
|
args = cli.RawArg("args", "Arguments to pass to the command")
|
||||||
|
|
||||||
|
|
||||||
@cli.command("d", "debug", "Debug a program")
|
@cli.command("d", "debug", "Debug a program")
|
||||||
def _(args: DebugArgs):
|
def _(args: DebugArgs):
|
||||||
wait = args.consumeOpt("wait", False) is True
|
command = [args.cmd, *args.args]
|
||||||
debugger = args.consumeOpt("debugger", "lldb")
|
debug(command, args.debugger, args.wait)
|
||||||
command = [str(args.consumeArg()), *args.extra]
|
|
||||||
debug(command, debugger=debugger, wait=wait)
|
|
||||||
|
class ProfilerArgs:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileArgs:
|
||||||
|
cmd = cli.FreeFormArg[str]("cmd", "Command to profile", default="a.out")
|
||||||
|
extra: cli.RawArg
|
||||||
|
|
||||||
|
|
||||||
@cli.command("p", "profile", "Profile a program")
|
@cli.command("p", "profile", "Profile a program")
|
||||||
def _(args: cli.Args):
|
def _(args: ProfileArgs):
|
||||||
command = [str(args.consumeArg()), *args.extra]
|
command = [args.cmd, *args.extra]
|
||||||
profile(command)
|
profile(command)
|
||||||
|
|
||||||
|
|
||||||
|
class CompressArgs:
|
||||||
|
dest = cli.Arg[str]("d", "dest", "Destination file")
|
||||||
|
format = cli.Arg[str](
|
||||||
|
"f", "format", "Compression format (zip, zstd, gzip)", default="zstd"
|
||||||
|
)
|
||||||
|
path = cli.FreeFormArg[str]("path", "Path to compress")
|
||||||
|
|
||||||
|
|
||||||
@cli.command("c", "compress", "Compress a file or directory")
|
@cli.command("c", "compress", "Compress a file or directory")
|
||||||
def _(args: cli.Args):
|
def _(args: CompressArgs):
|
||||||
path = str(args.consumeArg())
|
compress(args.path, args.dest, args.format)
|
||||||
dest = args.consumeOpt("dest", None)
|
|
||||||
format = args.consumeOpt("format", "zstd")
|
|
||||||
compress(path, dest, format)
|
|
||||||
|
|
Loading…
Reference in a new issue