Decluttering CachyOS

Following on from the Useful Oneliners for Ubuntu, I also wanted to clean up my CachyOS system, which I made the mistake of installing a bunch of things natively onto.

I'm messing around with Claude again, and it's generating me a useful script.

Starting Point

Prompts

Here goes:

The Optional Package Groups

Desktop Install Option Groups

What It Can Do

Here's the command-line help of the final script:

$ ./get_names.py --help
Usage: get_names.py [OPTIONS] [--<group-flag> ...]

Prints a list of packages for different CachyOS desktop environments and optional extras.

The following base groups are always included:
  - Base-devel + Common packages
  - CPU specific Microcode update packages
  - CachyOS Packages
  - CachyOS required (hidden)
  - CachyOS shell configuration

Options:
  -h, --help     Show this help message and exit
  --refresh      Re-download the package list from GitHub (ignores cache)
  --sort         Sort the output package list alphabetically

Optional package groups:
  --kde-desktop                              KDE-Plasma Desktop - Simple by default, powerful when needed.
  --gnome-desktop                            GNOME desktop environment - designed to put you in control and get things
                                             done.
  --xfce4                                    Xfce is a lightweight desktop environment for UNIX-like operating systems.
                                             It aims to be fast and low on system resources, while still being visually
                                             appealing and user friendly.
  --bspwm                                    bspwm is a tiling window manager that represents windows as the leaves of a
                                             full binary tree. bspwm supports multiple monitors and is configured and
                                             controlled through messages.
  --budgie-desktop                           Budgie - an independent, familiar, and modern desktop.
  --cinnamon                                 Linux desktop which provides advanced innovative features and a traditional
                                             user experience.
  --cosmic                                   Linux desktop, which provides an environment that features advanced
                                             functionality and a responsive design.
  --i3-window-manager                        i3 tiling window manager, primarily targeted at developers and advanced
                                             users.
  --hyprland                                 Hyprland is a highly customizable dynamic tiling Wayland compositor that
                                             doesn't sacrifice on its looks.
  --niri                                     A scrollable-tiling Wayland compositor.
  --lxde-desktop                             The Lightweight Desktop Environment.
  --lxqt-desktop                             The Lightweight Qt Desktop Environment.
  --mate-desktop                             MATE Desktop - the continuation of GNOME 2
  --openbox                                  Openbox is a highly configurable, floating window manager with extensive
                                             standards support.
  --qtile                                    Qtile is a X11 window manager that is configured with the Python
                                             programming language.
  --sway                                     Sway is a tiling Wayland compositor and a drop-in replacement for the i3
                                             window manager for X11. It works with your existing i3 configuration and
                                             supports most of i3's features, plus a few extras.
  --ukui                                     It is a lightweight desktop environment, which consumes few resources and
                                             works with older computers. It has been developed with GTK and Qt
                                             technologies. Its visual appearance is similar to Windows 7, making it
                                             easier for new users of Linux.
  --wayfire                                  Wayfire is a wayland compositor based on wlroots. It aims to create a
                                             customizable, extendable and lightweight environment without sacrificing
                                             its appearance.
  --firefox-and-language-package             Add firefox and language pack if possible
  --printing-support                         Support for printing (Cups)
  --support-for-hp-printer-scanner           Extra Packages for HP Printer/Scanner
  --accessibility-tools                      Screen reader and mouse tweaks (impaired vision)

Output

Here's what it outputs for the CachyOS options I originally chose:

$ ./get_names.py --gnome-desktop --firefox-and-language-package --printing-support --support-for-hp-printer-scanner --sort
Using cached file: /home/max/Downloads/idea-cleanup-cachyos-host/netinstall.yaml
accountsservice
alacritty
alsa-firmware
alsa-plugins
alsa-utils
amd-ucode
awesome-terminal-fonts
bash-completion
bluez
bluez-hid2hci
bluez-libs
bluez-obex
bluez-utils
btop
cachyos-fish-config
cachyos-hello
cachyos-hooks
cachyos-kernel-manager
cachyos-keyring
cachyos-micro-settings
cachyos-mirrorlist
cachyos-packageinstaller
cachyos-rate-mirrors
cachyos-settings
cachyos-v3-mirrorlist
cachyos-v4-mirrorlist
cachyos-wallpapers
cachyos-zsh-config
cantarell-fonts
chwd
cpupower
cups
cups
cups-filters
cups-filters
cups-pdf
cups-pdf
dmidecode
dmraid
dnsmasq
dnsutils
duf
efitools
ethtool
fastfetch
ffmpegthumbnailer
file-roller
firefox
firefox-i18n-$LOCALE
foomatic-db
foomatic-db-engine
foomatic-db-gutenprint-ppds
foomatic-db-nonfree
foomatic-db-nonfree-ppds
foomatic-db-ppds
fsarchiver
fwupd
gdm
gedit
ghostscript
git
glances
gnome-backgrounds
gnome-calculator
gnome-console
gnome-control-center
gnome-disk-utility
gnome-keyring
gnome-nettool
gnome-power-manager
gnome-shell
gnome-tweaks
gnome-usage
gsfonts
gst-libav
gst-plugin-pipewire
gst-plugins-bad
gst-plugins-ugly
gutenprint
gvfs-afc
gvfs-gphoto2
gvfs-mtp
gvfs-nfs
gvfs-smb
hdparm
hplip
hwdetect
hwinfo
intel-ucode
iwd
libdvdcss
libgsf
libnma
libopenraw
linux-cachyos
linux-cachyos-headers
linux-cachyos-lts
linux-cachyos-lts-headers
linux-firmware
loupe
lsscsi
malcontent
meld
mesa-utils
micro
modemmanager
mtools
nano
nano-syntax-highlighting
networkmanager
networkmanager-openvpn
nfs-utils
nilfs-utils
noto-fonts
noto-fonts-cjk
noto-fonts-emoji
nss-mdns
octopi
openssh
pacman-contrib
papers
paru
pavucontrol
pipewire-alsa
pipewire-pulse
pkgfile
plocate
poppler-glib
power-profiles-daemon
pv
python-defusedxml
python-packaging
python-pyqt5
python-reportlab
rebuild-detector
reflector
ripgrep
rsync
sg3_utils
showtime
simple-scan
smartmontools
sof-firmware
splix
sushi
system-config-printer
ttf-bitstream-vera
ttf-dejavu
ttf-liberation
ttf-meslo-nerd
ttf-opensans
ufw
unrar
unzip
upower
usb_modeswitch
vim
vlc-plugins-all
wget
wireless-regdb
wireplumber
wpa_supplicant
xdg-user-dirs
xdg-utils
xl2tpd

Now What?

Now I can use this to compare the list of original installed packages to the list of currently-installed packages.

This is similar to what I did previously with Ubuntu's install manifests.

The idea is to clean up my CachyOS host system, to remove all the cruft, and put as many of those userspace commands into Docker container calls, and so on.

Useful Oneliners: Comparing CachyOS Installed Packages and Original Install Manifest Lists

diff -y <(pacman -Q | awk '{print $1}') <(./get_names.py --gnome-desktop --firefox-and-language-package --printing-support --support-for-hp-printer-scanner --sort)

Ok this one's a bit of a mess compared to Ubuntu, because there's a lot of fanout from the packages in the install manifests to what is then installed on the system.

These seem to be meta packages and a lot of noise.

This isn't particularly useful. What I need instead is a directed graph showing which packages cause the most other packages to be installed, and can be cleaned up.

Using cached file: /home/max/Downloads/idea-cleanup-cachyos-host/netinstall.yaml
7zip                                                      <
a52dec                                                    <
aalib                                                     <
abseil-cpp                                                <
accountsservice                                           accountsservice
acl                                                       | alacritty
adobe-source-han-sans-cn-fonts                            <
adobe-source-han-sans-jp-fonts                            <
adobe-source-han-sans-kr-fonts                            <
adwaita-cursors                                           <
adwaita-fonts                                             <
adwaita-icon-theme                                        <
adwaita-icon-theme-legacy                                 <
alsa-card-profiles                                        <
alsa-firmware                                             alsa-firmware
alsa-lib                                                  <
alsa-plugins                                              alsa-plugins
alsa-topology-conf                                        <
alsa-ucm-conf                                             <
alsa-utils                                                alsa-utils
amd-ucode                                                 amd-ucode
ananicy-cpp                                               <
android-file-transfer                                     <
android-tools                                             <
android-udev                                              <
aom                                                       <
apache                                                    <
appstream                                                 <
apr                                                       <
apr-util                                                  <
archlinux-keyring                                         <
argon2                                                    <
aribb24                                                   <
aribb25                                                   <
at-spi2-core                                              <
atkmm                                                     <
atomicparsley                                             <
attr                                                      <
audit                                                     <
autoconf                                                  <
automake                                                  <
avahi                                                     <
awesome-terminal-fonts                                    awesome-terminal-fonts
baobab                                                    <

Claude's Neat Automatic Tricks

The original command-line help wasn't word wrapped:

Original CLI Help

The word wrap was cleverer than I even asked it to be, and I was testing what it would do. It wrapped and aligned the description text automatically. Nice going!

Wrapped CLI Help

The Script Itself

#!/usr/bin/env python3
import re
import sys
import urllib.request
import yaml
from pathlib import Path

url = "https://raw.githubusercontent.com/CachyOS/cachyos-calamares/cachyos-dev/src/modules/netinstall/netinstall.yaml"
cache_path = Path(__file__).parent / "netinstall.yaml"

if "--help" in sys.argv or "-h" in sys.argv:
    import io
    temp_data = None
    if cache_path.exists():
        temp_data = yaml.safe_load(cache_path.read_text())
    else:
        print("(Downloading package list to show available groups...)", file=sys.stderr)
        with urllib.request.urlopen(url) as response:
            content = response.read()
        cache_path.write_bytes(content)
        temp_data = yaml.safe_load(cache_path.read_text())

    BASE_GROUPS_HELP = {
        "CachyOS required (hidden)",
        "CachyOS Packages",
        "CachyOS shell configuration",
        "Base-devel + Common packages",
        "CPU specific Microcode update packages",
    }
    optional = [
        item for item in temp_data
        if isinstance(item, dict) and "name" in item and item["name"] not in BASE_GROUPS_HELP
    ]

    print("Usage: get_names.py [OPTIONS] [--<group-flag> ...]")
    print()
    print("Prints a list of packages for different CachyOS desktop environments and optional extras.")
    print()
    print("The following base groups are always included:")
    for name in sorted(BASE_GROUPS_HELP):
        print(f"  - {name}")
    print()
    print("Options:")
    print("  -h, --help     Show this help message and exit")
    print("  --refresh      Re-download the package list from GitHub (ignores cache)")
    print("  --sort         Sort the output package list alphabetically")
    print()
    import textwrap
    print("Optional package groups:")
    for item in optional:
        flag = re.sub(r"[^a-z0-9]+", "-", item["name"].lower()).strip("-")
        desc = item.get("description", "")
        prefix = f"  --{flag:<40} "
        indent = " " * len(prefix)
        wrapped = textwrap.fill(desc, width=120, initial_indent=prefix, subsequent_indent=indent)
        print(wrapped)
    sys.exit(0)

force_download = "--refresh" in sys.argv

if force_download or not cache_path.exists():
    with urllib.request.urlopen(url) as response:
        content = response.read()
    cache_path.write_bytes(content)
    print(f"Downloaded and cached to {cache_path}", file=sys.stderr)
else:
    print(f"Using cached file: {cache_path}", file=sys.stderr)

data = yaml.safe_load(cache_path.read_text())

BASE_GROUPS = {
    "CachyOS required (hidden)",
    "CachyOS Packages",
    "CachyOS shell configuration",
    "Base-devel + Common packages",
    "CPU specific Microcode update packages",
}

def collect_packages(item):
    pkgs = list(item.get("packages") or [])
    for sub in item.get("subgroups") or []:
        pkgs.extend(collect_packages(sub))
    return pkgs

def name_to_flag(name):
    slug = name.lower()
    slug = re.sub(r"[^a-z0-9]+", "-", slug)
    return slug.strip("-")

optional_groups = {
    name_to_flag(item["name"]): item
    for item in data
    if isinstance(item, dict) and "name" in item and item["name"] not in BASE_GROUPS
}

base_packages = []
for item in data:
    if isinstance(item, dict) and item.get("name") in BASE_GROUPS:
        base_packages.extend(collect_packages(item))

for flag, item in optional_groups.items():
    if f"--{flag}" in sys.argv:
        base_packages.extend(collect_packages(item))

packages = sorted(base_packages) if "--sort" in sys.argv else base_packages
for pkg in packages:
    print(pkg)