danirod

Diario - page 2

Lo guay de un blog es que puedes contar cosas que no caben en un tweet. Páginas escritas últimamente:

⏱ Qué estoy haciendo ahora: danirod.es/now
💻 Con qué grabo: danirod.es/equipo
🎒 Qué llevo en la mochila: danirod.es/edc

Control de versiones en dpkg

Es demasiado fácil perder el control sobre los paquetes instalados a través de un gestor de paquetes, sobre todo cuando esos paquetes tienen dependencias. Para evitar esto, la última vez que tuve que resetear mi workstation (equipada con Debian), hice un sistema para asegurar que tengo una manera de controlar qué paquetes entran y salen de mi ordenador.

En un directorio aislado del sistema creé un repositorio de Git. Después, fabriqué un script que cuando es llamado, vuelca la lista de paquetes instalados en un archivo dentro de ese repo y hace commit:

#!/bin/sh

cd /backups/dpkg
dpkg --get-selections > /backups/dpkg/selections.lst
git commit -am "Updating selection list after dpkg change"

Cada vez que se llame este script, si ha habido cambios en los paquetes instalados (no en sus versiones), se actualizará el archivo selections.lst y se hará un commit. Si no ha habido cambios, dirá nothing to commit, working tree clean, y ya.

Para que todo funcione como un reloj, configuré el script como un hook en apt. Creé un archivo en /etc/apt/apt.conf.d/06dpkghook con el siguiente contenido:

Dpkg::Post-Invoke { "if [ -x /sbin/dpkg-hook ]; then /sbin/dpkg-hook; fi" ; };

Un poco más complejo de lo que me gustaría, pero me venía bien controlar errores en este script. Aun así, como el código de retorno de git commit cuando no hay cambios es 1, y como mi script Bash no trata ese caso con un || return 0, a veces apt muestra mensajes de error un poco peculiares cuando hago un apt upgrade y no cambia la lista de paquetes instalados. Llevo 6 meses diciendo que tengo que editar el script, y aquí sigo.

No obstante, en general funciona que es una maravilla, y ya me ha permitido alguna vez detectar dependencias que no se eliminan bien al quitar el paquete que depende en ellas, o hacer rollbacks al instalar paquetes que causan problemas de estabilidad en mi workstation.

Captura de pantalla de tig
El repositorio con mis paquetes visualizado en tig.

Tengo pendiente portarlo a otros gestores de paquete, porque Homebrew en mi portátil personal es todavía más guarro con el tema dependencias y estoy harto de me cueste la vida hacer limpieza por no saber qué paquetes son dependencias y cuáles no.

“Horario flexible de 10 a 18 horas.” Flexiblemente inconciliable con la vida personal, querrás decir. Me envían cada cosa al LinkedIn…

Nuevo aspecto visual en danirod.es

Nuevo aspecto visual en danirod.es. Ha estado aproximadamente una semana en pruebas en mi ordenador siendo visitado por varios tipos de pantalla y navegadores para asegurar que todo se ve bien, pero ya habrá tiempo de pulir los posibles errores que aparezcan ahora que está en producción. Changelog:

  • La interfaz de usuario de la página web ahora está mucho menos cargada, sobre todo para las actualizaciones de estado y las fotografías. Fuera barra lateral, fuera menús de navegación complejos.

  • Traduce la interfaz de usuario al español ahora que he adoptado el español para nuevas publicaciones. Tengo pendiente de ver qué hago con el contenido viejo. Idealmente, me gustaría tenerlo en varios idiomas, pero no es tan fácil implementar negociación de contenido en NGINX –mi servidor–, como lo es en Apache.

  • Crea un nuevo feed llamado Diario para las actualizaciones de estado sueltas. La diferencia entre una nota y un artículo es que los artículos de blog tardan más en desarrollarse, pero las notas son más improvisadas. Las notas pueden ser del tamaño de un tweet, o pueden tener formato –como esta nota.

Por favor, dejad ya de usar fotos de stock de pantallas de ordenador con código PHP sacado de algún plugin de WordPress para ilustrar artículos sobre cosas que no tienen que ver con WordPress. 👏

Sobre git merge-base

Con el comando git merge-base puedes obtener el hash del commit más reciente que es común a las ramas que le digas. Esto viene bien si brancheas una rama upstream (pongamos master o develop) y luego quieres ver en qué commit hiciste el git checkout -b (por ejemplo, como parte de un rebase que estés haciendo). Esto es válido incluso cuando ha habido commits en la rama upstream después de que creaste la rama (que suele ser el caso).

Pongamos un repo en el siguiente estado:

A --- B --- C      master
       \
        \-- D      feature/x

Entonces se cumple que:

git merge-base master feature/x
[hash del commit B]

If you’re working on a Git repository and your team makes exhaustive use of branches, every time you run git fetch, Git will download new commits on those remote branches, even if you don’t plan on having local branches tracking their remote pair. Once those branches are removed by your teammates in your remote repositories, git fetch won’t download new commits for those branches, but it won’t notify you that the branch has been removed either.

If you want to remove remote branches in your local repository that have been already removed from the remote repository, you can run git fetch --prune. This will download new commits from existing or new branches, but also if a branch has been removed from the remote repository, you’ll have that branch removed from your local copy as well.

If you don’t want to forget about prune, you can also configure your Git repository to automatically prune removed branches by running git config remote.origin.prune true. Replace origin with the name of the remote repository you have the setting to have effect on. Every time you run git fetch on that remote, deleted branches will get removed too.

I made a pre-commit hook that forbids committing directly to master or devel branches on Git. This is something that you don’t want to do if you find yourself working under some Git model such as GitFlow or GitHubFlow. Specially if you are using GitFlow. Specially if you are not alone in your project.

Yes, I know that you can fix this after you make the commit by creating a new branch and then moving back the branch pointer for devel or master a few commits back, but I would waste less time if Git actually forbid me to make the commit in first place.

#!/bin/bash

FORBIDDEN_BRANCHES="master devel"
BRANCH=$(git rev-parse --abbrev-ref HEAD)

BOLD=$(tput bold)
YELLOW=$(tput setaf 3)
GRAY=$(tput setaf 8)
RESET=$(tput sgr0)

for branch in $FORBIDDEN_BRANCHES; do
 if [ "$branch" == "$BRANCH"  ]; then
  echo
  echo "${BOLD}You are currently in branch ${YELLOW}$BRANCH${RESET}."
  echo "    Committing from this branch would be a bad idea."
  echo "    Would you mind... changing to a different branch first?"
  echo
  echo "${GRAY}(Or you know, using the -n flag to bypass this message."
  echo " Hey, don't look at me! I'm a script, not a police officer!)"
  echo $RESET
  exit 1
 fi
done

You can also download the hook as a shell script. I don’t mind. It wouldn’t be that long if I didn’t declare all these variables but, hey, colors!

The hook in action
The hook in action. Note that I can still force the commit if I need or want to by using `git commit -n` (read the docs).

Sometimes I wish we had something like SSHFS but for executable files.

Yes, I’m aware of the ssh [hostname] [command] syntax. But, just like when you mount a remote file system using something like SSHFS or DAVFS, your local applications can interact with that remote directory without them ever noticing files are not actually in your computer (aside from the network latency), I just wish the same could be done with executable files.

You run (or another application runs) ./hello, and that gets translated into ssh foo@192.168.4.4 /usr/bin/hello. stdin is piped to the remote server, stdout is piped from the remote server. It doesn’t matter if you are on a Mac and /usr/bin/hello is a Linux ELF binary file, because the file is actually executed on a remote server. A use case for this would be being able to run remote debuggers without having to rely on fancy IDEs that support this feature, such as debugging a remote Node.js or Python script in a server or in a Vagrant box.

I guess the people behind Plan 9 actually had their point.

TIL: Jekyll’s incremental builds do not trigger rebuilds of pages that make use of the site.posts variable, which means that, if you have a page that lists recent posts using site.posts, and you update a post, the page won’t be regenerated. This can be fixed by adding regenerate: true to those pages that you want to always be regenerated on each incremental rebuild.

I make use of incremental builds to speed up the compilation time of my site in development mode, but I also have a lot of feed pages on my site, and it always drove me bonkers that my feed pages were not being regenerated, because apparently I didn’t see this footprint.