Compare commits
2
Commits
8575c4ffa3
...
64bb53eadf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64bb53eadf | ||
|
|
598e8b52c8 |
+54
-7
@@ -23,6 +23,12 @@ 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"
|
||||
|
||||
# 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 +225,18 @@ 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"
|
||||
}
|
||||
|
||||
# --- subcomandos --------------------------------------------------------------
|
||||
cmd_bootstrap() {
|
||||
echo ">> pkgsync bootstrap"
|
||||
@@ -282,7 +300,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 +332,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 +374,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
|
||||
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
#!/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 kvirc 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
|
||||
papirus-icon-theme
|
||||
# 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
|
||||
)
|
||||
|
||||
# --- 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
|
||||
visual-studio-code-bin
|
||||
vscodium-bin
|
||||
betterbird-bin
|
||||
onlyoffice-bin
|
||||
spotify
|
||||
ungoogled-chromium-bin
|
||||
ventoy-bin
|
||||
woeusb-ng
|
||||
bibata-cursor-theme-bin
|
||||
googledot-cursor-theme
|
||||
pamac-all
|
||||
yay
|
||||
# herramientas adicionales presentes en la máquina maestra
|
||||
autofirma debtap gotify-desktop openlogi stably-orca-bin
|
||||
codexbar-cli
|
||||
metapac
|
||||
paru
|
||||
tauri-cli
|
||||
android-sdk-platform-tools
|
||||
)
|
||||
|
||||
# --- helpers usados por los setup.sh --------------------------------------
|
||||
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 -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 -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user