Refactor package list to Python module with dynamic versions #1

Merged
JMR-dev merged 3 commits from claude/refactor-packages-python-20kcR into main 2026-05-18 14:10:36 +00:00
3 changed files with 458 additions and 190 deletions
+342 -102
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
"""
Bootstrap packages listed in formatted_packages.txt.
Bootstrap packages declared in formatted_packages.py.
Sections handled:
=== System Packages === — installed via dnf or apt-get
=== Flatpak Packages === — installed via flatpak from Flathub
=== Custom Installed Packages === — downloaded, verified, extracted
System Packages — installed via dnf or apt-get
Flatpak Packages — installed via flatpak from Flathub (skipped with --no-gui)
Custom Packages — downloaded, verified, extracted
Usage:
sudo python3 bootstrap_environment.py [--only system|flatpak|custom] [--no-gui]
@@ -32,8 +32,9 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Optional
import formatted_packages
SCRIPT_DIR = Path(__file__).parent
PACKAGES_FILE = SCRIPT_DIR / "formatted_packages.txt"
RUN_LOG = SCRIPT_DIR / "bootstrap_run.log"
# ── issue log ─────────────────────────────────────────────────────────────────
@@ -118,6 +119,11 @@ ARCH = detect_arch()
_ARCH_GO = {"x86_64": "amd64", "aarch64": "arm64"}
_ARCH_MINIKUBE = {"x86_64": "amd64", "aarch64": "arm64"}
_ARCH_DEB = {"x86_64": "amd64", "aarch64": "arm64"}
_ARCH_NVIM = {"x86_64": "x86_64", "aarch64": "arm64"}
def _url_format(template: str, version: Optional[str]) -> str:
"""Interpolate {version}, {arch}, {arch_go} into a URL template."""
return template.format(version=version or "", arch=ARCH, arch_go=_ARCH_GO[ARCH])
def _arch_matches(name: str, arch: str = ARCH) -> bool:
n = name.lower()
@@ -152,6 +158,22 @@ def detect_pkg_mgr() -> str:
PKG_MGR = detect_pkg_mgr()
def _is_rhel_family() -> bool:
"""True for RHEL-derived distros (Fedora, RHEL, CentOS, Rocky, Alma, ...)."""
try:
data = Path("/etc/os-release").read_text()
except OSError:
return PKG_MGR == "dnf"
tokens: list[str] = []
for line in data.splitlines():
if line.startswith(("ID=", "ID_LIKE=")):
_, _, val = line.partition("=")
tokens.extend(val.strip().strip('"').split())
return any(t in {"rhel", "fedora", "centos", "rocky", "almalinux"} for t in tokens)
IS_RHEL_FAMILY = _is_rhel_family()
# ── network helpers ───────────────────────────────────────────────────────────
def _download(url: str, dest: Path) -> bool:
@@ -174,6 +196,14 @@ def _fetch_json(url: str) -> Optional[dict]:
err(f"API request failed for {url}: {e}")
return None
def _fetch_text(url: str) -> Optional[str]:
try:
with urllib.request.urlopen(url) as resp:
return resp.read().decode().strip()
except (urllib.error.URLError, OSError) as e:
err(f"Fetch failed for {url}: {e}")
return None
# ── installation checks ───────────────────────────────────────────────────────
def is_system_pkg_installed(pkg: str) -> bool:
@@ -625,32 +655,62 @@ def install_flatpak_packages(to_install: list[str]) -> None:
@dataclass
class CustomPackage:
name: str
url: Optional[str] = None # archive download URL (not required for all install methods)
sha256: Optional[str] = None # hex digest to compare against
sha256_url: Optional[str] = None # URL to a minisig file
minisign_key: Optional[str] = None # base64 public key for minisign verification
install_path: Optional[str] = None # override the default install-check path
version: Optional[str] = None # pinned fallback version
url_template: Optional[str] = None # uses {version}, {arch}, {arch_go}
sha256: Optional[str] = None # hex digest of the pinned archive
sha256_url_template: Optional[str] = None # template for a .minisig URL
minisign_key: Optional[str] = None # base64 public key for minisign verification
fetch_latest: Optional[str] = None # latest-version resolver hint
install_path: Optional[str] = None # override the default install-check path
@property
def url(self) -> Optional[str]:
return _url_format(self.url_template, self.version) if self.url_template else None
@property
def sha256_url(self) -> Optional[str]:
return (
_url_format(self.sha256_url_template, self.version)
if self.sha256_url_template else None
)
@property
def display_name(self) -> str:
return f"{self.name}-{self.version}" if self.version else self.name
_DEFAULT_INSTALL_PATHS: dict[str, Path] = {
"go": Path("/usr/local/go"),
"firecracker": Path("/usr/local/bin/firecracker"),
"zig": Path("/usr/local/bin/zig"),
"nvm": Path("~/.nvm"),
"pyenv": Path("~/.pyenv"),
"neovim": Path(f"/opt/nvim-linux-{_ARCH_NVIM[ARCH]}"),
"oh-my-zsh": Path("~/.oh-my-zsh"),
}
def _default_install_path(pkg: CustomPackage) -> Optional[Path]:
n = pkg.name.lower()
if n.startswith("go-"):
return Path("/usr/local/go")
if "firecracker" in n:
return Path("/usr/local/bin/firecracker")
if "zig" in n:
return Path("/usr/local/bin/zig")
if n == "nvm":
return Path("~/.nvm")
if n == "pyenv":
return Path("~/.pyenv")
if n == "neovim":
return Path("/opt/nvim-linux-x86_64")
return None
return _DEFAULT_INSTALL_PATHS.get(pkg.name.lower())
def _pip_installed() -> bool:
if not has_cmd("python3"):
return False
return subprocess.run(
["python3", "-m", "pip", "--version"],
capture_output=True, check=False,
).returncode == 0
def is_custom_pkg_installed(pkg: CustomPackage) -> tuple[bool, Optional[Path]]:
"""Return (is_installed, check_path)."""
"""Return (is_installed, check_path).
For most custom packages the check is a filesystem path. ``pip`` is the
exception: it ships inside a Python distribution rather than at a known
path, so it's detected by running ``python3 -m pip --version``.
"""
if pkg.name.lower() == "pip":
return _pip_installed(), None
raw = Path(pkg.install_path) if pkg.install_path else _default_install_path(pkg)
if raw is None:
return False, None
@@ -741,8 +801,7 @@ def _install_firecracker(archive: Path, tmp: Path) -> None:
def _install_zig(pkg: CustomPackage, archive: Path, tmp: Path) -> None:
parent = Path("/usr/local")
version = pkg.name.split("-", 1)[1] # "zig-0.16.0" → "0.16.0"
zig_dir = parent / f"zig-{version}"
zig_dir = parent / f"zig-{pkg.version}"
if zig_dir.exists():
run(["rm", "-rf", str(zig_dir)], as_sudo=True)
run(["tar", "-C", str(parent), "-xJf", str(archive)], as_sudo=True)
@@ -762,6 +821,72 @@ def _install_pyenv() -> None:
print(" pyenv installed to ~/.pyenv")
def _install_pip() -> None:
"""Install pip via Python's bundled ``ensurepip`` module, then self-upgrade.
Unlike the other custom packages, pip ships inside CPython itself and is
bootstrapped from the wheel in the standard library rather than downloaded.
"""
if not has_cmd("python3"):
err("python3 is not installed — cannot install pip")
return
print(" Bootstrapping pip via 'python3 -m ensurepip --upgrade' ...")
bootstrap = run(
["python3", "-m", "ensurepip", "--upgrade"],
as_sudo=True, check=False,
)
if bootstrap.returncode != 0:
err("python3 -m ensurepip failed (system Python may need a distro 'python3-pip' package)")
return
print(" Upgrading pip to the latest version ...")
upgrade = run(
["python3", "-m", "pip", "install", "--upgrade", "pip"],
as_sudo=True, check=False,
)
if upgrade.returncode != 0:
warn("pip self-upgrade failed (likely PEP 668 externally-managed); "
"ensurepip-provided pip remains")
def _install_oh_my_zsh() -> None:
"""Install oh-my-zsh via its official installer and force ZSH_THEME=gnzh."""
if not has_cmd("zsh"):
err("zsh is not installed — required by oh-my-zsh")
return
if not has_cmd("git"):
err("git is not installed — required by oh-my-zsh")
return
target = Path.home() / ".oh-my-zsh"
if target.exists():
print(f" oh-my-zsh already present at {target}; updating theme only")
else:
print(" Installing oh-my-zsh via the official installer ...")
installer = (
'sh -c "$(curl -fsSL '
'https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" '
'"" --unattended'
)
if shell(installer, check=False).returncode != 0:
err("oh-my-zsh installer failed")
return
zshrc = Path.home() / ".zshrc"
if not zshrc.exists():
warn("~/.zshrc not present after oh-my-zsh install; cannot set theme")
return
text = zshrc.read_text()
new_text, replaced = re.subn(r'^\s*ZSH_THEME=.*$', 'ZSH_THEME="gnzh"', text, flags=re.M)
if replaced == 0:
new_text = text.rstrip() + '\nZSH_THEME="gnzh"\n'
if new_text != text:
zshrc.write_text(new_text)
print(' Set ZSH_THEME="gnzh" in ~/.zshrc')
else:
print(' ~/.zshrc already has ZSH_THEME="gnzh"')
def _install_nvm() -> None:
data = _fetch_json("https://api.github.com/repos/nvm-sh/nvm/releases/latest")
if data is None:
@@ -780,7 +905,8 @@ def _install_neovim(pkg: CustomPackage, tmp: Path) -> None:
if data is None:
return
asset_name = "nvim-linux-x86_64.tar.gz"
arch_token = _ARCH_NVIM[ARCH]
asset_name = f"nvim-linux-{arch_token}.tar.gz"
asset = next((a for a in data["assets"] if a["name"] == asset_name), None)
if asset is None:
err(f"Neovim asset {asset_name} not found")
@@ -802,17 +928,18 @@ def _install_neovim(pkg: CustomPackage, tmp: Path) -> None:
return
print(" SHA256 OK")
print(" Extracting Neovim to /opt ...")
run(["rm", "-rf", "/opt/nvim-linux-x86_64"], as_sudo=True)
install_dir = f"/opt/nvim-linux-{arch_token}"
print(f" Extracting Neovim to /opt ...")
run(["rm", "-rf", install_dir], as_sudo=True)
run(["tar", "-C", "/opt", "-xzf", str(dest)], as_sudo=True)
profile_line = 'export PATH="$PATH:/opt/nvim-linux-x86_64/bin"'
profile_line = f'export PATH="$PATH:{install_dir}/bin"'
profile_script = "/etc/profile.d/neovim.sh"
run(["bash", "-c",
f"grep -qxF {profile_line!r} {profile_script} 2>/dev/null || "
f"echo {profile_line!r} >> {profile_script}"],
as_sudo=True, check=False)
print(f" Neovim installed to /opt/nvim-linux-x86_64")
print(f" Neovim installed to {install_dir}")
def _clone_nvim_config() -> None:
@@ -847,6 +974,55 @@ def _clone_nvim_config() -> None:
print(f" Neovim configuration ready at {config_dir}")
def _invoking_user() -> str:
"""User whose login shell / home we should target.
When the script is run via sudo, SUDO_USER is the original invoker;
otherwise the current process user is correct.
"""
return os.environ.get("SUDO_USER") or getpass.getuser()
def ensure_zsh_default() -> None:
"""Make zsh the default login shell for the invoking user.
Uses ``usermod -s`` on RHEL-family distros and ``chsh -s`` elsewhere —
on Debian/Ubuntu ``chsh`` is the canonical (and PAM-permitted) path,
while on RHEL/Fedora ``chsh`` for another user often fails under the
default authselect config and ``usermod`` is the reliable alternative.
"""
if not has_cmd("zsh"):
warn("zsh not installed — skipping default-shell change")
return
zsh_path = shutil.which("zsh") or "/bin/zsh"
user = _invoking_user()
import pwd
try:
current = pwd.getpwnam(user).pw_shell
except KeyError:
warn(f"user {user} not found in passwd; skipping default-shell change")
return
if current == zsh_path:
print(f"\n[zsh] {user}'s default shell is already {zsh_path}.")
return
family = "RHEL-family" if IS_RHEL_FAMILY else "Debian-family"
print(f"\n[zsh] Setting default shell for {user} to {zsh_path} ({family}) ...")
if IS_RHEL_FAMILY:
cmd = ["usermod", "-s", zsh_path, user]
else:
cmd = ["chsh", "-s", zsh_path, user]
if run(cmd, as_sudo=True, check=False).returncode != 0:
err(f"Failed to set default shell to zsh for {user}")
else:
print(f"[zsh] Default shell updated. Log out and back in for it to take effect.")
def ensure_node_lts() -> None:
"""If nvm is present, ensure Node LTS is installed and set as the default."""
nvm_dir = Path.home() / ".nvm"
@@ -962,16 +1138,110 @@ def ensure_python_latest() -> Optional[threading.Thread]:
return t
def _resolve_latest_go(pkg: CustomPackage) -> Optional[tuple[str, str]]:
releases = _fetch_json("https://go.dev/dl/?mode=json")
if not releases:
return None
latest = releases[0] if isinstance(releases, list) else releases
raw_version = latest.get("version", "")
version = raw_version[2:] if raw_version.startswith("go") else raw_version
if not version:
return None
archive_name = f"go{version}.linux-{_ARCH_GO[ARCH]}.tar.gz"
entry = next(
(f for f in latest.get("files", [])
if f.get("filename") == archive_name and f.get("kind") == "archive"),
None,
)
if not entry or not entry.get("sha256"):
return None
return version, entry["sha256"]
def _resolve_latest_firecracker(pkg: CustomPackage) -> Optional[tuple[str, str]]:
data = _fetch_json(
"https://api.github.com/repos/firecracker-microvm/firecracker/releases/latest"
)
if not data:
return None
version = data.get("tag_name", "").lstrip("v")
if not version:
return None
archive_name = f"firecracker-v{version}-{ARCH}.tgz"
sha_asset = next(
(a for a in data.get("assets", []) if a["name"] == f"{archive_name}.sha256.txt"),
None,
)
if not sha_asset:
return None
sha = _fetch_text(sha_asset["browser_download_url"])
if not sha:
return None
return version, sha.split()[0]
def _resolve_latest_zig(_pkg: CustomPackage) -> Optional[tuple[str, str]]:
data = _fetch_json("https://ziglang.org/download/index.json")
if not data:
return None
stable = [v for v in data.keys() if v != "master" and re.match(r"^\d+\.\d+\.\d+$", v)]
if not stable:
return None
stable.sort(key=lambda v: tuple(int(x) for x in v.split(".")))
version = stable[-1]
entry = data[version].get(f"{ARCH}-linux")
if not entry or "shasum" not in entry:
return None
return version, entry["shasum"]
_LATEST_RESOLVERS = {
"go": _resolve_latest_go,
"firecracker": _resolve_latest_firecracker,
"zig": _resolve_latest_zig,
}
def _resolve_latest(pkg: CustomPackage) -> None:
"""Best-effort upgrade pkg.version/sha256 to the latest release.
On any failure, logs a warning and leaves the pinned values in place.
Clears sha256_url_template when sha256 is overridden so the dynamic
digest is what gets verified.
"""
resolver = _LATEST_RESOLVERS.get(pkg.fetch_latest or "")
if resolver is None:
return
print(f" Checking latest version for {pkg.name} ...")
try:
result = resolver(pkg)
except Exception as e: # noqa: BLE001 — best-effort lookup, any failure is logged
warn(f"{pkg.name}: latest-version lookup raised {e!r}; "
f"falling back to pinned version {pkg.version}")
return
if result is None:
warn(f"{pkg.name}: could not resolve latest version; "
f"falling back to pinned version {pkg.version}")
return
latest_version, latest_sha = result
if latest_version == pkg.version:
print(f" Pinned version {pkg.version} is already the latest.")
return
print(f" Latest is {latest_version} (pinned was {pkg.version}); using latest.")
pkg.version = latest_version
pkg.sha256 = latest_sha
pkg.sha256_url_template = None # prefer the freshly resolved sha256
def install_custom_packages(to_install: list[CustomPackage]) -> None:
print("\n=== Custom Packages ===")
for pkg in to_install:
_, check_path = is_custom_pkg_installed(pkg)
print(f"\n Installing {pkg.name} ..."
+ (f" (install path: {check_path})" if check_path else ""))
if check_path is None:
warn(f"{pkg.name}: no known install path — script will not detect future installs")
name_lower = pkg.name.lower()
_, check_path = is_custom_pkg_installed(pkg)
print(f"\n Installing {pkg.display_name} ..."
+ (f" (install path: {check_path})" if check_path else ""))
if check_path is None and name_lower != "pip":
warn(f"{pkg.name}: no known install path — script will not detect future installs")
# Handlers that manage their own download/install
if name_lower == "nvm":
@@ -980,11 +1250,19 @@ def install_custom_packages(to_install: list[CustomPackage]) -> None:
if name_lower == "pyenv":
_install_pyenv()
continue
if name_lower == "pip":
_install_pip()
continue
if name_lower == "oh-my-zsh":
_install_oh_my_zsh()
continue
if name_lower == "neovim":
with tempfile.TemporaryDirectory() as tmp_str:
_install_neovim(pkg, Path(tmp_str))
continue
_resolve_latest(pkg)
if not pkg.url:
warn(f"No URL or install handler for '{pkg.name}' — skipping")
continue
@@ -999,11 +1277,11 @@ def install_custom_packages(to_install: list[CustomPackage]) -> None:
continue
if not _verify(archive, pkg):
continue
if name_lower.startswith("go-"):
if name_lower == "go":
_install_go(archive)
elif "firecracker" in name_lower:
elif name_lower == "firecracker":
_install_firecracker(archive, tmp)
elif "zig" in name_lower:
elif name_lower == "zig":
_install_zig(pkg, archive, tmp)
else:
warn(f"No install handler for '{pkg.name}' — skipping")
@@ -1096,10 +1374,10 @@ def print_check_summary(sys_c: dict, flat_c: dict, cust_c: dict, only: Optional[
ok = cust_c["already_installed"]
print("\nCustom packages:")
for pkg, path in ok:
print(f" [OK] {pkg.name} ({path})")
print(f" [OK] {pkg.display_name}" + (f" ({path})" if path else ""))
for pkg in to:
_, path = is_custom_pkg_installed(pkg)
print(f" [INSTALL] {pkg.name}" + (f" → {path}" if path else ""))
print(f" [INSTALL] {pkg.display_name}" + (f" → {path}" if path else ""))
total += len(to)
return total
@@ -1154,79 +1432,29 @@ def check_and_setup_ssh() -> None:
err("gh auth login failed — skipping key upload.")
return
# ── file parser ───────────────────────────────────────────────────────────────
# ── packages module loader ────────────────────────────────────────────────────
def parse_packages_file(path: Path) -> tuple[list[str], list[str], list[CustomPackage]]:
system_pkgs: list[str] = []
flatpak_pkgs: list[str] = []
custom_pkgs: list[CustomPackage] = []
current: dict = {}
section: Optional[str] = None
def flush_custom():
if "name" in current:
custom_pkgs.append(CustomPackage(**current))
current.clear()
for raw in path.read_text().splitlines():
line = raw.strip()
if line == "=== System Packages ===":
section = "system"
elif line == "=== Flatpak Packages ===":
flush_custom()
section = "flatpak"
elif line == "=== Custom Installed Packages ===":
flush_custom()
section = "custom"
elif not line:
if section == "custom":
flush_custom()
elif line.startswith("==="):
pass
elif section == "system":
system_pkgs.append(line)
elif section == "flatpak":
flatpak_pkgs.append(line)
elif section == "custom" and " - " in line:
key, _, val = line.partition(" - ")
key = key.strip().lower().replace(" ", "_")
val = val.strip()
if key == "name":
current["name"] = val
elif key == "url":
current["url"] = val
elif key == "sha256":
current["sha256"] = val
elif key == "sha256_url_tar":
current["sha256_url"] = val
elif key == "minisign_key":
current["minisign_key"] = val
elif key == "install_path":
current["install_path"] = val
flush_custom()
def load_packages() -> tuple[list[str], list[str], list[CustomPackage]]:
system_pkgs = list(formatted_packages.SYSTEM_PACKAGES)
flatpak_pkgs = list(formatted_packages.FLATPAK_PACKAGES)
custom_pkgs = [CustomPackage(**spec) for spec in formatted_packages.CUSTOM_PACKAGES]
return system_pkgs, flatpak_pkgs, custom_pkgs
# ── entry point ───────────────────────────────────────────────────────────────
def main() -> None:
ap = argparse.ArgumentParser(
description="Bootstrap packages from formatted_packages.txt"
description="Bootstrap packages declared in formatted_packages.py"
)
ap.add_argument("--only", choices=["system", "flatpak", "custom"],
help="Install only the named section")
ap.add_argument("--no-gui", action="store_true",
help="Skip GUI applications (suitable for headless environments). "
"Excludes GUI system packages and the entire Flatpak section.")
ap.add_argument("--file", default=str(PACKAGES_FILE), metavar="PATH",
help="Path to packages file (default: formatted_packages.txt next to this script)")
"Excludes GUI system packages and skips the entire Flatpak "
"section, including installing flatpak itself.")
args = ap.parse_args()
pkg_file = Path(args.file)
if not pkg_file.exists():
sys.exit(f"Packages file not found: {pkg_file}")
system_pkgs, flatpak_pkgs, custom_pkgs = parse_packages_file(pkg_file)
system_pkgs, flatpak_pkgs, custom_pkgs = load_packages()
print(f"Architecture: {ARCH}")
print(f"Package manager: {PKG_MGR}")
@@ -1241,6 +1469,8 @@ def main() -> None:
print(f" [NO-GUI] Skipping GUI system packages: {_fmt(skipped_gui)}")
flatpak_pkgs = []
# --no-gui suppresses the Flatpak section entirely (both `flatpak` itself
# and the Flathub apps), even when --only=flatpak is requested.
do_flatpak = args.only in (None, "flatpak") and not args.no_gui
sys_c = check_system_packages(system_pkgs) if args.only in (None, "system") else {}
@@ -1266,6 +1496,7 @@ def main() -> None:
if args.only in (None, "system"):
install_system_packages(sys_c["to_install_regular"], sys_c["to_install_special"])
ensure_zsh_default()
if do_flatpak:
install_flatpak_packages(flat_c["to_install"])
@@ -1288,6 +1519,15 @@ def main() -> None:
write_run_log()
print("\nDone.")
# Final step (user-requested): source ~/.zshrc.
# This runs in a subshell, so it only validates the rc file — the user's
# interactive shell is unaffected and they'll need to open a new terminal
# (or 'exec zsh') to pick up the new default shell.
zshrc = Path.home() / ".zshrc"
if has_cmd("zsh") and zshrc.exists():
print("\nSourcing ~/.zshrc ...")
shell(f"zsh -c 'source {zshrc}'", check=False)
if __name__ == "__main__":
main()
+116
View File
@@ -0,0 +1,116 @@
"""Package definitions consumed by bootstrap_environment.py.
System and Flatpak packages are flat lists of names.
Custom packages declare a URL template plus an optional ``fetch_latest`` hint.
At install time the bootstrap script will attempt to look up the most recent
release and fall back to the pinned (version, sha256) tuple on failure.
URL templates use ``str.format`` with the following substitutions:
{version} pkg.version (or the latest resolved version)
{arch} "x86_64" or "aarch64"
{arch_go} Go-style: "amd64" or "arm64"
"""
SYSTEM_PACKAGES: list[str] = [
"ansible",
"ansible-core",
"aria2",
"bashtop",
"build-essential",
"buildah",
"containerd.io",
"docker-buildx-plugin",
"docker-ce-cli",
"docker-ce-rootless-extras",
"docker-ce",
"docker-compose-plugin",
"dotnet-sdk-10.0",
"ffmpeg-free",
"gcc",
"gh",
"git",
"github-desktop",
"google-chrome-stable",
"lua",
"minisign",
"minikube",
"obs-studio",
"obsidian",
"pipx",
"poetry",
"podman",
"qemu",
"restic",
"rg",
"shutter",
"temurin-25-jdk",
"vagrant",
"virt-manager",
"vivaldi-stable",
"webcamoid",
"wireshark",
"yt-dlp",
"zoom",
"zsh",
"bzip2",
"bzip2-devel",
"gdbm-libs",
"libffi-devel",
"libnsl2",
"libuuid-devel",
"libzstd-devel",
"make",
"openssl-devel",
"patch",
"readline-devel",
"sqlite",
"sqlite-devel",
"tk-devel",
"xz-devel",
"zlib-devel",
]
FLATPAK_PACKAGES: list[str] = [
"com.obsproject.Studio",
"fr.handbrake.ghb",
"io.github.webcamoid.Webcamoid",
"one.ablaze.floorp",
"com.vivaldi.Vivaldi",
"org.darktable.Darktable",
]
CUSTOM_PACKAGES: list[dict] = [
{
"name": "go",
"version": "1.26.3",
"url_template": "https://go.dev/dl/go{version}.linux-{arch_go}.tar.gz",
"sha256": "2b2cfc7148493da5e73981bffbf3353af381d5f93e789c82c79aff64962eb556",
"fetch_latest": "go",
},
{"name": "neovim"},
{
"name": "firecracker",
"version": "1.15.1",
"url_template": (
"https://github.com/firecracker-microvm/firecracker/releases/download/"
"v{version}/firecracker-v{version}-{arch}.tgz"
),
"sha256": "d4a32ab2322d887ca1bc4a4e7afa9cc35393e6362dfc2b3becb389d362e4275a",
"fetch_latest": "firecracker",
},
{
"name": "zig",
"version": "0.16.0",
"url_template": "https://ziglang.org/download/{version}/zig-{arch}-linux-{version}.tar.xz",
"sha256_url_template": (
"https://ziglang.org/download/{version}/zig-{arch}-linux-{version}.tar.xz.minisig"
),
"minisign_key": "RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U",
"fetch_latest": "zig",
},
{"name": "nvm"},
{"name": "pyenv"},
{"name": "pip"},
{"name": "oh-my-zsh"},
]
-88
View File
@@ -1,88 +0,0 @@
=== System Packages ===
ansible
ansible-core
aria2
bashtop
build-essential
buildah
containerd.io
docker-buildx-plugin
docker-ce-cli
docker-ce-rootless-extras
docker-ce
docker-compose-plugin
dotnet-sdk-10.0
ffmpeg-free
gcc
gh
git
github-desktop
google-chrome-stable
lua
minisign
minikube
obs-studio
obsidian
pipx
poetry
podman
qemu
restic
rg
shutter
temurin-25-jdk
vagrant
virt-manager
vivaldi-stable
webcamoid
wireshark
yt-dlp
zoom
bzip2
bzip2-devel
gdbm-libs
libffi-devel
libnsl2
libuuid-devel
libzstd-devel
make
openssl-devel
patch
readline-devel
sqlite
sqlite-devel
tk-devel
xz-devel
zlib-devel
=== Flatpak Packages ===
com.obsproject.Studio
fr.handbrake.ghb
io.github.webcamoid.Webcamoid
one.ablaze.floorp
com.vivaldi.Vivaldi
org.darktable.Darktable
=== Custom Installed Packages ===
Name - go-1.26.3
URL - https://go.dev/dl/go1.26.3.linux-amd64.tar.gz
SHA256 - 2b2cfc7148493da5e73981bffbf3353af381d5f93e789c82c79aff64962eb556
Name - neovim
URL - https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz
Name - firecracker-v1.15.1
URL - https://github.com/firecracker-microvm/firecracker/releases/download/v1.15.1/firecracker-v1.15.1-x86_64.tgz
SHA256 - d4a32ab2322d887ca1bc4a4e7afa9cc35393e6362dfc2b3becb389d362e4275a
Name - zig-0.16.0
URL - https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz
SHA256_URL_TAR - https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz.minisig
MINISIGN_KEY - RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U
Name - nvm
Name - pyenv