feat: package lists per role, with a check-and-install script
The lists are derived rather than typed. beastix has 512 explicit packages - years of experiments among them - so taking that as "what this machine needs" would be meaningless. curate.sh combines four usage signals, each of which alone has a blind spot: shell history sees no GUI application, KDE's activity database sees no chat client, autostart sees only what starts by itself, and flatpak entries never appear in pacman -Qqe at all. That reduces 512 to 62. The split follows the data: what beastix and shron both have becomes the base, the rest of beastix becomes desktop, shron's own becomes server. base-devel is one entry in the server list rather than its 26 members. It stopped being a package group in 2022 - "pacman -Sg base-devel" now fails outright - and is a meta package today. base, grub and linux-lts are dropped entirely; they arrive with the operating system. packages.sh never removes anything. Extras are reported and left alone, for the same reason the BookStack playbook refuses to: on a server, individual packages carry mail and web services. It avoids process substitution because /bin/sh is dash on Debian and a POSIX-mode bash on macOS, and this has to run on both. Verified on beastix: 62 wanted, 0 missing, 450 extra reported and untouched; install is a no-op. The server list is a snapshot from 2026-08-06 and wants re-reading when shron is reachable again - its login session has since expired. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d215dfefc5
commit
035c88c31c
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Build a candidate "daily use" package list from two independent signals.
|
||||||
|
#
|
||||||
|
# Shell history alone is blind to anything started from the desktop menu, and a
|
||||||
|
# .desktop file alone says nothing about whether the thing is ever opened. Taken
|
||||||
|
# together they cover both halves: CLI tools by how often they were actually
|
||||||
|
# run, GUI applications by the fact that they were explicitly installed and ship
|
||||||
|
# a menu entry.
|
||||||
|
#
|
||||||
|
# Everything here is a candidate, not a decision. The point is to replace
|
||||||
|
# guessing about 512 packages with a list short enough to read.
|
||||||
|
|
||||||
|
set -u
|
||||||
|
OUT=/home/templis/.claude/jobs/e44cbeae/tmp
|
||||||
|
EXPLICIT=$(mktemp)
|
||||||
|
pacman -Qqe | sort -u > "$EXPLICIT"
|
||||||
|
|
||||||
|
# One pass over the file lists instead of one pacman -Qo per command: -Qo forks
|
||||||
|
# a lookup every time and takes minutes for 40 commands.
|
||||||
|
MAP="$OUT/file2pkg.txt"
|
||||||
|
pacman -Ql $(cat "$EXPLICIT") 2>/dev/null | awk '$2 ~ /\/(bin|sbin)\/[^\/]+$/ {n=split($2,a,"/"); print a[n], $1}' | sort -u > "$MAP"
|
||||||
|
|
||||||
|
# --- Signal 1: commands actually run, mapped to their package -------------
|
||||||
|
: > "$OUT/cli_pkgs.txt"
|
||||||
|
while read -r count cmd; do
|
||||||
|
[ "${count:-0}" -lt 5 ] && continue
|
||||||
|
pkg=$(awk -v c="$cmd" '$1 == c {print $2; exit}' "$MAP")
|
||||||
|
[ -n "$pkg" ] && printf '%s\t%s\t%s\n' "$count" "$pkg" "$cmd"
|
||||||
|
done < /tmp/claude-1000/cmdfreq.txt | sort -rn -k1 > "$OUT/cli_pkgs.txt"
|
||||||
|
|
||||||
|
# --- Signal 2: explicitly installed packages that ship a menu entry -------
|
||||||
|
: > "$OUT/gui_pkgs.txt"
|
||||||
|
pacman -Qlq $(cat "$EXPLICIT") 2>/dev/null | grep -E '/usr/share/applications/[^/]+\.desktop$' > "$OUT/desktop_files.txt"
|
||||||
|
while read -r pkg; do
|
||||||
|
if pacman -Qlq "$pkg" 2>/dev/null | grep -qE '/usr/share/applications/[^/]+\.desktop$'; then
|
||||||
|
echo "$pkg"
|
||||||
|
fi
|
||||||
|
done < "$EXPLICIT" > "$OUT/gui_pkgs.txt"
|
||||||
|
|
||||||
|
echo "CLI-Pakete aus History : $(awk '{print $2}' "$OUT/cli_pkgs.txt" | sort -u | wc -l)"
|
||||||
|
echo "GUI-Pakete mit Menue : $(wc -l < "$OUT/gui_pkgs.txt")"
|
||||||
|
rm -f "$EXPLICIT"
|
||||||
Executable
+88
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Compare the installed packages against the lists for this machine's role.
|
||||||
|
#
|
||||||
|
# packages.sh what is missing and what is extra
|
||||||
|
# packages.sh install install what is missing
|
||||||
|
#
|
||||||
|
# It never removes anything. Extra packages are reported and left alone: on a
|
||||||
|
# server, individual packages carry mail and web services, and a tool that
|
||||||
|
# tidies up on its own is a tool that eventually takes one of them down. The
|
||||||
|
# same caution is why the BookStack upgrade playbook refuses to remove
|
||||||
|
# anything either.
|
||||||
|
#
|
||||||
|
# The lists are data, not code - plain names, one per line. That is deliberate:
|
||||||
|
# they map onto home-manager and environment.systemPackages almost unchanged if
|
||||||
|
# this ever moves to Nix, whereas an install script would not.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROLE=$(cat "$HOME/.config/dotconfs/role" 2>/dev/null || echo desktop)
|
||||||
|
DIR="$(cd "$(dirname "$0")/../packages" 2>/dev/null && pwd)" || {
|
||||||
|
echo "packages/ not found next to $0" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Comments and blank lines are for the reader, not the package manager.
|
||||||
|
read_list() {
|
||||||
|
[ -f "$1" ] || return 0
|
||||||
|
sed -E 's/#.*//' "$1" | tr -d '\r' | awk 'NF'
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Linux)
|
||||||
|
command -v pacman >/dev/null 2>&1 || { echo "not an Arch machine" >&2; exit 1; }
|
||||||
|
WANT=$(read_list "$DIR/arch-base.txt"; read_list "$DIR/arch-$ROLE.txt" || true)
|
||||||
|
HAVE=$(pacman -Qqe)
|
||||||
|
INSTALL="yay -S --needed --noconfirm"
|
||||||
|
command -v yay >/dev/null 2>&1 || INSTALL="sudo pacman -S --needed"
|
||||||
|
;;
|
||||||
|
Darwin)
|
||||||
|
command -v brew >/dev/null 2>&1 || { echo "Homebrew not in PATH - is this a login shell?" >&2; exit 1; }
|
||||||
|
WANT=$(read_list "$DIR/brew-$ROLE.txt")
|
||||||
|
HAVE=$(brew leaves)
|
||||||
|
INSTALL="brew install"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "unsupported platform: $(uname -s)" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Temporary files rather than process substitution: <(...) is not POSIX, and
|
||||||
|
# /bin/sh is dash on Debian and a POSIX-mode bash on macOS. This script has to
|
||||||
|
# run on both.
|
||||||
|
TMP=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$TMP"' EXIT INT TERM
|
||||||
|
printf '%s\n' "$WANT" | awk 'NF' | sort -u > "$TMP/want"
|
||||||
|
printf '%s\n' "$HAVE" | awk 'NF' | sort -u > "$TMP/have"
|
||||||
|
MISSING=$(comm -23 "$TMP/want" "$TMP/have")
|
||||||
|
EXTRA=$(comm -13 "$TMP/want" "$TMP/have")
|
||||||
|
WANT=$(cat "$TMP/want")
|
||||||
|
HAVE=$(cat "$TMP/have")
|
||||||
|
|
||||||
|
count() { printf '%s\n' "$1" | awk 'NF' | wc -l | tr -d ' '; }
|
||||||
|
|
||||||
|
case "${1:-check}" in
|
||||||
|
check)
|
||||||
|
echo "role: $ROLE wanted: $(count "$WANT") installed: $(count "$HAVE")"
|
||||||
|
echo
|
||||||
|
echo "missing ($(count "$MISSING")):"
|
||||||
|
printf '%s\n' "$MISSING" | awk 'NF' | sed 's/^/ /'
|
||||||
|
echo
|
||||||
|
echo "extra, not touched ($(count "$EXTRA")):"
|
||||||
|
printf '%s\n' "$EXTRA" | awk 'NF' | sed 's/^/ /'
|
||||||
|
;;
|
||||||
|
install)
|
||||||
|
if [ -z "$(printf '%s\n' "$MISSING" | awk 'NF')" ]; then
|
||||||
|
echo "nothing to install"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "installing:"
|
||||||
|
printf '%s\n' "$MISSING" | awk 'NF' | sed 's/^/ /'
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$INSTALL $(printf '%s ' $MISSING)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage: $0 [check|install]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Packages wanted on every Arch machine, regardless of role.
|
||||||
|
#
|
||||||
|
# Derived, not curated by hand: the intersection of what beastix actually uses
|
||||||
|
# (shell history, KDE's activity database, autostart, flatpak - see
|
||||||
|
# .scripts/curate.sh) with what shron has installed explicitly. A package on
|
||||||
|
# both machines has earned its place in the base by being useful with and
|
||||||
|
# without a desktop.
|
||||||
|
bind
|
||||||
|
bpytop
|
||||||
|
docker
|
||||||
|
ex-vi-compat
|
||||||
|
git
|
||||||
|
htop
|
||||||
|
lnav
|
||||||
|
man-db
|
||||||
|
mc
|
||||||
|
nano
|
||||||
|
ncdu
|
||||||
|
neovim
|
||||||
|
openssh
|
||||||
|
redis
|
||||||
|
tailscale
|
||||||
|
tmux
|
||||||
|
vim
|
||||||
|
wget
|
||||||
|
yay
|
||||||
|
zsh
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Packages for the desktop role, on top of arch-base.txt.
|
||||||
|
#
|
||||||
|
# From the same four usage signals on beastix, minus everything shron also has.
|
||||||
|
# The original 512 explicit packages are years of experiments; these are the
|
||||||
|
# ones something actually points at.
|
||||||
|
android-tools
|
||||||
|
blueman
|
||||||
|
brscan4
|
||||||
|
claude-code
|
||||||
|
clicker-git
|
||||||
|
cwitch
|
||||||
|
firefox
|
||||||
|
flameshot
|
||||||
|
hytale-launcher-bin
|
||||||
|
inetutils
|
||||||
|
kdeconnect
|
||||||
|
kdotool
|
||||||
|
keepassxc
|
||||||
|
kitty
|
||||||
|
libpulse
|
||||||
|
lsd
|
||||||
|
mtr-gtk
|
||||||
|
networkmanager
|
||||||
|
ntfs-3g
|
||||||
|
openai-codex
|
||||||
|
pamixer
|
||||||
|
path-of-building-community-git
|
||||||
|
pdftk
|
||||||
|
pipewire
|
||||||
|
pipewire-pulse
|
||||||
|
redshift
|
||||||
|
seatd
|
||||||
|
signal-desktop
|
||||||
|
smartmontools
|
||||||
|
snapper
|
||||||
|
steam
|
||||||
|
sway
|
||||||
|
tailwindcss-bin
|
||||||
|
teamspeak
|
||||||
|
thunderbird
|
||||||
|
uxplay
|
||||||
|
whois
|
||||||
|
wine
|
||||||
|
wireplumber
|
||||||
|
yazi
|
||||||
|
ytdl
|
||||||
|
zoiper-bin
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Packages for the server role, on top of arch-base.txt.
|
||||||
|
#
|
||||||
|
# Taken from shron's explicit list of 2026-08-06. Re-check it when shron is
|
||||||
|
# reachable again - the snapshot was read while a login session still existed,
|
||||||
|
# and that session has since expired.
|
||||||
|
#
|
||||||
|
# base-devel appears as a single entry rather than its 26 members. It stopped
|
||||||
|
# being a package group in 2022 and is a meta package now, so pulling in the
|
||||||
|
# members individually is both noisier and no longer how it is installed.
|
||||||
|
# base, grub and linux-lts are left out entirely: they come with the operating
|
||||||
|
# system and nobody installs them from a dotfiles script.
|
||||||
|
7zip
|
||||||
|
acme.sh
|
||||||
|
base-devel
|
||||||
|
btop
|
||||||
|
caddy
|
||||||
|
certbot
|
||||||
|
certbot-dns-gandi
|
||||||
|
certbot-nginx
|
||||||
|
composer
|
||||||
|
crowdsec
|
||||||
|
crowdsec-firewall-bouncer-iptables
|
||||||
|
docker-compose
|
||||||
|
dotpac
|
||||||
|
endlessh-git
|
||||||
|
fail2ban
|
||||||
|
ffmpeg
|
||||||
|
figlet
|
||||||
|
fzf
|
||||||
|
gdu
|
||||||
|
gitmux
|
||||||
|
kitty-shell-integration
|
||||||
|
lazydocker
|
||||||
|
libappimage
|
||||||
|
libpam-google-authenticator
|
||||||
|
librsvg
|
||||||
|
libxcrypt-compat
|
||||||
|
links
|
||||||
|
mariadb
|
||||||
|
mdadm
|
||||||
|
mtools
|
||||||
|
mtr
|
||||||
|
net-tools
|
||||||
|
nextcloud-client
|
||||||
|
nginx
|
||||||
|
nmon
|
||||||
|
ntfysh-bin
|
||||||
|
php
|
||||||
|
php-apcu
|
||||||
|
php-fpm
|
||||||
|
php-gd
|
||||||
|
php-imagick
|
||||||
|
php-legacy
|
||||||
|
php-legacy-gd
|
||||||
|
php-legacy-memcache
|
||||||
|
php-legacy-memcached
|
||||||
|
php-legacy-odbc
|
||||||
|
php-legacy-redis
|
||||||
|
php-legacy-sodium
|
||||||
|
php-legacy-sqlite
|
||||||
|
php-legacy-tidy
|
||||||
|
php-redis
|
||||||
|
php-sodium
|
||||||
|
php-sqlite
|
||||||
|
php-tidy
|
||||||
|
powerline
|
||||||
|
progress
|
||||||
|
python-pip
|
||||||
|
python-pipx
|
||||||
|
rsync
|
||||||
|
screen
|
||||||
|
sshfs
|
||||||
|
teamspeak3-server
|
||||||
|
unace
|
||||||
|
unarj
|
||||||
|
unrar
|
||||||
|
unzip
|
||||||
|
zip
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Homebrew casks for the mobile role - applications rather than command line
|
||||||
|
# tools, installed with "brew install --cask".
|
||||||
|
alfred
|
||||||
|
appcleaner
|
||||||
|
claude-code
|
||||||
|
codex
|
||||||
|
cyberduck
|
||||||
|
dbeaver-community
|
||||||
|
drawio
|
||||||
|
font-hack-nerd-font
|
||||||
|
font-inconsolata-nerd-font
|
||||||
|
font-ubuntu-mono-nerd-font
|
||||||
|
font-ubuntu-nerd-font
|
||||||
|
ghostty
|
||||||
|
gimp
|
||||||
|
iterm2
|
||||||
|
joplin
|
||||||
|
jordanbaird-ice
|
||||||
|
karabiner-elements
|
||||||
|
keepassxc
|
||||||
|
kitty
|
||||||
|
libreoffice
|
||||||
|
mactex
|
||||||
|
mullvad-vpn
|
||||||
|
mullvadvpn
|
||||||
|
neovide
|
||||||
|
neovide-app
|
||||||
|
nextcloud
|
||||||
|
obsidian
|
||||||
|
openvpn-connect
|
||||||
|
rectangle
|
||||||
|
rustdesk
|
||||||
|
sublime-text
|
||||||
|
thunderbird@esr
|
||||||
|
utm
|
||||||
|
vimr
|
||||||
|
vlc
|
||||||
|
wireshark-chmodbpf
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Homebrew formulae for the mobile role (the Mac).
|
||||||
|
#
|
||||||
|
# "brew leaves" rather than "brew list": leaves are the things asked for, the
|
||||||
|
# full list is those plus everything dragged in as a dependency.
|
||||||
|
aria2
|
||||||
|
bat
|
||||||
|
cabextract
|
||||||
|
cdrtools
|
||||||
|
colima
|
||||||
|
composer
|
||||||
|
docker
|
||||||
|
docker-compose
|
||||||
|
fd
|
||||||
|
fzf
|
||||||
|
git-delta
|
||||||
|
go
|
||||||
|
hashcat
|
||||||
|
helix
|
||||||
|
imapsync
|
||||||
|
john
|
||||||
|
lazygit
|
||||||
|
midnight-commander
|
||||||
|
nmap
|
||||||
|
node
|
||||||
|
perl
|
||||||
|
putty
|
||||||
|
pypy
|
||||||
|
ripgrep
|
||||||
|
rust
|
||||||
|
tailscale
|
||||||
|
telnet
|
||||||
|
tmux
|
||||||
|
tree
|
||||||
|
vim
|
||||||
|
wget
|
||||||
|
wimlib
|
||||||
|
wireguard-tools
|
||||||
|
wireshark
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Flatpaks on the desktop.
|
||||||
|
#
|
||||||
|
# These never appear in "pacman -Qqe", which is why they need their own list -
|
||||||
|
# a package survey based on pacman alone silently loses them.
|
||||||
|
com.hypixel.HytaleLauncher
|
||||||
|
org.gnome.Mines
|
||||||
Reference in New Issue
Block a user