Programmer la publication d’un article Cobalt
Parmi les choses qui étaient très pratiques sous WordPress et que j’ai perdues en passant à Cobalt, il y a le fait de pouvoir programmer à l’avance la publication d’un article. Il existe plein de moyens de le faire depuis un dépôt GitHub, mais étant auto-hébergé, je ne peux pas utiliser les mêmes outils. Mais étant auto-hébergé, justement, j’ai la main sur ce que je peux faire sur mon serveur, et après quelques scripts, j’ai pu à nouveau programmer mes posts à l’avance !
Préparer les scripts
Tous les scripts dont je vais parler sont rangés dans ~/bin
, et ce dossier est présent dans le PATH
du shell.
Préparer le répertoire de travail
On va appeler user
l’utilisateur qui a les droits sur le dossier qui va contenir la racine web :
~/www
Dans ce dossier, on va cloner le dépôt du blog pour obtenir la version à partir de laquelle le site sera construit :
$ cd ~/www
$ git clone https://mon_serveur_git/blog.git
On obtient alors :
~/www/blog
Quand Cobalt génère le site web, il le fait dans le dossier _site
depuis la racine du dépôt :
~/www/blog/_site
Ce dossier est celui qui doit être configuré dans le serveur web (Apache ou nginx, par exemple) pour être la racine web de votre site.
Définir un hook git
Tout d’abord, j’ai défini un hook du côté de Gitea. Un hook est une action à exécuter à un certain moment. Ici, j’ai créé un hook lorsque le serveur a reçu un push.

Étant donné que Gitea est lancé par supervisor
qui lui-même a l’air d’être lancé en root
, j’ai préféré limiter ce qui est fait à ce moment-là, par sécurité. Je me contente donc de lancer un script en tant qu’utilisateur user
.
Mettre à jour le dépôt de travail
Une fois qu’on a donné la main à user
, c’est là que le travail commence. Première chose à faire quand un push a été détecté, mettre à jour le dépôt de user
, et c’est par ça que commence le script update_blog.sh
:
#!/bin/sh
unset GIT_DIR
cd ~/www/blog/
git pull > ~/git.log 2> ~/err.log
git log -n 1 --format=%B | gawk -f ~/bin/process_git_command
L’astuce ici, c’est le unset GIT_DIR
. Cette variable d’environnement est affectée par Gitea et si on ne l’enlève pas, le git pull
échoue. J’avoue ne pas avoir tout saisi, mais ça fonctionne ainsi. 😅 Notez la création de fichiers de logs pour avoir un peu de retours. La ligne en git log
récupère le dernier message de commit et l’envoie à gawk
pour analyse, et déterminer ce qu’il y a faire.
Parser le message de commit
Voici le contenu du script awk
:
BEGIN { regex="^(cobalt_[a-z]+)( (\"([A-Za-z0-9:+ ]+)\" )?(.*))?(\n.*)?" }
{ if (match($0, regex, part)) {
switch (part[1])
{
case "cobalt_build":
print "exec cobalt_build";
system("exec ~/bin/build.sh > ~/cobalt_build.log");
break;
case "cobalt_schedule":
if (part[4] == "") {
print "No date given";
} else if (part[5] == "") {
print "No file given";
} else {
print "exec cobalt_schedule";
system("exec ~/bin/schedule.sh \""part[4]"\" \""part[5]"\" \
2> ~/cobalt_schedule.log");
}
break;
case "cobalt_cancel":
if (part[5] == "") {
print "No name given";
} else {
print "exec cobalt_cancel";
system("exec ~/bin/cancel_schedule.sh \""part[5]"\" \
> ~/cobalt_cancel.log");
break;
}
break;
default:
print "Unknown command";
break;
}
} else { print "No match"; }
}
Ce script va parser la première ligne du message de commit à la recherche d’une commande reconnue. Vous pouvez donc toujours renseigner vos commits avec d’autres infos à partir de la 2e ligne.
La regex du script va découper la première ligne pour stocker dans le tableau part
les différentes correspondances, s’il y en a. Celles qui nous intéressent se trouvent dans les cases 1, 4 et 5, respectivement le nom de la commande, la date et le nom de fichier sans extension. On teste donc part[1]
pour voir si une commande s’y trouve, sinon on imprime Unknown command
.
Lancer la génération
La première commande à être reconnaissable est cobalt_build
sans aucun paramètre. Si cette commande est présente, alors awk
lancera le script build.sh
:
#!/bin/sh
cd ~/www/blog
cobalt build > ~/cobalt_build.log
Rien de bien sorcier ici, on se rend dans le dossier de travail et on lance Cobalt.
Programmer la publication différée
La commande cobalt_schedule
nécessite 2 paramètres : une date et un nom de fichier. Par exemple : cobalt_schedule "11:12 Feb 14" table-rase-et-cobalt
Si c’est bon, alors on exécute le script schedule.sh
:
#!/bin/bash
job="$(echo "~/bin/publish.sh \"$2\" && \
git commit -a -m \"published $2.md\" && \
git push && ~/bin/build.sh" | at $1 |& grep job)"
echo "$job $2" >> ~/bin/jobs_list
Déjà, notez que c’est un script bash
et non sh
. J’expliquerai pourquoi plus bas. Dans ce script, $1
est la date et $2
le nom de fichier. La date est passée à la commande Linux at
et doit donc respecter un format accepté par cette dernière.
Il se passe pas mal de choses ici, alors je vais décomposer un peu. 🙂
On commence par appeler le script publish.sh
en lui donnant le nom de fichier :
cd ~/www/blog
cobalt publish ./src/posts/$1.md >> ~/cobalt_publish.log \
2> ~/cobalt_publish_err.log
Ceci va modifier le fichier en le mettant à la date courante et enlever le flag qui le maintenait à l’état de brouillon. Mais ça ne lance pas la génération !
Comme on a modifié un fichier du dépôt git de travail, il faut envoyer la modification au dépôt maître. C’est le travail des deux commandes git : on commit avec un message et on push sur le dépôt maître. Attention ici à ne pas mettre une commande cobalt_schedule
en message de commit, ça ferait une boucle infinie !
Enfin, on appelle la commande shell at
avec la date qui va produire en sortie quelques messages qu’on filtre avec grep
pour ne garder que celui qui donne le job programmé avec son id. On pipe le résultat de at
avec |&
pour passer à grep
le canal d’erreur standard au lieu de la sortie standard. Je ne sais pas pourquoi at
affiche le résultat de son exécution sur l’erreur standard… et |&
n’est pas supporté par sh
, voilà le pourquoi du bash
.
Le tout est dans un $()
pour affecter la sortie de cet ensemble de commandes à la variable job
qu’on utilise pour enregistrer les informations du job ainsi que le fichier concerné dans un fichier jobs_list
qui nous servira pour la dernière commande supportée.
Annuler la publication différée
Pour annuler une planification, la commande cobalt_cancel
nécessite le nom du fichier sans extension (ou une partie de celui-ci, attention toutefois aux ambiguïtés !) en paramètre. Par exemple : cobalt_cancel table-rase
.
La commande at
permet l’annulation d’un job programmé, mais seulement si on connaît l’id du job à annuler. C’est pour ça qu’on a sauvé les infos du job en les associant au fichier concerné, car sinon on ne pourrait pas retrouver le job voulu.
Le script d’annulation cancel_schedule.sh
est comme suit :
#!/bin/sh
cat ~/bin/jobs_list | gawk -v name="$1" '
$0 ~ name { if (NF > 0) {
system("atrm "$2);
}}
' && sed -i "/$1/d" ~/bin/jobs_list
Il prend en paramètre le nom de fichier dont la planification est à annuler. On lit toute la liste de jobs et on la passe à gawk
pour qu’il trouve la ligne qui contient le nom de fichier. S’il le trouve, il appelle atrm
avec l’id du job qui se situe dans le champ $2
et on appelle ensuite sed
pour effacer cette ligne du fichier.
Conclusion
C’est sûr, c’est beaucoup plus complexe que sous WordPress, où on planifie une publication en 2 clics. Sans compter les plugins qui rendent cette planification encore plus ergonomique. Mais avec un peu d’huile de coude, j’ai un système qui me convient pour atteindre le même but tout en gardant les avantages d’un blog statique. De plus, j’ai appris à me servir de awk
, de at
, réappris à modifier un fichier avec sed
et révisé quelques notions de script shell. Donc un peu de travail, mais ça fait partie de la philosophie d’un tel système de blog ! En espérant que ça puisse vous être utile aussi !