Update
This commit is contained in:
parent
cc88242cef
commit
ea0e5613e8
|
@ -11,6 +11,7 @@ from . import (
|
|||
plugins,
|
||||
pods, # noqa: F401 this is imported for side effects
|
||||
vt100,
|
||||
shell,
|
||||
)
|
||||
|
||||
|
||||
|
@ -40,10 +41,7 @@ def setupLogger(verbose: bool):
|
|||
if projectRoot is not None:
|
||||
logFile = os.path.join(projectRoot.dirname(), const.PROJECT_LOG_FILE)
|
||||
|
||||
# create the directory if it doesn't exist
|
||||
logDir = os.path.dirname(logFile)
|
||||
if not os.path.isdir(logDir):
|
||||
os.makedirs(logDir)
|
||||
shell.mkdir(os.path.dirname(logFile))
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
|
@ -56,21 +54,21 @@ def setupLogger(verbose: bool):
|
|||
|
||||
def main() -> int:
|
||||
try:
|
||||
shell.mkdir(const.GLOBAL_CK_DIR)
|
||||
args = cli.parse(sys.argv[1:])
|
||||
setupLogger(args.consumeOpt("verbose", False) is True)
|
||||
safemode = args.consumeOpt("safemode", False) is True
|
||||
if not safemode:
|
||||
plugins.loadAll()
|
||||
pods.reincarnate(args)
|
||||
|
||||
const.setup()
|
||||
plugins.setup(args)
|
||||
pods.setup(args)
|
||||
cli.exec(args)
|
||||
print()
|
||||
|
||||
return 0
|
||||
except RuntimeError as e:
|
||||
logging.exception(e)
|
||||
cli.error(str(e))
|
||||
cli.usage()
|
||||
print()
|
||||
return 1
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
return 1
|
||||
|
||||
return 1
|
||||
|
|
62
cutekit/bootstrap.sh
Normal file
62
cutekit/bootstrap.sh
Normal file
|
@ -0,0 +1,62 @@
|
|||
#!/bin/env bash
|
||||
# version: {version}
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$CUTEKIT_NOVENV" == "1" ]; then
|
||||
echo "CUTEKIT_NOVENV is set, skipping virtual environment setup."
|
||||
exec cutekit $@
|
||||
fi
|
||||
|
||||
if [ "$1" == "tools" -a "$2" == "nuke" ]; then
|
||||
rm -rf .cutekit/tools .cutekit/venv
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -f .cutekit/tools/ready ]; then
|
||||
if [ ! \( "$1" == "tools" -a "$2" == "setup" \) ]; then
|
||||
echo "CuteKit is not installed."
|
||||
echo "This script will install cutekit into $PWD/.cutekit"
|
||||
|
||||
read -p "Do you want to continue? [Y/n] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Aborting."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Installing CuteKit..."
|
||||
fi
|
||||
|
||||
mkdir -p .cutekit
|
||||
if [ ! -d .cutekit/venv ]; then
|
||||
echo "Setting up Python virtual environment..."
|
||||
python3 -m venv .cutekit/venv
|
||||
fi
|
||||
source .cutekit/venv/bin/activate
|
||||
|
||||
echo "Downloading CuteKit..."
|
||||
if [ ! -d .cutekit/tools/cutekit ]; then
|
||||
git clone --depth 1 https://github.com/cute-engineering/cutekit .cutekit/tools/cutekit --branch "0.7-dev"
|
||||
else
|
||||
echo "CuteKit already downloaded."
|
||||
fi
|
||||
|
||||
echo "Installing Tools..."
|
||||
pip3 install -e .cutekit/tools/cutekit
|
||||
pip3 install -r meta/plugins/requirements.txt
|
||||
|
||||
touch .cutekit/tools/ready
|
||||
echo "Done!"
|
||||
fi
|
||||
|
||||
if [ "$1" == "tools" -a "$2" == "setup" ]; then
|
||||
echo "Tools already installed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
source .cutekit/venv/bin/activate
|
||||
export PATH="$PATH:.cutekit/venv/bin"
|
||||
|
||||
cutekit $@
|
||||
|
|
@ -11,7 +11,23 @@ _logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
@dt.dataclass
|
||||
class TargetScope:
|
||||
class Scope:
|
||||
registry: model.Registry
|
||||
|
||||
@staticmethod
|
||||
def use(args: cli.Args) -> "Scope":
|
||||
registry = model.Registry.use(args)
|
||||
return Scope(registry)
|
||||
|
||||
def key(self) -> str:
|
||||
return self.registry.project.id
|
||||
|
||||
def openTargetScope(self, t: model.Target):
|
||||
return TargetScope(self.registry, t)
|
||||
|
||||
|
||||
@dt.dataclass
|
||||
class TargetScope(Scope):
|
||||
registry: model.Registry
|
||||
target: model.Target
|
||||
|
||||
|
@ -21,6 +37,9 @@ class TargetScope:
|
|||
target = model.Target.use(args)
|
||||
return TargetScope(registry, target)
|
||||
|
||||
def key(self) -> str:
|
||||
return super().key() + "/" + self.target.id + "/" + self.target.hashid
|
||||
|
||||
def openComponentScope(self, c: model.Component):
|
||||
return ComponentScope(self.registry, self.target, c)
|
||||
|
||||
|
@ -29,6 +48,9 @@ class TargetScope:
|
|||
class ComponentScope(TargetScope):
|
||||
component: model.Component
|
||||
|
||||
def key(self) -> str:
|
||||
return super().key() + "/" + self.component.id
|
||||
|
||||
def openComponentScope(self, c: model.Component):
|
||||
return ComponentScope(self.registry, self.target, c)
|
||||
|
||||
|
@ -76,6 +98,8 @@ def _computeCinc(scope: TargetScope) -> str:
|
|||
for c in scope.registry.iterEnabled(scope.target):
|
||||
if "cpp-root-include" in c.props:
|
||||
res.add(c.dirname())
|
||||
elif "cpp-excluded" in c.props:
|
||||
pass
|
||||
elif c.type == model.Kind.LIB:
|
||||
res.add(str(Path(c.dirname()).parent))
|
||||
|
||||
|
@ -108,18 +132,16 @@ def buildpath(scope: ComponentScope, path) -> Path:
|
|||
|
||||
|
||||
def subdirs(scope: ComponentScope) -> list[str]:
|
||||
registry = scope.registry
|
||||
target = scope.target
|
||||
component = scope.component
|
||||
result = [component.dirname()]
|
||||
|
||||
for subs in component.subdirs:
|
||||
result.append(os.path.join(component.dirname(), subs))
|
||||
|
||||
for inj in component.resolved[target.id].injected:
|
||||
injected = registry.lookup(inj, model.Component)
|
||||
assert injected is not None # model.Resolver has already checked this
|
||||
result.extend(subdirs(scope))
|
||||
# for inj in component.resolved[target.id].injected:
|
||||
# injected = registry.lookup(inj, model.Component)
|
||||
# assert injected is not None # model.Resolver has already checked this
|
||||
# result.extend(subdirs(scope.openComponentScope(injected)))
|
||||
|
||||
return result
|
||||
|
||||
|
@ -134,13 +156,31 @@ def compile(
|
|||
res: list[str] = []
|
||||
for src in srcs:
|
||||
rel = Path(src).relative_to(scope.component.dirname())
|
||||
dest = buildpath(scope, "obj") / rel.with_suffix(".o")
|
||||
dest = buildpath(scope, path="obj") / rel.with_suffix(".o")
|
||||
t = scope.target.tools[rule]
|
||||
w.build(str(dest), rule, inputs=src, order_only=t.files)
|
||||
res.append(str(dest))
|
||||
return res
|
||||
|
||||
|
||||
def compileObjs(w: ninja.Writer, scope: ComponentScope) -> list[str]:
|
||||
objs = []
|
||||
objs += compile(w, scope, "cc", wilcard(scope, ["*.c"]))
|
||||
objs += compile(
|
||||
w,
|
||||
scope,
|
||||
"cxx",
|
||||
wilcard(scope, ["*.cpp", "*.cc", "*.cxx"]),
|
||||
)
|
||||
objs += compile(
|
||||
w,
|
||||
scope,
|
||||
"as",
|
||||
wilcard(scope, ["*.s", "*.asm", "*.S"]),
|
||||
)
|
||||
return objs
|
||||
|
||||
|
||||
# --- Ressources ------------------------------------------------------------- #
|
||||
|
||||
|
||||
|
@ -195,26 +235,12 @@ def link(
|
|||
w.newline()
|
||||
out = outfile(scope)
|
||||
|
||||
objs = []
|
||||
objs += compile(w, scope, "cc", wilcard(scope, ["*.c"]))
|
||||
objs += compile(
|
||||
w,
|
||||
scope,
|
||||
"cxx",
|
||||
wilcard(scope, ["*.cpp", "*.cc", "*.cxx"]),
|
||||
)
|
||||
objs += compile(
|
||||
w,
|
||||
scope,
|
||||
"as",
|
||||
wilcard(scope, ["*.s", "*.asm", "*.S"]),
|
||||
)
|
||||
|
||||
res = compileRes(w, scope)
|
||||
libs = collectLibs(scope)
|
||||
objs = compileObjs(w, scope)
|
||||
if scope.component.type == model.Kind.LIB:
|
||||
w.build(out, "ar", objs, implicit=res)
|
||||
else:
|
||||
libs = collectLibs(scope)
|
||||
w.build(out, "ld", objs + libs, implicit=res)
|
||||
return out
|
||||
|
||||
|
@ -300,12 +326,12 @@ def build(
|
|||
|
||||
|
||||
@cli.command("b", "builder", "Build/Run/Clean a component or all components")
|
||||
def buildCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
pass
|
||||
|
||||
|
||||
@cli.command("b", "builder/build", "Build a component or all components")
|
||||
def buildBuildCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
scope = TargetScope.use(args)
|
||||
componentSpec = args.consumeArg()
|
||||
component = None
|
||||
|
@ -315,7 +341,7 @@ def buildBuildCmd(args: cli.Args):
|
|||
|
||||
|
||||
@cli.command("r", "builder/run", "Run a component")
|
||||
def buildRunCmd(args: cli.Args):
|
||||
def runCmd(args: cli.Args):
|
||||
scope = TargetScope.use(args)
|
||||
debug = args.consumeOpt("debug", False) is True
|
||||
|
||||
|
@ -337,28 +363,28 @@ def buildRunCmd(args: cli.Args):
|
|||
|
||||
|
||||
@cli.command("t", "builder/test", "Run all test targets")
|
||||
def buildTestCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
# This is just a wrapper around the `run` command that try
|
||||
# to run a special hook component named __tests__.
|
||||
args.args.insert(0, "__tests__")
|
||||
buildRunCmd(args)
|
||||
runCmd(args)
|
||||
|
||||
|
||||
@cli.command("d", "builder/debug", "Debug a component")
|
||||
def buildDebugCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
# This is just a wrapper around the `run` command that
|
||||
# always enable debug mode.
|
||||
args.opts["debug"] = True
|
||||
buildRunCmd(args)
|
||||
runCmd(args)
|
||||
|
||||
|
||||
@cli.command("c", "builder/clean", "Clean build files")
|
||||
def buildCleanCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
model.Project.use(args)
|
||||
shell.rmrf(const.BUILD_DIR)
|
||||
|
||||
|
||||
@cli.command("n", "builder/nuke", "Clean all build files and caches")
|
||||
def buildNukeCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
model.Project.use(args)
|
||||
shell.rmrf(const.PROJECT_CK_DIR)
|
||||
|
|
|
@ -1,6 +1,20 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
class Uninitialized:
|
||||
def __repr__(self):
|
||||
raise Exception("Uninitialized constant")
|
||||
|
||||
def __str__(self):
|
||||
raise Exception("Uninitialized constant")
|
||||
|
||||
def __bool__(self):
|
||||
raise Exception("Uninitialized constant")
|
||||
|
||||
|
||||
VERSION = (0, 7, 0, "dev")
|
||||
VERSION_STR = f"{VERSION[0]}.{VERSION[1]}.{VERSION[2]}{'-' + VERSION[3] if len(VERSION) >= 4 else ''}"
|
||||
MODULE_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
|
@ -16,4 +30,17 @@ TARGETS_DIR = os.path.join(META_DIR, "targets")
|
|||
DEFAULT_REPO_TEMPLATES = "cute-engineering/cutekit-templates"
|
||||
DESCRIPTION = "A build system and package manager for low-level software development"
|
||||
PROJECT_LOG_FILE = os.path.join(PROJECT_CK_DIR, "cutekit.log")
|
||||
GLOBAL_LOG_FILE = os.path.join(os.path.expanduser("~"), ".cutekit", "cutekit.log")
|
||||
GLOBAL_LOG_FILE: str = os.path.join(os.path.expanduser("~"), ".cutekit", "cutekit.log")
|
||||
HOSTID: str | Uninitialized = Uninitialized()
|
||||
|
||||
|
||||
def setup():
|
||||
global HOSTID
|
||||
hostIdPath = GLOBAL_CK_DIR + "/hostid"
|
||||
if os.path.exists(hostIdPath):
|
||||
with open(hostIdPath, "r") as f:
|
||||
HOSTID = f.read().strip()
|
||||
else:
|
||||
HOSTID = utils.randomHash()
|
||||
with open(hostIdPath, "w") as f:
|
||||
f.write(HOSTID)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#!/bin/env bash
|
||||
|
||||
# This script makes sure that the virtual environment is
|
||||
# set up and that the plugins requirements are installed.
|
||||
|
||||
set -e
|
||||
|
||||
export PY=python3.11
|
||||
|
@ -30,5 +33,6 @@ else
|
|||
source /tools/venv/bin/activate
|
||||
fi
|
||||
|
||||
cd /project
|
||||
export PYTHONPATH=/tools
|
||||
$PY -m cutekit "$@"
|
||||
$PY -m cutekit $@
|
||||
|
|
|
@ -46,7 +46,7 @@ def view(
|
|||
|
||||
g.node(
|
||||
component.id,
|
||||
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.decription, 40,newline='<BR/>')}>",
|
||||
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.description, 40,newline='<BR/>')}>",
|
||||
shape=shape,
|
||||
style="filled",
|
||||
fillcolor=fillcolor,
|
||||
|
@ -67,7 +67,7 @@ def view(
|
|||
elif showDisabled:
|
||||
g.node(
|
||||
component.id,
|
||||
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.decription, 40,newline='<BR/>')}<BR/><BR/><I>{vt100.wordwrap(str(component.resolved[target.id].reason), 40,newline='<BR/>')}</I>>",
|
||||
f"<<B>{component.id}</B><BR/>{vt100.wordwrap(component.description, 40,newline='<BR/>')}<BR/><BR/><I>{vt100.wordwrap(str(component.resolved[target.id].reason), 40,newline='<BR/>')}</I>>",
|
||||
shape="plaintext",
|
||||
style="filled",
|
||||
fontcolor="#999999",
|
||||
|
@ -84,7 +84,7 @@ def view(
|
|||
|
||||
|
||||
@cli.command("g", "graph", "Show the dependency graph")
|
||||
def graphCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
registry = model.Registry.use(args)
|
||||
target = model.Target.use(args)
|
||||
|
||||
|
|
|
@ -174,18 +174,18 @@ class Project(Manifest):
|
|||
|
||||
|
||||
@cli.command("m", "model", "Manage the model")
|
||||
def modelCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
pass
|
||||
|
||||
|
||||
@cli.command("i", "model/install", "Install required external packages")
|
||||
def modelInstallCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
project = Project.use(args)
|
||||
Project.fetchs(project.extern)
|
||||
|
||||
|
||||
@cli.command("I", "model/init", "Initialize a new project")
|
||||
def modelInitCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
import requests
|
||||
|
||||
repo = args.consumeOpt("repo", const.DEFAULT_REPO_TEMPLATES)
|
||||
|
@ -275,7 +275,10 @@ class Target(Manifest):
|
|||
|
||||
@property
|
||||
def builddir(self) -> str:
|
||||
return os.path.join(const.BUILD_DIR, f"{self.id}-{self.hashid[:8]}")
|
||||
postfix = f"-{self.hashid[:8]}"
|
||||
if self.props.get("host"):
|
||||
postfix += f"-{str(const.HOSTID)[:8]}"
|
||||
return os.path.join(const.BUILD_DIR, f"{self.id}{postfix}")
|
||||
|
||||
@staticmethod
|
||||
def use(args: cli.Args) -> "Target":
|
||||
|
@ -310,7 +313,7 @@ class Resolved:
|
|||
|
||||
@dt.dataclass
|
||||
class Component(Manifest):
|
||||
decription: str = dt.field(default="(No description)")
|
||||
description: str = dt.field(default="(No description)")
|
||||
props: Props = dt.field(default_factory=dict)
|
||||
tools: Tools = dt.field(default_factory=dict)
|
||||
enableIf: dict[str, list[Any]] = dt.field(default_factory=dict)
|
||||
|
@ -593,7 +596,7 @@ class Registry(DataClassJsonMixin):
|
|||
for c in r.iter(Component):
|
||||
if c.isEnabled(target)[0]:
|
||||
for inject in c.injects:
|
||||
victim = r.lookup(inject, Component)
|
||||
victim = r.lookup(inject, Component, includeProvides=True)
|
||||
if not victim:
|
||||
raise RuntimeError(f"Cannot find component '{inject}'")
|
||||
victim.resolved[target.id].injected.append(c.id)
|
||||
|
@ -622,7 +625,7 @@ class Registry(DataClassJsonMixin):
|
|||
|
||||
|
||||
@cli.command("l", "model/list", "List all components and targets")
|
||||
def modelListCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
registry = Registry.use(args)
|
||||
|
||||
components = list(registry.iter(Component))
|
||||
|
|
|
@ -2,7 +2,7 @@ import logging
|
|||
import os
|
||||
import sys
|
||||
|
||||
from . import shell, model, const
|
||||
from . import shell, model, const, cli
|
||||
|
||||
import importlib.util as importlib
|
||||
|
||||
|
@ -43,3 +43,8 @@ def loadAll():
|
|||
for files in shell.readdir(pluginDir):
|
||||
if files.endswith(".py"):
|
||||
load(os.path.join(pluginDir, files))
|
||||
|
||||
|
||||
def setup(args: cli.Args):
|
||||
if not bool(args.consumeOpt("safemode", False)):
|
||||
loadAll()
|
||||
|
|
|
@ -7,7 +7,7 @@ from . import cli, model, shell, vt100
|
|||
|
||||
|
||||
podPrefix = "CK__"
|
||||
projectRoot = "/self"
|
||||
projectRoot = "/project"
|
||||
toolingRoot = "/tools"
|
||||
defaultPodName = f"{podPrefix}default"
|
||||
defaultPodImage = "ubuntu"
|
||||
|
@ -16,8 +16,9 @@ defaultPodImage = "ubuntu"
|
|||
@dt.dataclass
|
||||
class Image:
|
||||
id: str
|
||||
like: str
|
||||
image: str
|
||||
init: list[str]
|
||||
setup: list[str]
|
||||
|
||||
|
||||
@dt.dataclass
|
||||
|
@ -28,6 +29,7 @@ class Pod:
|
|||
|
||||
IMAGES: dict[str, Image] = {
|
||||
"ubuntu": Image(
|
||||
"ubuntu",
|
||||
"ubuntu",
|
||||
"ubuntu:jammy",
|
||||
[
|
||||
|
@ -36,6 +38,7 @@ IMAGES: dict[str, Image] = {
|
|||
],
|
||||
),
|
||||
"debian": Image(
|
||||
"debian",
|
||||
"debian",
|
||||
"debian:bookworm",
|
||||
[
|
||||
|
@ -44,6 +47,7 @@ IMAGES: dict[str, Image] = {
|
|||
],
|
||||
),
|
||||
"alpine": Image(
|
||||
"alpine",
|
||||
"alpine",
|
||||
"alpine:3.18",
|
||||
[
|
||||
|
@ -52,6 +56,7 @@ IMAGES: dict[str, Image] = {
|
|||
],
|
||||
),
|
||||
"arch": Image(
|
||||
"arch",
|
||||
"arch",
|
||||
"archlinux:latest",
|
||||
[
|
||||
|
@ -60,6 +65,7 @@ IMAGES: dict[str, Image] = {
|
|||
],
|
||||
),
|
||||
"fedora": Image(
|
||||
"fedora",
|
||||
"fedora",
|
||||
"fedora:39",
|
||||
[
|
||||
|
@ -70,7 +76,7 @@ IMAGES: dict[str, Image] = {
|
|||
}
|
||||
|
||||
|
||||
def reincarnate(args: cli.Args):
|
||||
def setup(args: cli.Args):
|
||||
"""
|
||||
Reincarnate cutekit within a docker container, this is
|
||||
useful for cross-compiling
|
||||
|
@ -95,6 +101,7 @@ def reincarnate(args: cli.Args):
|
|||
"-it",
|
||||
pod,
|
||||
"/tools/cutekit/entrypoint.sh",
|
||||
"--reincarnated",
|
||||
*args.args,
|
||||
)
|
||||
sys.exit(0)
|
||||
|
@ -103,15 +110,15 @@ def reincarnate(args: cli.Args):
|
|||
|
||||
|
||||
@cli.command("p", "pod", "Manage pods")
|
||||
def podCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
pass
|
||||
|
||||
|
||||
@cli.command("c", "pod/create", "Create a new pod")
|
||||
def podCreateCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
"""
|
||||
Create a new development pod with cutekit installed and the current
|
||||
project mounted at /self
|
||||
project mounted at /project
|
||||
"""
|
||||
project = model.Project.ensure()
|
||||
|
||||
|
@ -144,7 +151,7 @@ def podCreateCmd(args: cli.Args):
|
|||
)
|
||||
|
||||
print(f"Initializing pod '{name[len(podPrefix) :]}'...")
|
||||
for cmd in image.init:
|
||||
for cmd in image.setup:
|
||||
print(vt100.p(cmd))
|
||||
exitCode, ouput = container.exec_run(f"/bin/bash -c '{cmd}'", demux=True)
|
||||
if exitCode != 0:
|
||||
|
@ -154,7 +161,7 @@ def podCreateCmd(args: cli.Args):
|
|||
|
||||
|
||||
@cli.command("k", "pod/kill", "Stop and remove a pod")
|
||||
def podKillCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
client = docker.from_env()
|
||||
name = str(args.consumeOpt("name", defaultPodName))
|
||||
if not name.startswith(podPrefix):
|
||||
|
@ -170,13 +177,13 @@ def podKillCmd(args: cli.Args):
|
|||
|
||||
|
||||
@cli.command("s", "pod/shell", "Open a shell in a pod")
|
||||
def podShellCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
args.args.insert(0, "/bin/bash")
|
||||
podExecCmd(args)
|
||||
|
||||
|
||||
@cli.command("l", "pod/list", "List all pods")
|
||||
def podListCmd(args: cli.Args):
|
||||
def _(args: cli.Args):
|
||||
client = docker.from_env()
|
||||
hasPods = False
|
||||
for container in client.containers.list(all=True):
|
||||
|
|
|
@ -10,7 +10,7 @@ import fnmatch
|
|||
import platform
|
||||
import logging
|
||||
import tempfile
|
||||
|
||||
import dataclasses as dt
|
||||
|
||||
from typing import Optional
|
||||
from . import const
|
||||
|
@ -18,15 +18,13 @@ from . import const
|
|||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dt.dataclass
|
||||
class Uname:
|
||||
def __init__(
|
||||
self, sysname: str, nodename: str, release: str, version: str, machine: str
|
||||
):
|
||||
self.sysname = sysname
|
||||
self.nodename = nodename
|
||||
self.release = release
|
||||
self.version = version
|
||||
self.machine = machine
|
||||
sysname: str
|
||||
nodename: str
|
||||
release: str
|
||||
version: str
|
||||
machine: str
|
||||
|
||||
|
||||
def uname() -> Uname:
|
||||
|
@ -131,12 +129,13 @@ def wget(url: str, path: Optional[str] = None) -> str:
|
|||
return path
|
||||
|
||||
|
||||
def exec(*args: str, quiet: bool = False) -> bool:
|
||||
def exec(*args: str, quiet: bool = False, cwd: str | None = None) -> bool:
|
||||
_logger.debug(f"Executing {args}")
|
||||
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
args,
|
||||
cwd=cwd,
|
||||
stdout=sys.stdout if not quiet else subprocess.PIPE,
|
||||
stderr=sys.stderr if not quiet else subprocess.PIPE,
|
||||
)
|
||||
|
@ -274,3 +273,10 @@ def which(cmd: str) -> Optional[str]:
|
|||
Find the path of a command
|
||||
"""
|
||||
return shutil.which(cmd)
|
||||
|
||||
|
||||
def nproc() -> int:
|
||||
"""
|
||||
Return the number of processors
|
||||
"""
|
||||
return os.cpu_count() or 1
|
||||
|
|
|
@ -29,6 +29,10 @@ def hash(
|
|||
return hashlib.sha256(data.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def randomHash() -> str:
|
||||
return hashlib.sha256(os.urandom(32)).hexdigest()
|
||||
|
||||
|
||||
def camelCase(s: str) -> str:
|
||||
s = "".join(x for x in s.title() if x != "_" and x != "-")
|
||||
s = s[0].lower() + s[1:]
|
||||
|
|
|
@ -11,7 +11,7 @@ For example you can add a new command to the CLI:
|
|||
from cutekit import cli
|
||||
|
||||
@cli.command("h", "hello", "Print hello world")
|
||||
def bootCmd(args: cli.Args) -> None:
|
||||
def _(args: cli.Args) -> None:
|
||||
print("Hello world!")
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in a new issue