Compare commits

..
19 Commits
Author SHA1 Message Date
Josevi 5c353e7287 pkgsync: actualiza inventario de paquetes 2026-09-21 09:14 2026-09-21 09:14:36 +02:00
Josevi 9ad957f4a0 pkgsync: actualiza inventario de paquetes 2026-09-20 23:28 2026-09-21 09:10:42 +02:00
JoseviandClaude Sonnet 5 2c288e1aec kde/setup.sh: actualiza comentario de plasmoides vía chezmoi
claudeusageswitcher y tt.launchpadPlasma ya no están en ningún panel
real (auditoría de plasmoides instalados vs. usados) y se quitaron del
tracking de chezmoi en arch-configs. commandoutput y codexbar sí están
en uso pero no viajaban -- añadidos al tracking. Refleja ambos cambios
aquí para que el comentario no mienta sobre qué plasmoides trae chezmoi.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:16:04 +02:00
JoseviandClaude Sonnet 5 da076af6ad kde/setup.sh, packages-common.sh: quita temas/efectos gráficos sin usar
Auditado contra los look-and-feel Vampire-Light/Vampire-dark (fuente de
verdad real: ~/.local/share/plasma/look-and-feel/*/contents/defaults)
y el kwinrc en vivo. Desinstalado de la máquina real y quitado de los
arrays: plasma6-themes-layan-git, layan-cursor-theme-git,
googledot-cursor-theme, breeze-enhanced, papirus-icon-theme,
kwin-effect-rounded-corners-git, plasma6-applets-panel-colorizer,
kwin-effects-better-blur-dx, klassy. Ninguno aparecía en los defaults
de los temas Vampire-* ni en kwinrc (icono real es Tela/Tela-dark,
cursor Bibata-Modern-Classic, decoración sierrabreezeenhanced).
kwin-effects-glass-git SÍ se queda (glassEnabled=true en kwinrc).

De paso, corregido kdeglobals[KDE]DefaultDarkLookAndFeel de la
referencia colgante a Layan (ya desinstalado) a Vampire-dark.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:13:02 +02:00
JoseviandClaude Sonnet 5 9a9f6d9931 packages-common.sh: migra fastpotify-bin a spotifast-bin (renombrado upstream)
fastpotify-bin avisaba en su propia descripción "Renamed to
spotifast-bin"; desinstalado y sustituido por el paquete AUR con el
nombre nuevo en la máquina real.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:06:32 +02:00
JoseviandClaude Sonnet 5 923da6d375 packages-common.sh: quita tauri-cli (sin ningún proyecto Tauri en la máquina)
Desinstalado del sistema real junto con ~30 dependencias huérfanas
acumuladas (pacman -Qdtq); tauri-cli quedó de una prueba puntual del
31-ago-2026 y nada lo usaba.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:05:18 +02:00
JoseviandClaude Sonnet 5 f86e424491 kde/setup.sh: pasos no críticos no abortan la instalación completa
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 23:01:17 +02:00
JoseviandClaude Sonnet 5 a8b4e3f51d kde/setup.sh: añade bit ejecutable
git ls-files lo tenía como 100644; ./kde/setup.sh (tal como indica el
README) fallaba con "permission denied" en un clon fresco. Detectado
durante una prueba end-to-end en VM.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 21:06:35 +02:00
JoseviandClaude Sonnet 5 226b439684 kde/setup.sh: mueve configure_btrfs_rollback() al final
snap-pac crea un snapshot pre+post por cada transacción de pacman/paru en
cuanto existe /etc/snapper/configs/root, y dentro de arch-chroot no hay
systemd para que el timer de cleanup aplique NUMBER_LIMIT. Con snapper
configurado antes de las ~30 instalaciones AUR, se acumulan decenas de
snapshots sin poda posible durante toda la instalación (visto en real:
llenó el disco de una VM de prueba). Configurarlo al final evita
snapshotear la propia instalación inicial.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 21:06:35 +02:00
JoseviandClaude Sonnet 5 68e30e9feb snapper: fuerza --no-dbus (falla en arch-chroot sin dbus-daemon)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 19:23:41 +02:00
Josevi 5b33bd26f9 pkgsync: actualiza inventario de paquetes 2026-09-20 11:03 2026-09-20 11:03:24 +02:00
Josevi 7a8693de8a pkgsync: actualiza inventario de paquetes 2026-09-19 21:55 2026-09-19 21:55:48 +02:00
Josevi df9cf52acf pkgsync: actualiza inventario de paquetes 2026-09-19 21:26 2026-09-19 21:26:29 +02:00
Josevi 4f2c3e937b pkgsync: actualiza inventario de paquetes 2026-09-19 17:15 2026-09-19 17:15:52 +02:00
Josevi 56c924d0a6 pkgsync: actualiza inventario de paquetes 2026-09-17 18:58 2026-09-17 18:58:48 +02:00
JoseviandClaude Sonnet 5 1994f7b030 pkgsync: añade aviso por Gotify a los fallos, además de notify-send
notify-send depende de la sesión gráfica -- poco fiable justo cuando
push falla en ExecStop durante el apagado (sesión/D-Bus cayéndose).
notify_fail() ahora también hace POST a push.josevi.ovh (mismo Gotify
que ya usa WUD para avisos de actualizaciones), best-effort con
timeout corto, leyendo el token de ~/.config/pkgsync/gotify.env --
fichero local-only, nunca tracked (mismo criterio que
overrides.local.toml), con scaffold comentado en bootstrap.

De paso: durante la verificación se descubrió que notify-send llevaba
tiempo fallando en silencio en esta máquina (applet de Notifications
ausente de shownItems/extraItems en el systray) -- arreglado aparte,
no es parte de este commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 18:58:37 +02:00
Josevi 56742247ce pkgsync: actualiza inventario de paquetes 2026-09-17 18:49 2026-09-17 18:49:34 +02:00
JoseviandClaude Sonnet 5 64bb53eadf pkgsync: aborta sin commitear si falla el parcheo, notifica fallos
cmd_push() seguía adelante y commiteaba lo que hubiera en disco aunque
patch_array_file no encontrara los arrays -- así fue como el fichero
acabó vacío en origin/main. Ahora aborta sin tocar git si cualquiera
de los 3 parcheos falla.

Añade notify_fail() (notify-send con urgencia critical) en los puntos
de fallo real de push/pull: parcheo fallido, git push/pull/clone
fallidos. Como push corre en ExecStop durante el apagado (sesión/D-Bus
cayéndose, notify-send no garantizado), también deja un marcador en
disco que el siguiente pull revisa al arrancar con la sesión completa,
para no depender de que la notificación del apagado llegara a verse.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 18:49:26 +02:00
JoseviandClaude Sonnet 5 598e8b52c8 Restaura shared/packages-common.sh, vaciado por un push corrupto
El push de pkgsync del 2026-08-27 17:41 (8575c4f) commiteó el fichero
ya vacío en disco (algo lo truncó a 0 bytes fuera de pkgsync entre las
16:10 y las 17:41 de ese día -- causa exacta desconocida), porque
cmd_push() no validaba que el parcheo de arrays hubiera funcionado
antes de commitear. Restaurado desde f3cdc35 (último commit bueno,
mismo día 07:37). El siguiente push recapturará los 3 semanas de
drift real desde entonces.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 18:49:20 +02:00
3 changed files with 408 additions and 38 deletions
Regular → Executable
+52 -30
View File
@@ -74,54 +74,76 @@ install_pacman "${PACMAN_COMMON[@]}" \
echo ">> Bootloader (GRUB)..."
install_bootloader
echo ">> Configurando rollback de actualizaciones (snapper)..."
configure_btrfs_rollback
echo ">> Bootstrapeando paru y paquetes AUR..."
bootstrap_paru
# Solo plasmoides que EXISTEN en AUR. Los de KDE Store/GitHub (gnome-pager, kMenu,
# kvitals, windowtitlereborn, claudeusageswitcher, tt.launchpadPlasma) NO están en
# AUR: viajan como dotfiles vía chezmoi (~/.local/share/plasma/plasmoids), capturados
# con shared/capture-kde.sh. No los añadas aquí.
# kvitals, windowtitlereborn, commandoutput, codexbar) NO están en AUR: viajan
# como dotfiles vía chezmoi (~/.local/share/plasma/plasmoids), capturados con
# shared/capture-kde.sh. No los añadas aquí. (claudeusageswitcher y
# tt.launchpadPlasma viajaron así hasta que se quitaron por no usarse en
# ningún panel real -- auditoría 2026-09-20.)
install_aur "${AUR_COMMON[@]}" \
plasma6-applets-panel-colorizer kwin-effects-better-blur-dx kwin-effects-glass-git breeze-enhanced \
plasma6-themes-layan-git tela-icon-theme layan-cursor-theme-git \
kwin-effects-glass-git tela-icon-theme \
plasma6-applets-appgrid plasma6-wallpapers-smart-video-wallpaper-reborn pamac-tray-plasma-git \
yin-yang kwin-decoration-sierra-breeze-enhanced-git
install_helpwire
install_claude_code
run_optional "install_helpwire" install_helpwire
run_optional "install_claude_code" install_claude_code
echo ">> Instalando Flatpaks comunes..."
install_flatpaks "${FLATPAK_COMMON[@]}"
run_optional "install_flatpaks" install_flatpaks "${FLATPAK_COMMON[@]}"
echo ">> Servicios..."
systemctl enable sddm
systemctl enable NetworkManager
systemctl enable bluetooth
systemctl enable "syncthing@$INSTALL_USER.service"
systemctl enable tailscaled
systemctl enable power-profiles-daemon
systemctl enable cpupower
enable_services() {
systemctl enable sddm
systemctl enable NetworkManager
systemctl enable bluetooth
systemctl enable "syncthing@$INSTALL_USER.service"
systemctl enable tailscaled
systemctl enable power-profiles-daemon
systemctl enable cpupower
}
run_optional "enable_services" enable_services
# Tema: Layan (global theme) + iconos Tela + cursores Bibata-Modern-Classic
# (bibata-cursor-theme-bin en AUR_COMMON), vía AUR (plasma6-themes-layan-git/
# tela-icon-theme/layan-cursor-theme-git de arriba, instalan en /usr/share, sin
# copiar binarios en dotfiles; layan-cursor-theme-git se deja instalado como
# alternativa aunque el cursor activo sea Bibata). El resto de la
# personalización (rc files, layout de paneles, splash Animated-Abstract-Cube)
# se captura del portátil con shared/capture-kde.sh y se replica vía chezmoi.
# Single-monitor como base.
# Tema: look-and-feel propios Vampire-Light/Vampire-dark (ver PROJECT_CONTEXT.md)
# + iconos Tela (tela-icon-theme de arriba) + cursores Bibata-Modern-Classic
# (bibata-cursor-theme-bin en AUR_COMMON), vía AUR, instalan en /usr/share, sin
# copiar binarios en dotfiles. El resto de la personalización (rc files,
# layout de paneles, splash Animated-Abstract-Cube, los propios paquetes
# look-and-feel Vampire-*) se captura del portátil con shared/capture-kde.sh
# y se replica vía chezmoi. Single-monitor como base.
# --- chezmoi: dotfiles personales (repo arch-configs) --------------------------
# chezmoi ya se instaló arriba (PACMAN_COMMON, repos oficiales desde 2.x).
echo ">> chezmoi init --apply (repo arch-configs)..."
REMOTE="$GITEA/arch-configs.git"
[[ -n "${GITEA_TOKEN:-}" ]] && REMOTE="https://$INSTALL_USER:$GITEA_TOKEN@gitea.josevi.ovh/josevi/arch-configs.git"
as_user "export GIT_NAME='$GIT_NAME' GIT_EMAIL='$GIT_EMAIL'; \
chezmoi_init_apply() {
as_user "export GIT_NAME='$GIT_NAME' GIT_EMAIL='$GIT_EMAIL'; \
chezmoi init --apply '$REMOTE'"
}
run_optional "chezmoi init --apply" chezmoi_init_apply
# --- pkgsync: sincronización de paquetes (repo arch-setup) -----------------
install_pkgsync
run_optional "install_pkgsync" install_pkgsync
# Snapper AL FINAL, no tras install_bootloader: snap-pac crea un snapshot
# pre+post por cada transacción de pacman/paru en cuanto existe el config de
# snapper, y dentro del chroot no hay systemd corriendo para que el timer de
# cleanup limite NUMBER_LIMIT -- ninguna limpieza puede correr hasta el primer
# arranque real. Con snapper configurado antes de las ~30 instalaciones AUR
# (cada una su propia transacción), se acumulan decenas de snapshots sin poda
# posible; en una VM de prueba con disco pequeño esto llenó el disco entero
# (visto en real). Configurándolo al final, ya con el sistema instalado, no se
# snapshotea la propia instalación -- el primer snapshot legítimo será el de
# un 'pacman -Syu' real ya con el sistema arrancado, que es para lo que existe
# el rollback.
echo ">> Configurando rollback de actualizaciones (snapper)..."
run_optional "configure_btrfs_rollback" configure_btrfs_rollback
rm -f /etc/sudoers.d/99-setup-nopasswd
if ((${#FAILED_STEPS[@]})); then
echo
echo "⚠️ Instalación completada con avisos. Pasos que fallaron y necesitan revisión manual:"
printf ' - %s\n' "${FAILED_STEPS[@]}"
fi
echo ">> Configuración de KDE completada."
+89 -7
View File
@@ -23,6 +23,18 @@ METAPAC_CONFIG_DIR="$HOME/.config/metapac"
METAPAC_GROUP="$METAPAC_CONFIG_DIR/groups/arch-setup.toml"
OVERRIDES_FILE="$METAPAC_CONFIG_DIR/groups/overrides.local.toml"
# Marca que un push falló. pull la revisa al arrancar (con la sesión gráfica
# ya arriba) para avisar aunque el notify-send del propio push, disparado
# durante el apagado con la sesión cayéndose, no llegara a mostrarse. Se
# borra en el primer push que vuelve a tener éxito.
FAIL_MARKER="$HOME/.local/share/pkgsync.failed"
# Token de Gotify (push.josevi.ovh, mismo servidor que usa WUD) para avisos
# de fallo -- llega también sin sesión gráfica, a diferencia de notify-send.
# Fichero local-only, nunca tracked (ni chezmoi ni el repo arch-setup):
# mismo criterio que OVERRIDES_FILE. Si no existe, se omite sin más.
GOTIFY_ENV="$HOME/.config/pkgsync/gotify.env"
# flatpak sin --user/--system instala en "system" con la config actual de
# install_flatpaks() en packages-common.sh. Si tu máquina usa --user, cambia
# esto (o verifica con: flatpak list --columns=installation,application).
@@ -219,6 +231,34 @@ notify() {
command -v notify-send &>/dev/null && timeout 5 notify-send --app-name=pkgsync "$1" "$2"
}
# Como notify(), pero urgencia "critical" (se queda en pantalla hasta que se
# cierra a mano) y además deja constancia en $FAIL_MARKER: push corre en
# ExecStop durante el apagado, con la sesión/D-Bus ya cayéndose, así que su
# notify-send puede no llegar a mostrarse -- el marcador es la red de
# seguridad que garantiza el aviso en el siguiente pull, ya con la sesión
# gráfica completa.
notify_fail() {
echo "!! $1: $2" >&2
printf '%s -- %s: %s\n' "$(date -Iseconds)" "$1" "$2" >"$FAIL_MARKER"
command -v notify-send &>/dev/null && timeout 5 notify-send --app-name=pkgsync --urgency=critical "$1" "$2"
notify_gotify "$1" "$2"
}
# Best-effort, nunca bloquea ni hace fallar el caller: mismo espíritu que el
# timeout de notify-send de arriba, pero via HTTPS -- llega aunque D-Bus/la
# sesión gráfica ya esté cayéndose (caso ExecStop en apagado) o no haya
# sesión gráfica en absoluto.
notify_gotify() {
[[ -f "$GOTIFY_ENV" ]] || return 0
local GOTIFY_URL GOTIFY_TOKEN
# shellcheck disable=SC1090
source "$GOTIFY_ENV"
[[ -n "${GOTIFY_URL:-}" && -n "${GOTIFY_TOKEN:-}" ]] || return 0
timeout 10 curl -fsS --max-time 8 \
-F "title=$1" -F "message=$2" -F "priority=8" \
"${GOTIFY_URL%/}/message?token=${GOTIFY_TOKEN}" >/dev/null 2>&1
}
# --- subcomandos --------------------------------------------------------------
cmd_bootstrap() {
echo ">> pkgsync bootstrap"
@@ -266,6 +306,19 @@ EOF
echo ">> scaffold de $OVERRIDES_FILE creado (vacío, edítalo a mano)"
fi
if [[ ! -f "$GOTIFY_ENV" ]]; then
mkdir -p "$(dirname "$GOTIFY_ENV")"
cat >"$GOTIFY_ENV" <<'EOF'
# Token de aplicación Gotify para los avisos de fallo de pkgsync (push.josevi.ovh).
# Crea una Application nueva en la UI de Gotify y pega su token aquí.
# Fichero local-only: pkgsync nunca lo lee/escribe salvo aquí, no viaja al repo.
#GOTIFY_URL="https://push.josevi.ovh"
#GOTIFY_TOKEN="pon-aqui-el-token"
EOF
chmod 600 "$GOTIFY_ENV"
echo ">> scaffold de $GOTIFY_ENV creado (comentado, edítalo a mano para activar avisos por Gotify)"
fi
if [[ ! -d "$REPO_DIR/.git" ]]; then
echo ">> clonando arch-setup en $REPO_DIR..."
git clone "$REPO_URL" "$REPO_DIR"
@@ -282,7 +335,7 @@ cmd_push() {
echo "=== push $(date -Iseconds) ==="
if [[ ! -d "$REPO_DIR/.git" ]]; then
echo "!! no existe $REPO_DIR, corre 'pkgsync bootstrap' primero"
notify_fail "pkgsync: push falló" "no existe el clon $REPO_DIR, corre 'pkgsync bootstrap'"
return 0
fi
@@ -314,25 +367,40 @@ cmd_push() {
add_flatpak="$(comm -23 "$work/inst_flatpak" "$work/decl_flatpak" | comm -23 - "$work/overrides")"
local pc="$REPO_DIR/shared/packages-common.sh"
patch_array_file "$pc" PACMAN_COMMON "$remove_pacman" "$add_pacman"
patch_array_file "$pc" AUR_COMMON "$remove_aur" "$add_aur"
patch_array_file "$pc" FLATPAK_COMMON "$remove_flatpak" "$add_flatpak"
local patch_failed=0
patch_array_file "$pc" PACMAN_COMMON "$remove_pacman" "$add_pacman" || patch_failed=1
patch_array_file "$pc" AUR_COMMON "$remove_aur" "$add_aur" || patch_failed=1
patch_array_file "$pc" FLATPAK_COMMON "$remove_flatpak" "$add_flatpak" || patch_failed=1
rm -rf "$work"
# No commitear nunca un parcheo a medias/fallido -- así fue como el fichero
# acabó vacío en origin/main una vez (2026-08-27): el parcheo falló pero el
# script siguió y commiteó igualmente lo que hubiera en disco. El fichero
# de trabajo no se toca cuando patch_array_file falla (solo escribe si
# encuentra el array), así que abortar aquí es seguro: el próximo intento
# vuelve a partir del mismo estado.
if (( patch_failed )); then
notify_fail "pkgsync: push falló" "no se pudo parchear packages-common.sh (arrays no encontrados), reviso sin commitear -- $LOG"
return 1
fi
write_group_file
cd "$REPO_DIR" || return 0
git add shared/packages-common.sh
if git diff --cached --quiet; then
echo "sin cambios de paquetes que subir"
rm -f "$FAIL_MARKER"
return 0
fi
git commit -m "pkgsync: actualiza inventario de paquetes $(date '+%Y-%m-%d %H:%M')"
if timeout 20 git push origin main; then
echo "push OK"
rm -f "$FAIL_MARKER"
else
echo "push falló (¿sin red?), el commit queda en local para el próximo intento"
notify_fail "pkgsync: push falló" "commit local OK pero 'git push' falló (¿sin red?); el commit queda en local para el próximo intento"
fi
}
@@ -341,17 +409,31 @@ cmd_pull() {
exec >>"$LOG" 2>&1
echo "=== pull $(date -Iseconds) ==="
# Red de seguridad: si un push anterior falló (típicamente en ExecStop
# durante el apagado, con la sesión/D-Bus ya cayéndose y su propio
# notify-send sin garantía de mostrarse), avisar aquí sí o sí -- pull corre
# al arrancar sesión, con el escritorio ya completo.
if [[ -f "$FAIL_MARKER" ]]; then
notify_fail "pkgsync: hay un fallo pendiente de revisar" "$(cat "$FAIL_MARKER")"
fi
if ! timeout 15 git ls-remote --exit-code "$REPO_URL" &>/dev/null; then
echo "sin conexión con el remoto, me quedo con el estado local"
elif [[ ! -d "$REPO_DIR/.git" ]]; then
echo "clonando arch-setup en $REPO_DIR..."
mkdir -p "$(dirname "$REPO_DIR")"
timeout 30 git clone "$REPO_URL" "$REPO_DIR" && echo "clon inicial OK" || echo "clone falló"
if timeout 30 git clone "$REPO_URL" "$REPO_DIR"; then
echo "clon inicial OK"
else
notify_fail "pkgsync: pull falló" "no se pudo clonar $REPO_URL, revisa $LOG"
fi
else
if timeout 30 git -C "$REPO_DIR" pull --ff-only origin main; then
echo "git pull OK"
else
echo "git pull falló (conflicto local o sin red); sigo con el estado que haya"
# ls-remote de arriba ya confirmó que hay red -- esto es un conflicto
# real (fast-forward imposible), no un simple "sin conexión".
notify_fail "pkgsync: pull falló" "'git pull --ff-only' falló (conflicto local, revisa $REPO_DIR a mano); sigo con el estado que haya"
fi
fi
+266
View File
@@ -0,0 +1,266 @@
#!/usr/bin/env bash
# Paquetes base, sourceados por kde/setup.sh (KDE Plasma es el único DE que
# instala este repo -- ver README "Decisiones").
# AUR-first: lo que está en repos oficiales va por pacman, el resto por paru.
# --- Repos oficiales (pacman) ---------------------------------------------
# Sincronizado con la instalación real de la máquina maestra. Si un paquete no
# está presente aquí, se elimina del instalador.
PACMAN_COMMON=(
# base / shell / CLI moderno
zsh starship fzf ripgrep bat eza btop zoxide fd tldr fastfetch 7zip vim usbutils
# dev toolchain (Podman en vez de Docker + capa de compat)
git base-devel podman podman-docker buildah
nodejs npm python python-pip rustup github-cli
# sync de dotfiles (repo arch-configs) -- en repos oficiales desde 2.x
chezmoi
# apps de escritorio que SÍ están en extra
bitwarden dbeaver gimp syncthing remmina freerdp apostrophe guvcview vlc vlc-plugin-ffmpeg
# diccionarios (corrector ortográfico en apps GTK/Qt)
hunspell-en_us hunspell-es_es
# fuentes / iconos compartidos
ttf-jetbrains-mono-nerd ttf-firacode-nerd noto-fonts noto-fonts-emoji
# red / utilidades base
networkmanager flatpak openssh networkmanager-openvpn openvpn tailscale wpa_supplicant
# audio (PipeWire reemplaza a PulseAudio)
pipewire pipewire-alsa pipewire-jack pipewire-pulse wireplumber libpulse
gst-plugin-pipewire gst-plugins-good gst-plugins-bad gst-libav
# bluetooth: stack + daemon comunes; la UI (bluedevil) se instala explícito
# en kde/setup.sh.
bluez bluez-utils
# hardware: mismo par de máquinas físicas (ver PROJECT_CONTEXT.md)
amd-ucode sof-firmware btrfs-progs zram-generator cpupower power-profiles-daemon
ibus wl-clipboard wmctrl smartmontools
# gvfs backend para montar móviles Android por MTP (Dolphin/Nautilus)
gvfs-mtp
# kernels: último + LTS como fallback ante actualizaciones rotas
linux linux-lts linux-lts-headers linux-firmware
# rollback de actualizaciones atómicas sobre btrfs. inotify-tools es
# dependencia OPCIONAL de grub-btrfs (pacman --needed no la instala sola)
# pero grub-btrfsd la necesita para arrancar -- sin ella el daemon muere
# con exit code 1 y el menú de GRUB nunca ve los snapshots (visto en real
# en Vampire-HM80: paquetes instalados pero grub-btrfsd en 'failed').
snapper snap-pac grub-btrfs inotify-tools
# bootloader (antes hardcodeado en kde/setup.sh, install_bootloader()
# también los instala explícito -- duplicado inofensivo con --needed, pero
# ahora viven aquí para que pkgsync pueda declararlos/protegerlos)
grub efibootmgr
# drivers gráficos open-source (antes hardcodeados en kde/setup.sh;
# mismo hardware físico en ambas máquinas, ver PROJECT_CONTEXT.md)
mesa vulkan-icd-loader vulkan-radeon vulkan-intel intel-media-driver
base
podman-compose
sudo
telegram-desktop
nano
android-tools
lzip
waydroid
glab
opencode
sshfs
isoimagewriter
)
# --- Flatpak ---------------------------------------------------------------
# Aplicaciones que en la máquina maestra están instaladas como Flatpak.
# Antes algunas de estas vivían en AUR (p.ej. heroic-games-launcher-bin,
# karere); ahora se gestionan desde Flathub.
FLATPAK_COMMON=(
com.heroicgameslauncher.hgl
io.github.tobagin.karere
org.gnome.gitlab.cheywood.Buffer
)
# --- AUR (paru) ------------------------------------------------------------
# helpwire: es un .deb; no hay paquete AUR estable -> se convierte con debtap
# (lo gestiona aparte la función install_helpwire de setup.sh).
# claude-code: NO va por AUR (se desincroniza del release real); se instala
# con el instalador nativo oficial, función install_claude_code() más abajo.
AUR_COMMON=(
zen-browser-bin
vscodium-bin
betterbird-bin
onlyoffice-bin
ungoogled-chromium-bin
ventoy-bin
woeusb-ng
bibata-cursor-theme-bin
pamac-all
yay
# herramientas adicionales presentes en la máquina maestra
autofirma debtap gotify-desktop stably-orca-bin
codexbar-cli
metapac
paru
spotifast-bin
)
# --- helpers usados por los setup.sh --------------------------------------
# Ejecuta un paso NO crítico: si falla, lo registra en FAILED_STEPS y
# CONTINÚA en vez de abortar todo el script (set -e no debe tirar una
# instalación de 1h por un mirror caído o un D-Bus que falta en el chroot).
FAILED_STEPS=()
run_optional() {
local desc="$1"; shift
echo ">> $desc"
if ! "$@"; then
FAILED_STEPS+=("$desc")
echo "!! Falló (no crítico, revisa a mano luego): $desc" >&2
fi
}
install_pacman() { pacman -S --needed --noconfirm "$@"; }
# paru se construye como usuario normal (makepkg no corre como root).
# RUSTUP_HOME/CARGO_HOME explícitos: bajo el fakeroot de makepkg, HOME pasa a
# /root y rustup intenta crear /root/.rustup (permission denied, uid real != root).
# rustup respeta RUSTUP_HOME antes que HOME, así que apuntarlo al home del usuario
# arregla los paquetes Rust (p.ej. karere).
as_user() {
sudo -u "$INSTALL_USER" bash -lc \
"export RUSTUP_HOME=\$HOME/.rustup CARGO_HOME=\$HOME/.cargo; $*"
}
bootstrap_paru() {
command -v paru &>/dev/null && return 0
install_pacman git base-devel
# rustup sin toolchain por defecto no expone cargo y rompe el build de paru
as_user 'rustup default stable'
# paru (fuente, no paru-bin): el binario de paru-bin enlaza contra una
# libalpm.so concreta y rompe si la del sistema nuevo difiere; compilar de
# fuente enlaza contra la libalpm instalada.
as_user 'cd /tmp && rm -rf paru && \
git clone https://aur.archlinux.org/paru.git && \
cd paru && makepkg -si --noconfirm'
}
install_aur() {
# Uno a uno: que un PKGBUILD roto (p.ej. karere) no se lleve por delante el
# resto del lote ni el chezmoi init posterior. Avisa de los que fallen.
local p failed=()
for p in "$@"; do
as_user "paru -S --needed --noconfirm $p" || failed+=("$p")
done
((${#failed[@]})) && echo "!! AUR falló (revisa a mano): ${failed[*]}" >&2
return 0
}
# archinstall 4.3 instala el paquete grub pero NO ejecuta grub-install con
# nuestro esquema (/boot ext4 separado + ESP): el sistema queda sin bootloader.
# Lo hacemos aquí (root, dentro del chroot). Idempotente.
install_bootloader() {
install_pacman grub efibootmgr
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB --recheck
grub-mkconfig -o /boot/grub/grub.cfg
}
# HelpWire no tiene AUR: descargar .deb, convertir con debtap, instalar.
install_helpwire() {
local url="https://www.helpwire.app/downloads/operator/linux/helpwire-operator.deb"
install_aur debtap
as_user "sudo debtap -u || true"
# debtap nombra el paquete por el campo Package del .deb ('helpwire', no
# 'helpwire-operator'): convertir en un dir limpio e instalar el único .zst,
# sin depender del nombre exacto.
as_user "d=\$(mktemp -d) && cd \"\$d\" && curl -fsSL -o hw.deb '$url' && \
yes '' | sudo debtap -Q hw.deb && \
sudo pacman -U --noconfirm \"\$d\"/*.pkg.tar.zst && rm -rf \"\$d\""
# debtap deja el .desktop y el icono dentro de /opt y su scriptlet (roto) no
# los registra, así que HelpWire no sale en el menú: lo hacemos a mano.
local hw=/opt/HelpWire/Operator/desktop
install -Dm644 "$hw/helpwire-operator.desktop" /usr/share/applications/helpwire-operator.desktop
install -Dm644 "$hw/helpwire-operator_128.png" /usr/share/icons/hicolor/128x128/apps/helpwire-operator.png
}
# Claude Code: instalador nativo oficial (no AUR, se autoactualiza). Instala
# en ~/.local/share/claude con symlink en ~/.local/bin/claude; no necesita
# root ni flags especiales para correr sin interacción.
install_claude_code() {
as_user "curl -fsSL https://claude.ai/install.sh | bash"
}
# Instala aplicaciones desde Flathub. Se usa para apps que en la máquina
# maestra están como Flatpak, aunque antes estuvieran en AUR.
install_flatpaks() {
echo ">> Instalando Flatpaks comunes..."
if ! command -v flatpak &>/dev/null; then
echo "!! flatpak no está disponible; omitiendo Flatpaks." >&2
return 0
fi
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
local app failed=()
for app in "$@"; do
flatpak install -y --noninteractive flathub "$app" || failed+=("$app")
done
((${#failed[@]})) && echo "!! Flatpak falló (revisa a mano): ${failed[*]}" >&2
return 0
}
# pkgsync: sincroniza qué paquetes hay instalados (pacman+AUR+flatpak) entre
# las dos máquinas, igual que chezmoi sincroniza los dotfiles. Instala metapac
# (motor de diff/aplicación), despliega pkgsync.sh y sus dos servicios de
# usuario, y deja el group file de metapac listo. Ver pkgsync/ en este repo.
install_pkgsync() {
echo ">> Instalando pkgsync (metapac + servicios de sincronización de paquetes)..."
install_aur metapac
as_user "mkdir -p ~/.local/bin ~/.config/systemd/user ~/.config/metapac/groups"
as_user "cp '$REPO_DIR/pkgsync/pkgsync.sh' ~/.local/bin/pkgsync.sh && chmod +x ~/.local/bin/pkgsync.sh"
as_user "cp '$REPO_DIR/pkgsync/systemd/'*.service ~/.config/systemd/user/"
as_user "~/.local/bin/pkgsync.sh bootstrap"
as_user "systemctl --user enable pkgsync-push.service pkgsync-pull.service"
# ya lo activa el run_once de chezmoi, pero por si este DE se instala algún
# día sin chezmoi: idempotente, no hace nada si ya está activo.
loginctl enable-linger "$INSTALL_USER" 2>/dev/null || true
}
# Configura snapper + snap-pac + grub-btrfs para rollback de actualizaciones.
# snap-pac crea snapshots automaticos antes/después de cada 'pacman -Syu'.
# grub-btrfs añade las entradas de snapshots al menú de GRUB.
configure_btrfs_rollback() {
echo ">> Configurando rollback btrfs (snapper + snap-pac + grub-btrfs)..."
# Asegurar que / es btrfs; si no, no hacemos nada.
if ! findmnt -nro FSTYPE / | grep -q btrfs; then
echo "!! Advertencia: / no es btrfs; omitiendo configuración de snapper." >&2
return 0
fi
# Crear configuración de snapper para / si no existe.
if [[ ! -f /etc/snapper/configs/root ]]; then
snapper --no-dbus -c root create-config /
fi
# Solo queremos snapshots automáticos de pacman (snap-pac), no por tiempo.
# Esto evita que se acumulen snapshots innecesarios y se coma espacio.
# ALLOW_GROUPS=wheel: snapper tiene su propia lista de permitidos aparte de
# los permisos de fichero de /.snapshots -- sin esto, "snapper -c root
# list" da "Sin permisos" para cualquiera que no sea root, aunque el chmod
# de abajo esté bien (visto en real en Vampire-HM80).
snapper --no-dbus -c root set-config \
TIMELINE_CREATE=no \
TIMELINE_LIMIT_HOURLY=0 \
TIMELINE_LIMIT_DAILY=0 \
TIMELINE_LIMIT_WEEKLY=0 \
TIMELINE_LIMIT_MONTHLY=0 \
TIMELINE_LIMIT_YEARLY=0 \
NUMBER_LIMIT=10 \
NUMBER_LIMIT_IMPORTANT=10 \
ALLOW_GROUPS=wheel
# Permisos del directorio de snapshots en sí (necesario además de
# ALLOW_GROUPS de arriba, snapper comprueba las dos cosas).
chmod 750 /.snapshots
chown :wheel /.snapshots 2>/dev/null || true
# Habilitar el daemon que regenera el menú de GRUB cuando cambian los snapshots.
systemctl enable grub-btrfsd
# Regenerar menú de GRUB con las entradas de snapshots.
grub-mkconfig -o /boot/grub/grub.cfg
}