De WordPress a Hugo

Posted on Tuesday, June 16, 2020 in Uncategorized

De WordPress a Hugo

Hasta ahora, he estado usando WordPress para esta web. Es un software para blogs muy potente y completo, con un montón de plugins, skins, optimizaciones para buscadores, etc. Lleva más de 15 años en el mercado y sigue siendo desarrollado activamente. Es el líder indiscutible en blogs y es muy cómodo y rápido producir contenido con él.
Por otra parte, llevo dos años sin actualizar este blog, con una sola entrada en los últimos 5 años. Así que, ¿por qué migrarlo otra vez? Pues precisamente por eso.

Como sitio dinámico, WordPress requiere mantenimiento. Salen actualizaciones constantes no solo del propio WordPress, sino también de cada plugin. Perderse actualizaciones supone dejar el sitio expuesto a posibles nuevas vulnerabilidades que se descubran en WordPress o cualquiera de los plugins que estaba usando.

Además, como cualquier sitio dinámico, el contenido se genera en el servidor con cada visita, consumiendo recursos y haciendo la carga más lenta. He probado todo tipo de combinaciones que me fue posible para intentar mejorar el rendimiento del sitio. He probado a desactivar todos los plugins de WordPress que no fueran indispensables, a intentar optimizar la configuración de MySQL, Apache y PHP, utilizar cachés en WordPress, PHP y Apache, depurar PHP buscando cuellos de botella, usar el caché y la protección contra bots de Cloudflare... Nada de lo que probaba conseguía evitar que los tiempos de carga fueran penosos.

Todo esto era demasiado simplemente para un sitio con tan poca actividad, necesitaba poder desentenderme de él y que siguiera funcionando bien y poder seguir añadiendo contenido si algún día quería.
Y resulta que últimamente se estaba hablando bastante de Hugo, así que decidí probarlo.
Y me gustó.

Es simple. Extremadamente simple. WordPress es súper completo y potente. ¿Pero realmente necesito tanto para un blog donde simplemente cuento mis cosas? Pues no.
Lo que buscaba era simplicidad. Y Hugo es simple.
Ni siquiera necesita un servidor de bases de datos como MySQL o similar. Las entradas del blog simplemente se ponen en un directorio en formato HTML, Markdown, AsciiDoc o cualquiera de los formatos soportados y el sistema de compilación de Hugo crea el blog a partir de los contenidos de ese directorio. Así de simple. Si quieres crear una entrada nueva en el blog, simplemente añades un fichero al directorio.

Mientras no quieras añadir más contenido al blog, no hay ningún mantenimiento requerido, pues el blog generado es simplemente un directorio de ficheros HTML estáticos. Por el mismo motivo, la carga también es mucha más rápida, pues no requiere ningún tipo de procesamiento por parte del servidor más que enviar el fichero HTML, lo cual incluso puede ser acelerado aprovechando la opción SendFile de Apache.

Sin embargo, un sitio estático tiene algunas limitaciones. Mayormente, los comentarios de usuarios y el buscador, que no están soportados por Hugo out of the box, pero hay soluciones para esto.

Para los comentarios he usado Staticman, que es una API para recibir comentarios para un blog y convertirlos en una pull request para el repositorio donde se encuentra el código del sitio en GitHub.

Para las búsquedas he usado Fuse.js, que es una librería que permite hacer la búsqueda en el lado del cliente proporcionándole un fichero JSON con los contenidos en los que buscar.

Aparte de esto, mi migración de WordPress a Hugo tendría que cumplir ciertos requisitos:

  • Multilenguaje. Mi sitio de WordPress usaba WPML para ofrecer contenidos en inglés o español y permitir cambiar el idioma de la página vista actualmente.
    Esto es soportado directamente por Hugo, aunque no para taxonomías. WordPress permite establecer que una categoría llamada Noticias y una etiqueta llamada privacidad sean la traducción en español de la categoría News y la etiqueta privacy. Actualmente, Hugo no permite establecer esta relación entre taxonomías en distintos idiomas. He necesitado un hack para tener lo mismo en Hugo. Más o menos cumple su objetivo, pero no estoy seguro de que me guste del todo y puede que acabe buscando otra solución más adelante.
  • Romper el menor número de enlaces posible.
    Para la mayor parte del contenido esto no fue un problema, pero requirió algún que otro hack, especialmente para conservar los enlaces de taxonomías (categorías y etiquetas).
  • Un tema claro y sencillo.
    Para ello he usado el tema CleanWhite de Huabing Zhao y lo he modificado para soportar los hacks mencionados anteriormente, añadir soporte para Staticman, Fuse.js y multilenguaje y alguna cosa más (la lista completa se puede ver en el README).

Finalmente, necesitaba migrar todo el contenido que tenía en WordPress a Hugo. Es decir, a ficheros HTML con Front Matter.

Los ficheros estáticos simplemente se pueden copiar de wp-content/upload en WordPress a static/wp-content/upload en Hugo, pero para posts, páginas, categorías, etiquetas y comentarios, he creado varios scripts de Python que leen esos contenidos de la base de datos de WordPress y los escribie en los tipos de ficheros necesarios, lo que es HTML con Front Matter para todo excepto los comentarios, que son ficheros YAML como los generados por Staticman.

xkcd 1205: Is It Worth the Time?
xkcd 1205: Is It Worth the Time?

Teniendo en cuenta que es algo que solo haría una vez, tampoco quería invertir demasiado tiempo en los scripts de automatización. Es cuestión de encontrar el equilibrio entre reducir el tiempo que requeriría para hacer la migración manualmente sin invertir tanto tiempo en automatizarlo que la automatización no acabase siendo rentable, aunque para ello tuviera que hacer una revisión y ajustes manuales después de la automatización.

Por lo tanto, estos scripts no generan contenido que pueda ser usado directamente en Hugo, sino que el resultado aún necesitará cierto trabajo manual.

Concretamente, estas son algunas de las limitaciones:

  • Han sido desarrollados para un sitio con WPML. Para un sitio sin WPML, requerirá modificaciones.
  • Las entradas multilenguaje se generan correctamente conservando la relación entre ellas y el slug original de WordPress. Esto se hace usando el mismo nombre de fichero para ambos idiomas y añadiendo el sufijo de lenguaje. Sin embargo, Hugo soporta usar el mismo slug para ambos idiomas y WordPress no. En los casos en los que el slug es el mismo, el script genera un mensaje para revisar manualmente el contenido generado.
  • WordPress usa ese formato de HTML bastardo que es como una mezcla entre texto plano y HTML. Incluso aunque en WordPress hayas estado usando el editor HTML, este todavía permite crear saltos de línea y párrafos simplemente a base de incluir saltos de línea en vez de escribir el correspondiente código HTML <br /> o <p>. Esto se guarda tal cual (sin añadir las correspondientes etiquetas HTML) en la base de datos y es convertido por WordPress cuando genera la página. El código extraído de la base de datos también incluirá shortcodes de WordPress. En resumen, el texto en la base de datos de WordPress no es HTML listo para usar en otros sitios y necesita revisión manual.
  • No estoy seguro de que las etiquetas de los posts se apliquen correctamente para que funcionen con las etiquetas traducidas generadas por los mismos scripts.
  • Igualmente, el script para comentarios coge el código tal cual de la base de datos y necesitará revisión para añadir el HTML ausente o convertirlo a otro formato Markdown si es el que se usa en el blog Hugo de destino. Además, si el comentario es multilínea, el script no añadirá la indentación necesaria a cada línea y provocará un error en el parser YAML. Es necesario añadir manualmente la indentación YAML en los comentarios multilínea.

Los scripts se pueden encontrar en GitHub: wpml2hugo.