suckless/install.sh
2026-09-28 12:18:28 +02:00

281 lines
9.2 KiB
Bash
Executable file

#!/bin/sh
# Deja un Void Linux recién instalado con este escritorio: paquetes, programas
# de suckless, enlaces de la configuración en ~ y archivos de sistema.
#
# Uso: ./install.sh [paquetes] [suckless] [enlaces] [gtk] [gnupg] [sistema] [quitar]
# Sin argumentos hace los siete pasos, en ese orden. Se ejecuta como el
# usuario normal; pide la contraseña de root (doas, o sudo en un Void recién
# instalado) cuando hace falta. Se puede repetir sin problema.
set -eu
DIR=$(cd "$(dirname "$0")" && pwd)
PAQUETES="
base-devel pkg-config
libX11-devel libXft-devel libXinerama-devel libXrandr-devel libXext-devel
libXfixes-devel libxcb-devel freetype-devel fontconfig-devel harfbuzz-devel
libXrender-devel xorgproto imlib2-devel zlib-devel libxcrypt-devel
xorg xinit setxkbmap xrandr dbus turnstile
picom dunst libnotify xwallpaper nsxiv maim xclip sct xss-lock
pipewire wireplumber libspa-bluetooth alsa-pipewire
wpa_supplicant dhcpcd bluez acpid chrony tlp
cups cups-filters hplip
font-firacode nerd-fonts-symbols-ttf papirus-icon-theme papirus-folders
sassc gnome-themes-extra qt5ct qt6ct
opendoas gnupg pinentry-dmenu
firefox thunderbird mpv neovim git fuse-sshfs unzip
yazi file ffmpeg 7zip jq poppler fd ripgrep fzf zoxide resvg ImageMagick
"
SUCKLESS="dwm st dmenu slstatus slock scroll clipmenu"
# Tema GTK Everforest, en una versión fija para que siempre salga igual
GTK_TEMA_REPO=https://github.com/Fausto-Korpsvart/Everforest-GTK-Theme
GTK_TEMA_COMMIT=9b8be4d6648ae9eaae3dd550105081f8c9054825
SERVICIOS="dbus turnstiled wpa_supplicant dhcpcd bluetoothd acpid chronyd tlp automontaje cupsd"
# La red la llevan wpa_supplicant y dhcpcd. polkitd no hace falta como
# servicio: si algo lo pide (udisks2, libvirt), D-Bus lo arranca. turnstiled
# sustituye a elogind.
SERVICIOS_FUERA="NetworkManager polkitd elogind"
# _pipewire da prioridad de tiempo real al audio sin rtkit
# (/etc/security/limits.d/25-pw-rlimits.conf, de pipewire).
GRUPOS="audio video input network bluetooth lpadmin _pipewire"
# Lo que sustituyen otras cosas del repo: sudo (doas), feh (xwallpaper y
# nsxiv), gammastep (sct), autorandr (pantallas), blueman y pavucontrol
# (scripts bluetooth y volumen), pinentry-gtk (pinentry-dmenu), vlc (mpv),
# NetworkManager (wpa_supplicant, dhcpcd y el script wifi), rtkit (el grupo
# _pipewire) y elogind (turnstile; Xorg arranca como root con Xorg.wrap y la
# tapa y los botones ya los lleva acpid).
QUITAR="
sudo feh gammastep autorandr blueman pavucontrol pinentry-gtk
gtk-engine-murrine vlc btop NetworkManager tlp-rdw rtkit elogind
"
msg() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
# Como root: con doas cuando ya está configurado; antes (Void recién
# instalado) con sudo.
root() {
if command -v doas >/dev/null && [ -f /etc/doas.conf ]; then
doas "$@"
else
sudo "$@"
fi
}
paquetes() {
msg "Instalando paquetes"
root xbps-install -Syu xbps
# shellcheck disable=SC2086
root xbps-install -Syu $PAQUETES
}
suckless() {
for p in $SUCKLESS; do
msg "Compilando $p"
make -C "$DIR/suckless/$p" clean >/dev/null
make -C "$DIR/suckless/$p"
root make -C "$DIR/suckless/$p" install
done
}
# Enlaza cada archivo de home/ en la misma ruta dentro de ~. Si ya hay un
# archivo que no es el enlace, se guarda como .bak en vez de pisarlo.
enlaces() {
msg "Enlazando la configuración en $HOME"
cd "$DIR/home"
find . -type f | while read -r f; do
f=${f#./}
src="$DIR/home/$f"
dst="$HOME/$f"
if [ "$(readlink "$dst" 2>/dev/null)" = "$src" ]; then
continue
fi
mkdir -p "$(dirname "$dst")"
if [ -e "$dst" ] || [ -L "$dst" ]; then
mv "$dst" "$dst.bak"
echo " $f -> $f.bak"
fi
ln -s "$src" "$dst"
echo " $f"
done
cd "$DIR"
# Los enlaces a archivos que ya no están en home/ (borrados del repo)
find "$HOME" -xdev -type l -lname "$DIR/home/*" ! -exec test -e {} \; -print |
while read -r l; do
rm "$l"
rmdir "$(dirname "$l")" 2>/dev/null || true
echo " ${l#"$HOME"/} (ya no está en el repo: quitado)"
done
# Las carpetas de home/.config/user-dirs.dirs (y la de las capturas)
mkdir -p "$HOME/Documents" "$HOME/Downloads" "$HOME/Images/Screenshots"
# ssh no quiere ~/.ssh accesible para otros
chmod 700 "$HOME/.ssh"
}
# Compila el tema GTK Everforest (verde, oscuro, paleta medium) en ~/.themes
# y lo enlaza para las apps de GTK 4. Qué tema e iconos se usan lo dice
# home/.config/gtk-*/settings.ini. Las carpetas de Papirus se ponen verdes;
# una actualización de papirus-icon-theme las devuelve a azul, y basta con
# repetir este paso.
gtk() {
msg "Compilando el tema GTK Everforest"
tmp=$(mktemp -d)
git clone -q "$GTK_TEMA_REPO" "$tmp"
git -C "$tmp" checkout -q "$GTK_TEMA_COMMIT"
"$tmp/themes/install.sh" -t green -c dark --tweaks medium -l >/dev/null
rm -rf "$tmp"
msg "Carpetas de Papirus en verde"
root papirus-folders -C green --theme Papirus-Dark >/dev/null
}
# ~/.gnupg se copia a mano (de un USB, por ejemplo). Deja los permisos como
# los quiere gpg, usa pinentry-dmenu (en una tty, pinentry-curses; lo decide
# home/.local/bin/pinentry-menu) y activa el agente SSH con las claves de
# autenticación.
gnupg() {
g="$HOME/.gnupg"
if [ ! -d "$g" ]; then
msg "No hay ~/.gnupg: cópialo y ejecuta ./install.sh gnupg"
return 0
fi
msg "Arreglando permisos de $g"
root chown -R "$(id -u):$(id -g)" "$g"
find "$g" -type d -exec chmod 700 {} +
find "$g" -type f -exec chmod 600 {} +
msg "Configurando gpg-agent (pinentry-dmenu y SSH)"
conf="$g/gpg-agent.conf"
touch "$conf"
chmod 600 "$conf"
sed -i '/^pinentry-program/d; /^default-cache-ttl/d' "$conf"
echo "pinentry-program $HOME/.local/bin/pinentry-menu" >>"$conf"
grep -qx enable-ssh-support "$conf" || echo enable-ssh-support >>"$conf"
# Recordar la contraseña una hora desde el último uso (por defecto, 10 min)
echo "default-cache-ttl 3600" >>"$conf"
echo "default-cache-ttl-ssh 3600" >>"$conf"
# pinentry-dmenu con la fuente y los colores de dmenu
cat >"$g/pinentry-dmenu.conf" <<-EOF
asterisk = "*";
prompt = "Contraseña:";
font = "Fira Code:size=11";
prompt_bg = "#a7c080";
prompt_fg = "#2d353b";
normal_bg = "#2d353b";
normal_fg = "#d3c6aa";
select_bg = "#a7c080";
select_fg = "#2d353b";
desc_bg = "#2d353b";
desc_fg = "#d3c6aa";
EOF
chmod 600 "$g/pinentry-dmenu.conf"
# sshcontrol dice qué claves usa el agente para SSH: se añaden las
# subclaves de autenticación [A] que falten.
touch "$g/sshcontrol"
chmod 600 "$g/sshcontrol"
gpg --with-colons --with-keygrip -K 2>/dev/null |
awk -F: '/^(sec|ssb):/ { auth = ($12 ~ /a/) } /^grp:/ && auth { print $10 }' |
while read -r kg; do
if ! grep -q "^$kg" "$g/sshcontrol"; then
echo "$kg" >>"$g/sshcontrol"
echo " clave SSH: $kg"
fi
done
# Reiniciar el agente para que lea la configuración nueva.
gpgconf --kill gpg-agent
}
sistema() {
msg "Copiando archivos de sistema"
cd "$DIR/sistema"
find . -type f | while read -r f; do
f=${f#.}
case $f in
/etc/doas.conf)
# Un doas.conf roto deja sin root: se valida antes de copiarlo.
doas -C ".$f" || die "doas.conf inválido"
root install -D -m 400 ".$f" "$f"
;;
*) root install -D -m "$(stat -c %a ".$f")" ".$f" "$f" ;;
esac
echo " $f"
done
cd "$DIR"
# wpa_cli sin root para los de wheel (script wifi) y guardar las redes
# nuevas. El archivo tiene las contraseñas: se edita en su sitio.
msg "Configurando wpa_supplicant"
w=/etc/wpa_supplicant/wpa_supplicant.conf
cabecera="ctrl_interface=/run/wpa_supplicant
ctrl_interface_group=wheel
update_config=1"
# shellcheck disable=SC2016
root sh -c 'umask 077; touch "$1"
{ printf "%s\n" "$2"; grep -v -e "^ctrl_interface" -e "^update_config" "$1"; } >"$1.nuevo"
mv "$1.nuevo" "$1"' sh "$w" "$cabecera"
msg "Activando servicios"
for s in $SERVICIOS; do
[ -d "/etc/sv/$s" ] || die "no existe el servicio $s (¿faltan paquetes?)"
if [ ! -e "/var/service/$s" ]; then
root ln -s "/etc/sv/$s" /var/service/
fi
done
for s in $SERVICIOS_FUERA; do
if [ -e "/var/service/$s" ]; then
root rm "/var/service/$s"
fi
done
msg "Añadiendo $USER a los grupos: $GRUPOS"
for g in $GRUPOS; do
if getent group "$g" >/dev/null; then
root usermod -aG "$g" "$USER"
fi
done
}
# Desinstala lo que el repo ya no usa (QUITAR), si está instalado. sudo solo
# se quita cuando doas ya funciona (paso sistema).
quitar() {
[ -f /etc/doas.conf ] || die "falta /etc/doas.conf: ejecuta antes el paso sistema"
fuera=
for p in $QUITAR; do
xbps-query "$p" >/dev/null 2>&1 && fuera="$fuera $p"
done
if [ -z "$fuera" ]; then
return 0
fi
msg "Quitando:$fuera"
# shellcheck disable=SC2086
root xbps-remove -Ry $fuera
# Lo que puso aquí una versión anterior de sistema/ para elogind
if [ -e /etc/elogind/logind.conf.d/10-acpid.conf ]; then
root rm /etc/elogind/logind.conf.d/10-acpid.conf
fi
}
[ -f /etc/void-release ] || command -v xbps-install >/dev/null ||
die "esto es solo para Void Linux"
[ "$(id -u)" -ne 0 ] || die "ejecútalo como tu usuario, no como root"
[ $# -gt 0 ] || set -- paquetes suckless enlaces gtk gnupg sistema quitar
for paso; do
case $paso in
paquetes|suckless|enlaces|gtk|gnupg|sistema|quitar) "$paso" ;;
*) die "paso desconocido: $paso" ;;
esac
done
msg "Hecho. Cierra sesión y vuelve a entrar para aplicar los grupos; luego startx."