Un shell pour pelican
Posted on 2015-01-09 in Programmation
Comme je l'ai déjà dit ici, mon blog est géré par Pelican. Il est également placé sous gestion de version. Pour régénérer automatiquement après chaque modification la version html des pages, il faut utiliser la commande make regenerate. Pour lancer le serveur de test pour voir les pages, on utilise make serve. Enfin, pour synchroniser le contenu sur le serveur, j'utilise make rsync_upload. J'en profite pour pousser les modifications de mon dépôt local vers mon serveur et vers Bitbucket pour en avoir une copie de sauvegarde.
Jusqu'à présent, je gérais tout ça à la main, dans un terminal. J'avais déjà écrit une petite fonction bash pour me déplacer dans le bon dossier et activer le venv python. Mais ce n'était pas très pratique : les commandes make serve et make regenerate doivent tourner en permanence en tâche de fond, il faut pusher sur deux dépôts différents et synchroniser les pages sur le serveur. J'ai donc décider de me créer un script pour améliorer tout ça.
Comme j'ai besoin de lancer plusieurs commandes bien définies dans un même dossier, sans sortir de l'environnement virtuel et que je veux pouvoir interagir facilement avec les processus en tâche de fond, j'ai décidé de créer un mini-shell. Je dois admettre que j'en suis très satisfait.
Quelques explications sur le script :
- Tout est dans une boucle infinie pour imiter un vrai shell. echo -ne "> " permet d'afficher un prompt et de laisser le curseur sur cette ligne.
- exec 3< <(make serve)
- La syntaxe <(make serve) est remplacée par chemin vers un fichier virtuel qui est connecté à la sortie standard de la commande située entre les parenthèses. Ici make serve qui démarre le serveur.
- Comme on veut que le processus soit en background, on lance la commande avec exec.
- Enfin, comme on veut pouvoir récupérer facilement la sortie de la commande, on envoie le contenu du fichier vers une nouvelle sortie 3. On lit son contenu avec cat <&3. Tant que le fichier n'est pas fermé, cette commande ne se retourne pas.
- Voir cette réponse sur stackoverflow pour plus de détails.
- Sorties additionnelles : j'ai tenté de stocker le numéro des sorties dans des variables. Le code est plus clair et cela permet de le factoriser. Malheureusement, exec "${var}"< <(make serve) provoque une erreur. Idem pour cat <&"${var}".
- [ -n "${serve_pid}" ] && has_died "${serve_pid}" : la fonction has_died ne doit pas être entre crochets (donc passée en argument à la fonction test) pour que le script fonctionne correctement.
- echo ${pid} : on ne peut pas renvoyer autre chose que des codes d'erreur avec return. Pour tout le reste, il faut utiliser echo.
Ci-dessous le script complet. Vous pouvez aussi le télécharger ou le voir sur github pour avoir la version la plus à jour.
1 #!/usr/bin/bash 2 3 stop_command() { 4 if [ "$1" ]; then 5 kill "$1" 6 fi 7 } 8 9 has_died() { 10 if kill -0 "$1" > /dev/null 2>&1; then 11 return 1 12 else 13 return 0 14 fi 15 } 16 17 find_pid() { 18 echo $(ps -elf | grep "$1" | grep -v '&&' | grep -v 'grep' | awk '{print $4}') 19 } 20 21 get_pid() { 22 pid=$(find_pid "$1") 23 until [ -n "${pid}" ]; do 24 pid=$(find_pid "$1") 25 done 26 echo "${pid}" 27 } 28 29 # Activate venv 30 cd ~/server/blog/ 31 source bin/activate 32 cd pelican/jujens.eu 33 34 regenerate_pid='' 35 serve_pid='' 36 # These variables cannot be used. If you try to use them, you will get an error like 3< not found. 37 #regenerate_output=3 38 #serve_output=4 39 40 while true; do 41 # Print errors for regenerate 42 if [ -n "${regenerate_pid}" ] && has_died "${regenerate_pid}"; then 43 echo -e "Regenerate has died with ouput:\n" 44 cat <&3 45 fi 46 # Print errors for serve 47 if [ -n "${serve_pid}" ] && has_died "${serve_pid}"; then 48 echo -e "Serve has died with ouput:\n" 49 cat <&4 50 fi 51 52 echo -en "(blog) > " 53 read command 54 55 case "${command}" in 56 deploy) 57 hg push > /dev/null 58 hg push bitbucket >/dev/null 59 stop_command "${serve_pid}" 60 stop_command "${regenerate_pid}" 61 serve_pid='' 62 regenerate_pid='' 63 cat <&3 > /dev/null 2>&1 64 cat <&4 > /dev/null 2>&1 65 make rsync_upload > /dev/null 66 ;; 67 push) 68 hg push > /dev/null 69 hg push bitbucket > /dev/null 70 ;; 71 st|status) 72 hg st 73 ;; 74 add) 75 echo "Enter the filename to add (. for all files)" 76 read file_name 77 hg add "${file_name}" 78 ;; 79 ci|commit) 80 echo "Please enter the commit message:" 81 read commit_msg 82 hg ci -m "${commit_msg}" 83 ;; 84 serve) 85 if [ -n "${serve_pid}" ] && ! has_died "${serve_pid}"; then 86 echo "Serve is already running." 87 else 88 exec 4< <(make serve 2>&1) 89 serve_pid=$(get_pid 'python3 -m pelican.server') 90 fi 91 ;; 92 regenerate) 93 if [ -n "${regenerate_pid}" ] && ! has_died "${regenerate_pid}"; then 94 echo "Regenerate is already running." 95 else 96 exec 3< <(make regenerate 2>&1) 97 regenerate_pid=$(get_pid 'make regenerate') 98 fi 99 ;; 100 "stop serve") 101 stop_command "${serve_pid}" > /dev/null 102 serve_pid='' 103 cat <&4 > /dev/null 104 ;; 105 "stop regenerate") 106 stop_command "${regenerate_pid}" > /dev/null 107 regenerate_pid='' 108 cat <&3 > /dev/null 109 ;; 110 stop) 111 echo "Stop requires an argument: serve or regenerate" 112 ;; 113 quit) 114 break 115 ;; 116 help) 117 echo "Available commands:" 118 echo -e "\tdeploy" 119 echo -e "\tpush" 120 echo -e "\tserve" 121 echo -e "\tregenerate" 122 echo -e "\tstop serve" 123 echo -e "\tstop regenerate" 124 echo -e "\thelp" 125 ;; 126 *) 127 if [ -n "${command}" ]; then 128 echo -e "${command} is invalid." 129 fi 130 esac 131 done 132 133 echo "Done"