Gestión de paquetes y dotfiles en arch

8 minutos de lectura Publicado:

TL;DR

Antes usaba ansible para gestionar los paquetes de mi SO y mis dotfiles y no me gustaba. Cambié de ansible a aconfmgr, brew y stow. Aquí se puede ver lo viejo y aquí lo nuevo.

Hace tiempo me di cuenta de que me le dedicaba mucho tiempo a personalizar mi ordenador. Configuraciones varias, scripts, alias, programas, etc. Muchos de los programas no estaban en los repositorios comunes, ya que por aquel entonces aún usaba debian. Y mi solución fue hacer bastantes roles de ansible y un playbook de ansible que me gestionase todo. Tanto la instalación de todos los paquetes (algunos compilados, descargados de no se donde, etc) como de las configuraciones. Hice este articulo, de hecho. El repositorio git ya no está en la misma dirección, pero el código se puede ver aquí.

En ese momento me venia muy bien, por que necesitaba aprender ansible y como cualquier tonto con un martillo, todo lo que veia eran clavos. Pero la realidad es que ansible no es la mejor herramienta para este tipo de cosas, sobretodo si hablamos de ordenadores personales.

Entre eso y que debian es leeeeeento a la hora de actualizar paquetes, hay pocos repositorios no oficiales (o menos), decidí pasarme a Arch.

Arch tiene el gestor de paquetes oficial, pacman. Y además, como Slackware, tiene un repositorio de paquetes hechos por la comunidad, llamado Aur. Tienen menos calidad que cualquier paquete de oficial de Arch o de Debian, pero ahí están. No es raro que algún paquete se te rompa al actualizar y tengas que hacer algún apaño o abrir un issue. Hay varias opciones para instalar paquetes de Aur, ir a pelo y clonar el repositorio git y ejecutar las órdenes o usar un gestor de paquetes. Han habido muchos a lo largo de los años, como pacaur o yaourt.

Pero hace un tiempo está yay. Las ventajas de yay es que tiene los mismos parámetros que pacman, por lo que es fácil de usar. Y lo mejor, se sincroniza con pacman. Osea que cuando haces la típica actualización de sistema de sudo pacman -Syu, también te actualiza los paquetes de Aur. Muy útil.

Pero aún así se me quedaba corto, aún con lo grande que es Aur, le siguen faltando paquetes. Y por ello el resto los complemento con Homebrew. Homebrew nació como el gestor de paquetes que MacOS no tenia y necesitaba, pero con el tiempo se hizo compatible con GNU/Linux. No es mi preferido, ya que usa dependencias estáticas. Esto significa que si instalas el paquete yamllint de brew, como tiene de dependencia Python, te descargará python para que puedas usar yamllint, aunque tu distribución ya tenga python. Hay formas de evitar que lo haga, pero es incómodo y depende de la fórmula igual no puedes hacerlo. Por lo tanto, es la peor de las 3 opciones, aunque sigue siendo una opción, ya que no necesita root, es rápido y no da problemas.

Aún así, seguía con el problema de como reproducir los programas que tengo instalado. Decidí que en vez de una herramienta imperativa como es Ansible, lo que necesitaba era una herramienta declarativa. Tras luchar mucho para no saltar a Guix, que gestiona todo el sistema operativo de forma declarativa, con rollback incluido, busque otras herramientas en Arch. Y encontré lo que buscaba, aconfmgr y vi que brew tiene esa misma funcionalidad metida, llamada bundle.

Diferencia entre herramientas imperativas y herramientas declarativas

Antes de entrar en las herramientas, hablemos de las diferencias entre una herramienta imperativa y otra declarativa. Una herramienta imperativa “ordena” que hagas cosas. Si tu borras una orden, esta no se deshace la próxima vez que la ejecutes, sinó que simplemente no se ejecuta. Por ejemplo, si yo tengo un script así:

mkdir -p ejemplo/loQueSea
echo "holi" > ejemplo/loQueSea

Ejecuto el script y decido que no me gusta esa ruta y que quiero cambiarla:

mkdir -p ejemplo/estoMolaMas
echo "holi" > ejemplo/estoMolaMas

Al ejecutar el siguiente script, no deshacemos el primero. Es decir, sigue existiendo ejemplo/loQueSea con el contenido holi.

En cambio, una herramienta declarativa habria borrado el fichero ejemplo/loQueSea. De una forma mágica y transparente para nosotras, al ejecutar el primer script habria hecho algo como:

cambioAHacer=ejemplo/loQueSea
mkdir -p $camgioAHacer
echo "holi" > $cambioAHacer

Y al ejecutarlo por segunda vez, de forma mágica y transparente habria hecho:

rm $cambioAHacer
cambioAHacer=ejemplo/estoMolaMas
mkdir -p $camgioAHacer
echo "holi" > $cambioAHacer

Sé que es un ejemplo cogido con pinzas, pero espero que quede clara la diferencia.

De una forma más practica, si yo con ansible tenia una lista de paquetes tales como:

  • git
  • curl
  • zsh

Y lo ejecutaba, me los instalaba. Pero si luego cambiaba la lista a:

  • git
  • curl

No me desinstalaba zsh.

En cambio, con herramientas declarativas como aconfmgr si lo hace.

Aconfmgr y brew

Después de este intento de explicar las diferencias entre herramientas imperativas y declarativas, vamos al turrón. Con aconfmgr, tengo un ficherito tal que así:

AddPackage base # Minimal package set to define a basic Arch Linux installation
AddPackage acpi # Client for battery, power, and thermal readings
AddPackage alacritty # A cross-platform, GPU-accelerated terminal emulator
AddPackage alsa-utils # Advanced Linux Sound Architecture - Utilities
IgnorePackage amd-ucode # Microcode update image for AMD CPUs
AddPackage arandr # Provide a simple visual front end for XRandR 1.2.
AddPackage archlinux-themes-slim # Arch Linux branded themes for the SLiM login manager

Es gigante y aburrido. Hacer la lista manualmente es una locura, por lo que el primer paso es hacer un dump de todo lo que tengas con aconfmgr save. Esto te creará un fichero llamado 99-unsorted.sh. La idea es que en tu directorio de aconfmgr cojas los chorrocientos paquetes de ese fichero y los partas en varios, dividiendo de alguna forma razonable. Uno de mis ficheros es este, y he estructurado así el directorio:

arch
├── 1-ignore.sh
├── 10-base.sh
├── 15-utils.sh
├── 30-gui.sh
├── 50-media.sh
├── 70-development.sh
└── 90-misc.sh

aconfmgr también te gestiona los ficheros de configuración, pero yo he decidido no usar esa funcionalidad, por eso en el fichero 1-ignore.sh ignoro todos los ficheros.

Una vez organizaditos, sólo queda aplicar los paquetes. Esto se hace con aconfmgr apply. Cuidado al hacer esto por primera vez, sobretodo si habéis hecho limpieza al hacer dump. Puede que intente reinstalar paquetes. A mi me la lió la primera vez y tuve que reinstalar el kernel.

Y con esto, ya tendriamos gestionados los paquetes de Arch. Ahora van los de brew. El concepto es el mismo, se hace un dump, se limpia y se aplica. Se vuelcan los paquetes instalados con brew bundle dump. Esto crea un fichero llamado Brewfile. Lo modificamos como querramos y para aplicarlo se ejecuta brew bundle install. Si se quiere borrar cualquier paquete que no esté declarado ahí, se puede ejecutar brew bundle cleanup.

Dotfiles

Hasta ahora hemos hablado solo de los paquetes del SO, nada de dotfiles. Esto era lo que peor llevaba ansible, por que al final solo se trataba de hacer enlaces simbólicos y se le daban regular los cambios.

Ahora uso GNU Stow, que es más viejo que cagar sentado, que dicen en mi tierra. Pero hace lo que tiene que hacer, pone los ficheritos que tu quieres en su ruta. Está en todos los repositorios, por lo que se puede instalar con pacman.

La idea es crear una estructura de directorios en los que separas según alguna lógica. Yo me he decidido por esta estructura:

dotfiles
├── git
│   ├── .compartido
│   │   ├── Proyectos
│   │   │   ├── alias
│   │   │   │   └── .gitconfig
│   │   │   └── real
│   │   │       └── .gitconfig
│   │   └── Trabajo
│   │       └── .gitconfig
│   └── .gitconfig
├── i3
│   ├── .compton.conf
│   ├── .config
│   │   ├── dunst
│   │   │   └── dunstrc
│   │   ├── i3status.conf
│   │   └── rofi
│   │       └── config
│   ├── .i3
│   │   ├── config
│   │   └── scripts
│   │       ├── autolock.sh
│   │       ├── battery
│   │       ├── date
│   │       ├── lock
│   │       ├── sensors
│   │       └── volume-pulseaudio
│   └── .i3blocks.conf
└── shells
    ├── .alacritty.yml
    ├── .aliases
    ├── .bashrc
    ├── .config
    │   └── starship.toml
    ├── .functions
    ├── .Xmodmap
    └── .zshrc

La idea es fácil, tres directorios principales: git, i3 y shells. En estos, se reproduce la estructura del directorio en el que querrás colocar las configuraciones. Por ejemplo, todas mis configuraciones van bajo el $HOME. Entonces, si yo quiero tener en mi $HOME el fichero .alacritty.yml, en el directorio shells lo pongo tal cual, en .alacritty.yml.

Cuando tienes la estructura hecha, solo queda ejecutar stow. En mi caso:

stow -t ~ git
stow -t ~ i3
stow -t ~ shells

Y ale, los dotfiles instalados.

PD: La parte de git no la veréis completa en mi repositorio, ya que ahí salen mi nombre real que uso tanto para el trabajo como para proyectos personales que hago bajo mi nombre. Pero el resto es tal cual.

Empaquetado

La idea es que todo esto sea fácil tanto de mantener como de instalar en un ordenador nuevo, por lo que habrá que usar algo de pegamento para unirlo. Por ello, he hecho un script que se puede consultar aquí. Nada como usar bash como buen pegamento.

Este script acepta los siguientes parámetros a día de hoy:

  • install-packages: Instalar todas las herramientas necesarias (aconfmgr, brew, yay y oh-my-zsh por que si).
  • diff-bundle: Mostrar las diferencias en los paquetes instalados y los declarados.
  • apply-bundle: Aplicar los paquetes declarados.
  • update-bundle: Actualizar el sistema operativo.
  • dotfiles: Instalar los dotfiles.
Cualquier duda o comentario, me puedes contactar en los canales descritos en la página principal