Generador de sitios estáticos con "Make" (2022)
(karl.berlin)- Los sitios estáticos son fáciles y rápidos de alojar y requieren poco mantenimiento, así que para un sitio simple puede bastar con unas pocas líneas de Makefile sin tener que aprender un constructor aparte
- La estructura central consiste en mantener la disposición de archivos de
source, añadirheader.htmla los HTML y copiar tal cual el resto de los archivos abuild make buildempareja archivos de entrada y salida confind source -type fypatsubst, y aplica reglas de make distintas para.htmly para los demás archivos- El resaltado de la página actual, la conversión de Markdown, el servidor local, la reconstrucción automática al detectar cambios y el despliegue en GitHub Pages pueden añadirse como targets adicionales cuando haga falta
- Como tiene pocas dependencias y es claro qué partes modificar, en sitios con requisitos pequeños un flujo de compilación hecho a mano puede ser más rápido que un generador genérico
Sitio estático básico hecho solo con Makefile
- Un generador de sitios estáticos produce resultados fáciles de alojar y rápidos, con una carga de mantenimiento muy baja
- Para un sitio simple, puede ser más rápido y satisfactorio escribir un script propio que aprender y adaptar un constructor de sitios existente
- Si se trata de un sitio común sin marcas de tiempo de actualización automática ni feed RSS, basta con una configuración todavía más simple que un script para blogs
-
Requisitos básicos
- Todos los archivos de entrada se colocan en el directorio
source, y en la salida se mantiene la misma estructura de directorios - A todos los archivos HTML se les agrega
header.htmlal inicio - Los archivos que no son HTML se copian sin cambios al directorio
build
- Todos los archivos de entrada se colocan en el directorio
-
Reglas del Makefile
- El target
buildtiene dependencias que hacen corresponder todos los archivos bajosourcecon archivos de salida bajobuild - No realiza trabajo por sí mismo, pero sirve como punto de entrada para que se apliquen las reglas adecuadas a cada archivo
- La regla
build/%.htmldepende desource/%.html,header.htmlyMakefile mkdir -p $(dir $@)crea el directorio de salida, ycat header.html $< > $@concatena el encabezado con el HTML de entrada para generar la salida- La regla
build/%copia tal cual los archivos que no son HTML concp $< $@ - Con solo este
header.htmly estas reglas, ejecutarmake buildcrea un directoriobuildque puede abrirse localmente o subirse a un servidor web
- El target
Ejemplos de extensión y targets auxiliares
-
Mostrar la página actual
- Si se resalta la página actual en la navegación, el visitante puede ver de inmediato en qué parte del sitio está
- En el ejemplo, se busca el enlace de navegación y se le agrega la clase
current sed -E 's|(href="$(subst source,,$<))|class="current" \\1|' header.html | cat - $< > $@- La forma exacta de sustitución debe adaptarse a la estructura de marcado que se esté usando
-
Generar HTML desde Markdown
- Si no quieres escribir HTML directamente o el contenido existente está en formato Markdown, puedes conectar por tubería un convertidor de Markdown a HTML
- El ejemplo usa smu
smu $< | cat header.html - > $@- La regla base sigue asumiendo que
build/foo.htmlse genera a partir desource/foo.html - Para archivos Markdown, hay que conservar también el sufijo
.htmlo modificar la regla para que busque archivos.mdcomo entrada
-
Vista previa local
- Algunos sitios no se previsualizan correctamente si se abre el archivo local directamente en el navegador; una razón común es usar enlaces absolutos en lugar de enlaces relativos
- Python ya está instalado en muchos sistemas e incluye un servidor web adecuado para este uso
- Target
serve:python -m http.server -d build
-
Reconstrucción automática al cambiar archivos
- Mientras se trabaja en el sitio, volver a compilar manualmente cada vez es incómodo
- Con
entrse puede ejecutar automáticamentemake buildcuando cambiensource,header.htmloMakefile - Target
watch:find source header.html Makefile | entr make build - Si quieres evitar dependencias, también puedes usar
inotifywait
-
Despliegue en GitHub Pages
- Si el repositorio está en GitHub, es natural considerar alojar el HTML resultante en GitHub Pages
- Ajustar el comando de despliegue para no tener que preocuparse por los detalles de git es un poco complicado, pero puede resolverse con
git worktree - El método se basa en el artículo de Sangsoo Nam
- Se agrega la rama
gh-pagescomo worktreepublic_html - Se copia
build/*apublic_html - Se ejecuta
git add --all,git commit -m "Deploy to github pages",git push origin gh-pages - Al final, se elimina el worktree con
git worktree remove public_html
-
Ejemplo real
- Una página creada con este enfoque puede verse en karlb/astridbartel.de
- Un generador de sitios estáticos completo puede empezar con seis líneas de Makefile y modificarse rápido según las necesidades, sin dependencias extrañas ni carga de mantenimiento
1 comentarios
Opiniones en Hacker News
Luego agregué funciones como noticias y feed RSS, listados automáticos de artículos de investigación y materiales de clases, y una lista de libros filtrable por etiquetas. Sigue siendo un Makefile, pero el Makefile en sí se volvió incluso un poco más simple; en cambio, llama a scripts Bash que usan las excelentes utilidades xml2 y 2xml para manejar HTML línea por línea. Principalmente aprovecha utilidades básicas como
grepysed.También le agregué algunos git hooks que llaman automáticamente a
makecuando hace falta y, en particular, en el servidor remoto donde está alojado el sitio, al hacer push al repositorio se reconstruye la versión pública.Lleva años funcionando muy bien, y el historial de git se remonta a 2009. Al ver los primeros commits, eran
beccad7 (FIRST_VERSION) Initial commit,d1cc6d7 adding link to Google Reader shared items,6ccfd0c fix typo,d337959 adding link to Identi.ca account, y de verdad ya pasaron 15 años.xml2y2xmlparecen un proyecto algo abandonado.https://github.com/cryptorick/xml2
https://manpages.debian.org/unstable/xml2/2xml.1.en.html
source/, no se borra enbuild/.En mis proyectos, reconstruir todo el sitio sigue siendo lo bastante rápido, así que opté por eliminar toda la carpeta build antes de reconstruir.
https://github.com/jez/jez.github.io/blob/source/Makefile#L1...
Esto anula en buena medida la compilación incremental, que es una de las grandes razones para usar un sistema de build, pero aun así, si sé qué página quiero regenerar, puedo ejecutar
makedirectamente sobre ese archivo.Me gustaría saber si hay algún workaround común para este patrón en Makefiles.
Uso la función
shellde GNU Make para listar archivos y la funciónfilter-outpara filtrar las salidas “esperadas”. Es un hack feo, pero garantiza que se ejecute cada vez al hacer que el comando se ejecute como parte de una expansión de variable mediante la funciónshell.Enlace al Makefile: https://github.com/jaredkrinke/make-blog/blob/main/Makefile
Además de una opción nuclear como
make clean, se pueden tener targets específicos de Make para borrar y renombrar. Por ejemplo, hacer quemake rm sourcefileomake mv sourcefile newsourcefilese encarguen tanto del borrado/cambio de nombre del origen como del destino generado.En la práctica, incluso en blogs o proyectos online bastante grandes, el ciclo
make clean/make allsuele ser lo bastante rápido, y muchas veces es necesario al modificar plantillas o elementos de diseño del sitio. Si el tamaño llega al punto de preocuparte por el tiempo de reconstrucción, quizá tenga más sentido usar un CMS real que gestione las fuentes en una base de datos y genere dinámicamente al acceder el cliente.make clean?rm/%.html:@rm -f source/%.html build/%.htmlSe ejecutaría con
$ make rm/page.html.prunejustamente para este propósito.http://neilmitchell.blogspot.com/2015/04/cleaning-stale-file...
Pero creo que la mejor solución que también funciona con
makees tener un targetmake distque genere el archivo final.tar.gzde los resultados. Si escribes bien las reglas, no incluirá archivos obsoletos. En proyectos grandes puede ser lento, pero durante el desarrollo de todos modos no hace falta usarlo, solo al momento de lanzar. La compilación incremental sigue siendo posible, y solo el.tar.gzfinal se crea desde cero.blog.shmencionado en este artículo.Lo tomé y lo modifiqué para crear
barf, mi generador de sitios estáticos minimalista. No existiría si Karl no hubiera publicado ese gran trabajo.[0]: https://github.com/karlb/karl.berlin/blob/master/blog.sh
[1]: https://barf.bt.ht
shite, y con eso construyo mi sitio. El nombre insinúa la calidad del software :)Lo que más me gusta es que hasta ahora no he tenido que actualizar nada, y parece que nunca tendré que hacerlo. Lo segundo mejor es que tiene hot reload sin JavaScript.
[1] https://github.com/adityaathalye/shite
[2] https://evalapply.org
m4, se puede mantener el mismo enfoque esquelético y aun así ganar un poco más de flexibilidad.Hace unos 20 años mantuve un sitio web pequeño hecho de esa forma. Pero, salvo por sitios web personales, no sé si hoy este modelo encaje bien. Esto se debe a que, en esencia, este enfoque obliga a asumir un rol de Web 1.0. Todos los usuarios que contribuyen tienen que dominar HTML, o alguien tiene que encargarse de las tareas ingratas de “webmaster”
[1] https://en.wikipedia.org/wiki/M4_(computer_language)
m4”Empiezas un proyecto limpio y prometes que esta vez no vas a tocar
m4. Luego, para reducir código repetido, terminas metiendo una pequeña llamada am4Un año después estás escarbando entre cinco capas de expansión de macros para descubrir por qué la palabra “cat” desaparece silenciosamente del sitio web, y al final encuentras que un desarrollador junior intentó implementar un bucle for por su cuenta, en vez de copiarlo del manual, y arruinó las comillas
Una vez resuelto el problema inmediato, decides que depurar DSL es demasiado difícil, así que traes el archivo de macros M4 que venías copiando de proyecto en proyecto. Después pasas un día cambiando todos los usos de
definepor macros generadoras de macros que agregan comentarios a la salida para que funcione el script que genera trazas de pilaEn el próximo proyecto pondrás una regla absoluta. ¡Nada de
m4! Claro, quizá se pueda hacer una excepción en ese único lugarm4operl, recomiendo usar SGML, que es la base y el superconjunto común de HTML y XMLSGML ofrece de forma nativa macros de texto con verificación de tipos sencilla, es decir, expansión de entidades, y también permite expansión de macros parametrizadas con conocimiento de tipos. Aquí, los tipos se refieren al tipo de contenido general de los elementos de marcado, es decir, qué elementos hijos se permiten y en qué orden, y también contempla expandir o escapar según contextos como atributos, CDATA y RCDATA
Expandir y escapar correctamente comentarios de usuarios potencialmente maliciosos, aplicando reglas de usuario que, por ejemplo, permitan marcado de nivel inline pero prohíban elementos
script, es algo que solo SGML puede expandir correctamente. También permite expandir Markdown o sintaxis wiki a HTML, importar contenido HTML externo/de sindicación, crear RSS y outlines para navegación, etc. Encaja bien con trabajos bastante complejos de preparación de sitios estáticos desde la línea de comandos[1]: https://sgmljs.net/docs/producing-html-tutorial/producing-ht...
[2]: https://sgmljs.net/docs/sgmlproc-manual.html
m4o buscar-y-reemplazar consed, estaría bueno probarenvsubstEs un programa que sustituye referencias a variables estilo Bash, por ejemplo
$TITLE, por el valor de una variable de entornoexport CURRENT="..."cat page.html | envsubtm4”, por favor, jamásm4ni siquiera es bueno como lenguaje de programación esotéricoQue haya funcionado en sendmail no alcanza para justificar nada
Cada vez que leo algo interesante aquí, me suscribo al feed. Ya sea un sitio de Wordpress, Bear Blog, Micro.blog, un blog de Havenweb o el feed de un sitio hecho a mano, lo agrego al módulo
Really Social Sitesde Hey HomepageEn última instancia, me gustaría publicar esta lista de blogs, como hace Kagi con la iniciativa Small Web. Aunque, si se quiere aportar calidad, la curaduría es clave, y cuando uno piensa en curaduría parece natural empezar una especie de revista online
¿De dónde saca la gente esa “sensación de derecho”, en el buen sentido, de que puede compartir cosas con otros? Me pregunto por qué asumen que a otros les va a interesar aquello en lo que están trabajando. A veces se siente como una competencia. Algo como: “para conseguir likes o exposición en el blog, tengo que construir algo lo más genial posible”
La colaboración obviamente es buena, y para eso todo tiene que ser público. Pero no me queda clara la línea entre “hago esto porque se ve genial” y “me esfuerzo por compartirlo con otros para obtener reacciones”
Decía que, aunque cambiara un solo archivo de prueba, con un único comando podía regenerar todo el artículo, incluyendo la ejecución de pruebas y la generación de gráficos
https://pages.github.com/
Lo mío quedó un poco sobrediseñado porque quería hot reload sin JavaScript, y fue un yak shaving divertido
La idea básica es la misma. Uso heredoc para las plantillas y un compilador que convierte texto plano en HTML; en mi caso es
pandoc. Uso un CSV intermedio para generar índices, y también tengo una técnica útil consedpara extraer el front matter. Clásico y bueno[1] https://github.com/karlb/karl.berlin/blob/master/blog.sh
[2] https://github.com/adityaathalye/shite
[3] Mi forma de hacerlo: https://github.com/adityaathalye/shite/blob/master/bin/templ...
Aunque tiene algunas limitaciones. Yo organizo los posts por namespace y pongo la fecha en la URL, y
makeno maneja eso directamente muy bienmakees que, en programas grandes compilados con compiladores lentos, hace que las recompilaciones incrementales sean mucho más rápidas cuando hay cambios pequeñosUn trabajo que tardaría 40 minutos en recompilarse completo puede terminar en unos 3 segundos
Si un sitio estático se genera desde cero en menos de 1 segundo solo haciendo
catde un encabezado común y unos cientos de archivos HTML, no hay ventaja en usarmakeen lugar de un script. Solo se corre el riesgo de hacer builds incompletos por bugs de dependencias.phonyAun así se puede mantener una separación como
make buildymake pushEn otros proyectos pequeños usé un script de shell muy chico como bundler improvisado. Insertaba CSS y JS dentro del HTML, y el objetivo era que también se pudieran servir localmente los archivos sin build