99 lines
4.7 KiB
Python
Executable File
99 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Generate disjoint, concern-scoped package lists from the current machine."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Final
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
PACKAGES = ROOT / "packages"
|
|
|
|
# A concern is reusable; a machine profile composes concerns elsewhere.
|
|
# Every installed explicit package must be assigned exactly once on this laptop.
|
|
CONCERNS: Final = {
|
|
"common/core": """base base-devel linux linux-firmware linux-headers cryptsetup refind
|
|
arch-install-scripts git yadm openssh man-db jq ripgrep fd fzf 7zip unzip rsync
|
|
ntfs-3g os-prober fwupd fish zsh zellij zoxide htop glances starship cowsay ex-vi-compat
|
|
mtools dmidecode libsmbios catdoc odt2txt hunspell-de""",
|
|
"common/wayland": """hyprland hypridle hyprlock xdg-desktop-portal-hyprland
|
|
pipewire-alsa pipewire-pulse wireplumber pavucontrol pamixer mako wofi grim
|
|
slurp satty wl-clipboard wlr-randr alacritty kitty tmux neovim vim yazi ueberzugpp
|
|
webkit2gtk-4.1
|
|
flameshot playerctl rofi bluez bluez-utils blueman gnome-keyring xorg-xwayland
|
|
noctalia-git ranger-git""",
|
|
"common/fonts": """noto-fonts noto-fonts-cjk noto-fonts-emoji ttf-jetbrains-mono-nerd
|
|
ttf-icomoon-feather ttf-meslo-nerd-font-powerlevel10k""",
|
|
"common/development": """nodejs npm yarn python python-pip pyenv uv rust cargo go ruby
|
|
jdk-openjdk docker docker-compose podman vagrant virtualbox virtualbox-host-dkms
|
|
cloudflared openbsd-netcat pycharm postman-bin mongodb-compass-bin pi engram-bin
|
|
decman yay gti fcron""",
|
|
"common/productivity": """brave-bin firefox chromium thunderbird obsidian bitwarden
|
|
nextcloud-client signal-desktop telegram-desktop libreoffice-fresh evince okular
|
|
nomacs zotero-bin qalculate-gtk qalculate-qt qbittorrent teams teamspeak3 cerebro-bin chatgtk
|
|
torbrowser-launcher spotify""",
|
|
"common/media": """vlc mpv smplayer audacity imagemagick steam cudnn""",
|
|
"common/research": """biber r texlive-basic texlive-bibtexextra texlive-binextra
|
|
texlive-context texlive-fontsextra texlive-fontsrecommended texlive-fontutils
|
|
texlive-formatsextra texlive-games texlive-humanities texlive-latex texlive-latexextra
|
|
texlive-latexrecommended texlive-luatex texlive-mathscience texlive-metapost
|
|
texlive-music texlive-pictures texlive-plaingeneric texlive-pstricks texlive-publishers
|
|
texlive-xetex resvg""",
|
|
"common/network": """networkmanager network-manager-applet avahi nss-mdns gvfs-smb
|
|
cups cups-pdf mullvad-vpn-beta-bin""",
|
|
"common/hardware": """keyd mesa-utils nvidia-open nvtop vulkan-tools""",
|
|
"desktop/local": """bun fakeroot geoclue github-cli gtk4-layer-shell lsof
|
|
lvm2 mpv numlockx nvidia-container-toolkit nvidia-settings ollama-cuda opencode
|
|
perl-yaml-tiny pipewire powerline qpwgraph ranger retry rustup screengrab seahorse
|
|
sudo sway texlive-bin texlive-meta tree ttf-meslo-nerd unrar vlc-plugins-all
|
|
vulkan-radeon wdisplays wget xournalpp beads downgrade nomacs-git yay-debug
|
|
zsa-keymapp-bin""",
|
|
"laptop/hardware": """intel-ucode thermald tlp tlp-pd tlp-rdw powertop psensor lm_sensors
|
|
ddcutil intel-gpu-tools intel-media-driver vulkan-intel
|
|
nvidia-prime envycontrol light nwg-displays
|
|
wireless_tools caffeine woeusb""",
|
|
"common/services": """sddm pcmanfm libva-utils""",
|
|
}
|
|
|
|
|
|
def explicit(command: str) -> set[str]:
|
|
return set(
|
|
subprocess.run(
|
|
["pacman", command], check=True, text=True, stdout=subprocess.PIPE
|
|
).stdout.split()
|
|
)
|
|
|
|
|
|
def write(path: Path, values: set[str]) -> None:
|
|
resolved = path.resolve()
|
|
package_root = PACKAGES.resolve()
|
|
if package_root not in resolved.parents:
|
|
raise ValueError(f"refusing to write outside packages: {path}")
|
|
resolved.parent.mkdir(parents=True, exist_ok=True)
|
|
resolved.write_text("\n".join(sorted(values)) + "\n", encoding="utf-8")
|
|
|
|
|
|
def main() -> None:
|
|
official, aur = explicit("-Qqen"), explicit("-Qqem")
|
|
assigned: set[str] = set()
|
|
for name, raw in CONCERNS.items():
|
|
selected = set(raw.split()) & (official | aur)
|
|
overlap = selected & assigned
|
|
if overlap:
|
|
raise SystemExit(f"duplicate ownership in {name}: {sorted(overlap)}")
|
|
assigned |= selected
|
|
write(PACKAGES / f"{name}-repo.txt", selected & official)
|
|
write(PACKAGES / f"{name}-aur.txt", selected & aur)
|
|
remaining = (official | aur) - assigned
|
|
if remaining:
|
|
raise SystemExit(
|
|
"unclassified explicit packages; add them to a named concern: "
|
|
+ ", ".join(sorted(remaining))
|
|
)
|
|
print(f"assigned={len(assigned)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|