Crear y publicar sitios web estáticos con Emacs
y Org-Mode
Introducción
Hola, este es mi primer post en mi blog personal, y la principal
motivación del mismo es simplemente la de servir de impulso para
comenzar a dar algo de contenido a este sitio web que he creado para
depositar (algunos de) mis pensamientos y avances en (algunos de) mis
campos de interés, como la informática, el software libre y la
electrónica, principalmente. Y como no se me ha ocurrido nada mejor ni
más original, aquí simplemente expondré el proceso de creación del
sitio. Los archivos fuente del contenido del sitio han sido escritos
en el programa Emacs
, empleando el al mismo tiempo "modo" de
escritura y lenguaje de marcado ligero Org-Mode
/ org
. Para
generar los archivos en formato html que constituyen el contenido
final que estás viendo, he utilizado la función integrada de
publicación org-publish
, y para hospedarlo, el servicio de hospedaje
de sitios web estáticos de Source Hut
, plataforma destinada
porincipalmente al almacenamiento de repositorios de código similar a
las más populares GitHub
o GitLab
. En futuros posts
probablemente exponga y profundice un poquito más en estas cosas que
acabo de mencionar.
Dado que todavía no tengo mucha experiencia en el asunto, y que
tampoco me quería complicar demasiado la vida, he seguido un tutorial
para completar el proceso de crear y publicar esta página disponible
en
https://systemcrafters.net/publishing-websites-with-org-mode/building-the-site/,
página que, como las demás partes del proyecto System Crafters
,
recomiendo a quien tenga interés en los mismos temas que trato en este
blog. Eso sí, he adaptado un poco el proceso a mi propia forma de
hacer las cosas. Todo el proceso ha sido realizado en una máquina
con sistema operativo Linux Manjaro
.
Construcción del script y generación del sitio
Para pasar del formato org
en que está escrito este sitio al formato
html típico de páginas web1,
podemos elaborar un script que permita realizar el proceso de forma
automática. Esto lo conseguimos mediante el sistema ox-publish
y su función
org-publish
, generando todos los archivos .html
a partir de los
archivos fuente .org
, traduciendo entre formatos la estructura de los
documentos, enlaces, referencias, etcétera. Además, mediante Rsync
podremos copiar todos los archivos fuente y recursos del directorio
de origen con los del directorio de destino, que albergará todo el
contenido final del sitio2.
construir-sitio.el
La llamada a la función de exportación con toda la configuración de la
misma la pondremos en el archivo construir-sitio.el
.
Primero, necesitaremos cargar el sitema de publicación
ox-publish
mencionado:;; Cargar el sistema de publicación (require 'ox-publish)
Para publicar nuestro sitio, añadiremos el siguiente fragmento al archivo, con un puñado de opciones de personalización, si queremos:
;; Mensaje de confirmación al inicio (message "Build iniciado.") ;; Definimos el proyecto a publicar (setq org-publish-project-alist (list (list "bitib" :recursive t :base-directory "./content" :publishing-directory "./public" :publishing-function 'org-html-publish-to-html :with-author nil ; No incluir el nombre del autor :with-creator t ; Incluir las versiones de Emacs y Orgmode :with-toc t ; Incluir un índice de contenido :section-numbers nil ; No numerar las secciones :time-stamp-file nil ; No incluir marca de fecha y hora de creación :html-validation-link nil ; No incluir enlace de validación :language "es" ; Idioma ;; Plantilla para el renderizado de expresiones matemáticas como texto :html-mathjax-template "<link rel='stylesheet' href='/katex/katex.css'/> <script defer='defer' src='/katex/katex.js'></script> <script defer='defer' src='/katex/contrib/auto-render.js' onload='renderMathInElement(document.body);'></script>" ))) ;; Generamos los archivos de salida (content -> public) (org-publish-all t) ;; Mensaje de confirmación al finalizar (message "¡Build completo!") )
La primera lista es la de todas las configuraciones de proyectos (por si hubiera más de uno), mientras que la segunda, anidada, corresponde a este primer proyecto. Cada vez que llamemos al script, se generará todo el contenido del sitio web. Al ser un pequeño sitio web estático, no supone un gran gasto de tiempo y recursos, y nos ahorra complicaciones. Las posibles opciones de exportación son muchas. Para una lista completa de estas opciones podemos acudir al manual de orgmode.
En mi caso, he añadido algunas otras configuraciones para la personalización de la apariencia de mi sitio:
;; Opciones de configuración preliminares ;; Eliminar scripts predeterminados (setq org-html-head-include-scripts nil) ;; Eliminar estilo predeterminado (setq org-html-head-include-default-style nil) ;; Estilo personalizado (setq org-html-head "<link rel='stylesheet' href='/css/estilo.css'/>") ;; Cabecera personalizada (setq org-html-preamble "<nav><a href='/index.html'>CASA</a><a href='/posts.html'>POSTS</a></nav>") ;; Método de resaltado de sintaxis para los bloques de código (setq org-html-htmlize-output-type 'css)
Podemos hacer uso de paquetes adicionales para modificar o ampliar ciertos aspectos. Por ejemplo, para conseguir resaltado de sintaxis disponemos del paquete
htmlize
:;; Configurar directorio de instalación dentro de nuestro proyecto (require 'package) ;; Incluir repositorios de paquetes utilizados (setq package-user-dir (expand-file-name "./.packages")) (setq package-archives '(("melpa" . "https://melpa.org/packages/") ("elpa" . "https://elpa.gnu.org/packages/"))) ;; Inicializar el sistema de gestión de paquetes (package-initialize) (unless package-archive-contents (package-refresh-contents)) ;; Instalar dependencias (unless (package-installed-p 'htmlize) (package-install 'htmlize))
Indico un dirrectorio de instalación dentro del directorio del proyectopara que los paquetes utilizados para el sitio no se mezclen con los de mi configuración local personal.
Finalmente, el archivo quedaría como sigue:
;; Configurar directorio de instalación dentro de nuestro proyecto (require 'package) (setq package-user-dir (expand-file-name "./.packages")) (setq package-archives '(("melpa" . "https://melpa.org/packages/") ("elpa" . "https://elpa.gnu.org/packages/"))) ;; Inicializar el sistema de gestión de paquetes (package-initialize) (unless package-archive-contents (package-refresh-contents)) ;; Instalar dependencias (unless (package-installed-p 'htmlize) (package-install 'htmlize)) ;; Cargar el sistema de publicación (require 'ox-publish) ;; Opciones de configuración preliminares ;; Eliminar scripts predeterminados (setq org-html-head-include-scripts nil) ;; Eliminar estilo predeterminado (setq org-html-head-include-default-style nil) ;; Estilo personalizado (setq org-html-head "<link rel='stylesheet' href='/css/estilo.css'/>") ;; Cabecera personalizada (setq org-html-preamble "<nav><a href='/index.html'>CASA</a><a href='/posts.html'>POSTS</a></nav>") ;; Método de resaltado de sintaxis para los bloques de código (setq org-html-htmlize-output-type 'css) ;; Mensaje de confirmación al inicio (message "Build iniciado.") ;; Definimos el proyecto a publicar (setq org-publish-project-alist (list (list "bitib" :recursive t :base-directory "./content" :publishing-directory "./public" :publishing-function 'org-html-publish-to-html :with-author nil ; No incluir el nombre del autor :with-creator t ; Incluir las versiones de Emacs y Orgmode :with-toc t ; Incluir un índice de contenido :section-numbers nil ; No numerar las secciones :time-stamp-file nil ; No incluir marca de fecha y hora de creación :html-validation-link nil ; No incluir enlace de validación :language "es" ; Idioma ; Plantilla para el renderizado de expresiones matemáticas como texto :html-mathjax-template "<link rel='stylesheet' href='/katex/katex.css'/> <script defer='defer' src='/katex/katex.js'></script> <script defer='defer' src='/katex/contrib/auto-render.js' onload='renderMathInElement(document.body);'></script>" ))) ;; Generamos los archivos de salida (content -> public) (org-publish-all t) ;; Mensaje de confirmación al finalizar (message "¡Build completo!")
construir-sitio.sh
Para ejecutar el código del archivo que acabamos de crear, podemos
abrirlo en Emacs
y evaluarlo, pero también podemos incluir una
llamada en un ejecutable bash, de modo que cada vez que se ejecute
el último, también lo hará el primero.
Creamos el script y llamamos a
Emacs
para que evalúe y ejecute el código deconstruir-sitio.el
:#!/bin/sh emacs -Q --script construir-sitio.el
El parámetro
-Q
facilita que el script pueda funcionar sin problemas en distintos sistemas, ya que le indica al programa que no emplee la configuración del usuario actual, evitando posibles interferencias.Llamamos también a
Rsync
para que realice la copia entre directorios de origen y destino, bautizados en este caso comocontent
ypublic
, respectivamente:rsync -av ./content/* ./public
Donde
av
responde a--archive
yverbose
.Nuestro script queda así:
#!/bin/sh emacs -Q --script construir-sitio.el rsync -av --delete-after --exclude={'*.org~','*.html','*.xml','.git','.build.yml','LICENSE.txt'} ./content/ ./public
Excluímos archivos y directorios que no nos interese copiar al directorio de destino3, o que no nos interese borrar en este último 4.
Debemos añadir la ruta de nuestro script al
path
para su ejecución:export PATH="/ruta/a/nuestro/script:$PATH"
Finalmente, necesitaremos hacerlo ejecutable:
chmod u+x ../../../construir-sitio.sh
Deberemos adaptar la ruta a la ubicación y nombre del ejecutable en cada caso, por supuesto. Es conveniete probar el funcionamiento de forma que nos asefuremos de que hace los que queremos y no nos borra o se "olvida" de nada importante.
Previsualización del sitio
Para previsualizar el aspecto que tendrá nuestro sitio web antes de
subirlo como tal, disponemos de un paquete de Emacs
denominado
simple-httpd
, que genera un servidor local desde el que podremos
acceder a través de cualquier navegador en nuestro equipo:
(unless (package-installed-p 'simple-httpd)
(package-install 'simple-httpd))
Al ejecutar httpd-serve-directory
se nos preguntará por un
directorio para el servidor. Le indicaremos el directorio public
y
abriremos en un navegador la dirección "http://localhost:8080" para
acceder a la previsualización.
Publicación del sitio
Para hacer públicamente accesible nuestro sitio una vez generado en
local, debemos realizar una serie de pasos adicionales. Para ello
haremos uso de git
, una herramienta de control de versiones que como
tal nos permitirá monitorizar y gestionar los cambios realizados en el
contenido del sitio a lo largo del tiempo, y que será necesaria para
subir el contenido a un repositorio de plataformas como Source Hut
,
que es la que yo he elegido, y donde se hospedará nuestro sitio web
estático.
Crear un repositorio
git
:git init
Añadir todo el contenido del directorio donde se encuentra el contenido de nuestro sitio:
git add -A
Hacer una captura del dicho contenido (
commit
):git commit -m "Primer commit"
Si es la primera vez que usamos git, es probable que se queje de que no hemos agregado un nombre de usuario y una dirección de correo. Entonces deberemos añadir estos datos:
git config --global user.email "<nombre>@<extension>" git config --global user.name "<nombre>"
- Si queremos aprovechar alguno de los servicios de creación de
sitios estáticos como los ofrecidos por
GitHub
,GitLab
oSource Hut
, debemos crear una cuenta en uno (o varios) de estos sitios. Los dos primeros permiten su uso de forma gratuita, mientras que el último requiere de una subscripción de2$
al mes ([https://man.sr.ht/ops/builds.sr.ht-migration.md][Gracias cryptobros]). Aquí se explica el proceso para el último, dado que ha sido el escogido por mi persona, pero es similar para los otros (y hay muchísimos tutoriales a lo largo y ancho de Internet). Una vez creada la cuenta en
Source Hut
, debemos crear una claveSSH
para subir el contenido de forma segura:ssh-keygen -t ed25519 -C "<nombre>@<extension>"
Se nos preguntará por una ruta para ubicar la clave, y una frase de seguridad.
- A continuación, debemos crear un repositorio online donde colocar el contenido de la página.
Una vez creado el repositorio, podemos añadirlo como repositorio remoto a nuestro repositorio local:
git remote add origin git@git.sr.ht:~<nombre-de-usuario>/<nombre-de-repositorio>
Si ya tuviéramos un repositorio remoto "origin", debemos poner otro nombre. Por ejemplo:
git remote add srht git@git.sr.ht:~<nombre-de-usuario>/<nombre-de-repositorio>
Finalmente, subimos el contenido del repositorio local al repositorio público:
git push origin master
o:
git push srht master
El repositorio debe contener una de las licencias disponibles, cuyo texto debemos copiar en un archivo con el título "
LICENSE
" o "COPYING
". En mi caso he escogido laGPLv3
, de modo que cualquier persona está autorizada a compartir y copiar su contenido.Para generar el sitio una vez subido el contenido al repositorio remoto, podemos crear un archivo
yaml
para queSource Hut
lo haga automáticamente. Por ejemplo:image: alpine/edge oauth: pages.sr.ht/PAGES:RW sources: - https://git.sr.ht/~bitib/bitib packages: - hut environment: site: bitib.srht.site tasks: - package: | cd ./bitib tar -cvz . > ../site.tar.gz - upload: | hut pages publish -d $site site.tar.gz
Debemos cambiar fundamentalmente tres cosas:
- La dirección de origen en
sources
. - La dirección del sitio a generar en
environment
. - La ruta del directorio donde se hará la generación (build).
Aquí es
./bitib
.
Para asegurarnos de que el proceso se lleva a cabo correctamente, acudimos a https://builds.sr.ht/. Con esto estaríamos.
- La dirección de origen en
Conclusión
Cada vez que hagamos un cambio local en el contenido de nuestro sitio,
invocaremos nuevamente al script de construcción, y cuando queramos
subirlo, debemos hacer un nuevo commit y push desde el repositorio
local al remoto. Entonces la (re)generación del sitio será automática.
Como se ha mencionado, si durante la realización de cambios queremos
previsualizar el resulato, tan solo hace falta ejecutar la función
httpd-serve-directory
la primera vez durante la sesión, acudir a
http://localhost:8080/, y refrescar la página en caso de
posteriores cambios.
El proceso seguido puede resultar algo laborioso la primera vez, pero
a partir de entonces, y gracias a nuestro script, será mucho más
rápido y sencillo. Para agilizar la subida del sitio podemos
configurar la autentificación con SSH
entre otras cosas, pero eso lo
dejo para más adelante.
Notas al pie de página:
Bajo el protocolo http.
También podríamos conseguir esto mediante
la propia función org-publish
, pero considero que de esta otra forma
resulta más cómodo y sencillo
En este caso los archivos de recuperación
(.org~
) generados por Emacs
Entre estos se encuentran el directorio con el
repositorio de git
y los archivos de construcción y licencia del
sitio, que solo son relevantes para el repositorio remoto, tal y
como se muestra en la sección Publicación del sitio