more package support

This commit is contained in:
Waylon S. Walker 2025-04-09 09:15:00 -05:00
parent a60562b7fc
commit 9737746923
5 changed files with 283 additions and 11 deletions

87
krayt/bundles.py Normal file
View file

@ -0,0 +1,87 @@
"""
Bundles of packages available in most package managers.
"""
basics = [
"curl",
"wget",
"jq",
"yq",
"bash",
"coreutils",
]
pretty = [
*basics
"starship",
"atuin",
"bash",
"zsh",
"fish",
"bat",
"eza",
]
networking = [
*basics
"mtr",
"bind-tools",
"aws-cli",
"curl",
"wget",
"iperf3",
"nmap",
"traceroute",
"netcat-openbsd",
]
database = [
*basics
"sqlite",
"sqlite-dev",
"sqlite-libs",
"postgresql",
"mysql",
"mariadb",
"redis",
"mongodb",
]
storage = [
*basics
"ncdu",
"dust",
"file",
"hexyl",
"ripgrep",
"fd",
"fzf",
"difftastic",
]
search = [
*basics
"ripgrep",
"fd",
"fzf",
"difftastic",
]
monitoring = [
*basics
"htop",
"bottom",
"mtr",
]
all = list(
set(
[
*basics,
*pretty,
*networking,
*database,
*storage,
*search,
*monitoring,
]
)
)

View file

@ -6,7 +6,7 @@ import os
from pathlib import Path from pathlib import Path
import time import time
import typer import typer
from typing import Any, Optional from typing import Any, List, Optional
import yaml import yaml
KRAYT_VERSION = "NIGHTLY" KRAYT_VERSION = "NIGHTLY"
@ -324,6 +324,11 @@ def create_inspector_job(
volumes: list, volumes: list,
image: str = "alpine:latest", image: str = "alpine:latest",
imagepullsecret: Optional[str] = None, imagepullsecret: Optional[str] = None,
additional_packages: Optional[List[str]] = None,
pre_init_scripts: Optional[List[str]] = None,
post_init_scripts: Optional[List[str]] = None,
pre_init_hooks: Optional[List[str]] = None,
post_init_hooks: Optional[List[str]] = None,
): ):
"""Create a Krayt inspector job with the given mounts""" """Create a Krayt inspector job with the given mounts"""
timestamp = int(time.time()) timestamp = int(time.time())

166
krayt/package.py Normal file
View file

@ -0,0 +1,166 @@
from more_itertools import unique_everseen
from pydantic import BaseModel, BeforeValidator, model_validator
from typing import Annotated, List, Literal, Optional
from typing_extensions import Self
SUPPORTED_KINDS = {
"system",
"uv",
"installer",
"i",
"curlbash",
"curlsh",
"brew",
"cargo",
"pipx",
"npm",
"go",
"gh",
}
def validate_kind(v):
if v not in SUPPORTED_KINDS:
raise ValueError(
f"Unknown installer kind: {v}\n Supported kinds: {SUPPORTED_KINDS}\n "
)
return v
class Package(BaseModel):
"""
Represents a package to be installed, either via system package manager
or an alternative installer like uv, installer.sh, brew, etc.
"""
kind: Annotated[
Literal[
"system",
"uv",
"i",
"curlsh",
"brew",
"cargo",
"pipx",
"npm",
"go",
"gh",
],
BeforeValidator(validate_kind),
] = "system"
dependencies: Optional[List[str]] = None
value: str
@classmethod
def from_raw(cls, raw: str) -> "Package":
"""
Parse a raw input string like 'uv:copier' into a Package(kind='uv', value='copier')
"""
if ":" in raw:
prefix, value = raw.split(":", 1)
return cls(kind=prefix.strip(), value=value.strip())
else:
return cls(kind="system", value=raw.strip())
@model_validator(mode="after")
def validate_dependencies(self) -> Self:
if self.dependencies:
return self
else:
if self.kind == "system":
return self
dependencies = []
if self.kind in ["uv", "i", "installer", "curlbash", "curlsh", "gh"]:
dependencies.extend(
[
Package.from_raw("curl"),
]
)
if self.kind == "brew":
dependencies.extend(
[
Package.from_raw("brew"),
Package.from_raw("git"),
]
)
if self.kind == "cargo":
dependencies.extend(
[
Package.from_raw("cargo"),
]
)
if self.kind == "pipx":
dependencies.extend(
[
Package.from_raw("pipx"),
]
)
if self.kind == "npm":
dependencies.extend(
[
Package.from_raw("npm"),
]
)
if self.kind == "go":
dependencies.extend(
[
Package.from_raw("go"),
]
)
self.dependencies = dependencies
return self
def __str__(self):
return f"{self.kind}:{self.value}" if self.kind != "system" else self.value
def is_system(self) -> bool:
return self.kind == "system"
def install_command(self) -> str:
"""
Generate the bash install command snippet for this package.
"""
if self.kind == "system":
return f"detect_package_manager_and_install {self.value}"
elif self.kind == "uv":
return f"uv tool install {self.value}"
elif self.kind in ["i", "installer", "gh"]:
return f"curl -fsSL https://i.jpillora.com/{self.value} | sh"
elif self.kind == "curlsh":
return f"curl -fsSL {self.value} | sh"
elif self.kind == "curlbash":
return f"curl -fsSL {self.value} | bash"
elif self.kind == "brew":
return f"brew install {self.value}"
elif self.kind == "cargo":
return f"cargo install {self.value}"
elif self.kind == "pipx":
return f"pipx install {self.value}"
elif self.kind == "npm":
return f"npm install -g {self.value}"
elif self.kind == "go":
return f"go install {self.value}@latest"
else:
raise ValueError(f"Unknown install method for kind={self.kind}")
if __name__ == "__main__":
raw_inputs = [
"curl",
"wget",
"uv:copier",
"i:sharkdp/fd",
"curlsh:https://example.com/install.sh",
"brew:bat",
]
packages = [Package.from_raw(raw) for raw in raw_inputs]
dependencies = []
for package in packages:
if package.dependencies:
dependencies.extend(
[dependency.install_command() for dependency in package.dependencies]
)
installs = [package.install_command() for package in packages]
print("\n".join(install for install in unique_everseen([*dependencies, *installs])))

View file

@ -1,7 +1,7 @@
{% if additional_packages %} {% if additional_packages %}
detect_package_manager_and_install_command() { detect_package_manager_and_install() {
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
echo "Usage: detect_package_manager_and_install_command <package1> [package2] [...]" echo "Usage: detect_package_manager_and_install <package1> [package2] [...]"
return 1 return 1
fi fi
@ -34,19 +34,32 @@ detect_package_manager_and_install_command() {
return 2 return 2
fi fi
PACKAGES="$*" echo "Using package manager: $PKG_MANAGER"
if [ -n "$UPDATE_CMD" ]; then if [ -n "$UPDATE_CMD" ]; then
echo "$UPDATE_CMD echo "Running package manager update..."
echo $INSTALL_CMD $PACKAGES" eval "$UPDATE_CMD"
$UPDATE_CMD fi
$INSTALL_CMD $PACKAGES
FAILED_PKGS=()
for pkg in "$@"; do
echo "Installing package: $pkg"
if ! eval "$INSTALL_CMD $pkg"; then
echo "⚠️ Warning: Failed to install package: $pkg"
FAILED_PKGS+=("$pkg")
fi
done
if [ ${#FAILED_PKGS[@]} -ne 0 ]; then
echo "⚠️ The following packages failed to install:"
for failed_pkg in "${FAILED_PKGS[@]}"; do
echo " - $failed_pkg"
done
else else
echo "$INSTALL_CMD $PACKAGES" echo "✅ All requested packages installed successfully."
$INSTALL_CMD $PACKAGES
fi fi
} }
detect_package_manager_and_install_command {% for package in additional_packages %}{{ package | trim }}{% if not loop.last %} {% endif %}{% endfor %} detect_package_manager_and_install {% for package in additional_packages %}{{ package | trim }}{% if not loop.last %} {% endif %}{% endfor %}
{% endif %} {% endif %}

View file

@ -31,6 +31,7 @@ dependencies = [
"InquirerPy", "InquirerPy",
"jinja2", "jinja2",
"iterfzf", "iterfzf",
"pydantic",
] ]
[[project.authors]] [[project.authors]]