vendredi 9 janvier 2026

Compter les lignes d'un projet git

Ceci est un blog-note, il serait donc dommage de ne pas en profiter pour... Prendre des notes !

J'ai récemment voulu voir où j'en étais quant à la quantité de code produite pour un projet. Simple curiosité, pour me rendre compte.
Comment faire ? Comme ceci :

git ls-file | xargs wc-l

Bien sûr, si vous voulez plus de détail, comme le nombre de lignes des fichiers php, vous pouvez rajouter un grep.

git ls-file | grep '.php' | xargs wc -l

C'est tout.

Edit du 17 janvier 2026 : On me souffle dans l'oreillette que Tokei fait aussi très bien le boulot ! Merci à Ollivier Robert pour la découverte.

mercredi 7 janvier 2026

A worker was found in a dead state

Nouvelle joie de MacOS... Oui je sais, j'ai qu'à installer Linux. Mais non.

Je bossais tranquillement sur un playbook Ansible ce matin quand, tout à coup, le drame !

[ERROR]: A worker was found in a dead state

Mais que vais-je faire ? Comment vais-je survivre ? Quel avenir vais-je laisser à mes enfants ?
Et bien figure-toi, encore une fois : ça n'arrive pas sur Linux et c'est visiblement récurrent sur MacOS. Mais on aune solution !

Il suffit de lancer son playbook avec la variable d'environnement OBJC_DISABLE_INITIALIZE_FORK_SAFETY à yes :

OBJC_DISABLE_INITIALIZE_FORK_SAFETY=yes ansible-playbook ...

En gros, si j'ai bien compris (rien de sûr), c'est dû à l'utilisation de la méthode python fork() qui permet de créer un nouveau processus. Sauf que sur MacOS, si la bibliothèque utilisée n'est pas "fork-safe", alors l'OS t'envoie balader avec un plantage ou un deadlock.

Voilà, enjoy, même si c'est pas optimal.

jeudi 5 juin 2025

Comprendre les RegExp - Exemple d'un feed BlueSky

Les RegExp ont tendance à faire peur à toute personne qui n'est pas familiarisée avec celles-ci. Pourtant, lorsqu'on connaît quelques points précis, on peut déjà aller assez loin.
Ainsi, sans avoir la prétention de couvrir tout l'usage des expressions régulières (loin s'en faut), je pense que celle qui illustre ce billet est une bonne entrée en matière.

Celle-ci est celle qui construit le feed TND(FR) que je décris dans ce billet. Prenons un peu de temps pour la décortiquer.

L'expression est donc la suivante :

(^|\s|\b|#)(ritaline|concerta|tda(h)?|tsa|autisme|
autistique(s)?|bipolaire(s)?|autiste(s)?|tnd
neurodéveloppement(aux)?|synesthésie|méthylphénidate|
hyperactivité|hyperacti(f|ve)(s)?|asperger|trouble de l'attention|
trouble déficit de l'attention)($|\s|\b)

Note : Elle diffère en effet de celle de l'illustration, puisque je me suis rendu compte d'erreurs et l'ai corrigée.

On peut ici la séparer en trois éléments distincts, chacun entre parenthèses (pour prioriser et regrouper, comme en mathématiques). Le premier est (^|\s|\b|#). Il correspond au début de l'expression recherchée, et contient plusieurs éléments intéressants.

  • ^ indique qu'il s'agit d'un début de ligne
  • \s indique un espace ou une tabulation
  • \b indique la position de début ou de fin de mot
  • # n'est ici que le caractère # lui-même, utilisé pour les hashtags sur la plupart des réseaux sociaux (coucou Threads)
  • | est quant à lui un simple OU qui sépare deux éléments

Ce bloc peut donc être lu comme : Un début de ligne OU un espace ou une tabulation OU un début ou une fin de mot OU le caractère #. Et c'est exactement ce que nous recherchons. Nous voulons que l'expression qui sera détectée commence (étant donné que c'est le premier bloc) par un début de ligne OU un espace ou une tabulation OU un début ou une fin de mot OU le caractère #.
Ceci empêche par exemple de chercher le mot vent sans variation, et d'avoir en retour éventuel qui contient bien vent mais n'est pas l'expression souhaitée, puisqu'il ne commence pas par vent.

Le second bloc est le plus long : (ritaline|concerta|tda(h)?|tsa|autisme|autistique(s)?|bipolaire(s)?|autiste(s)?|tnd|neurodéveloppement(aux)?|synesthésie|méthylphénidate|hyperactivité|hyperacti(f|ve)(s)?|asperger|trouble de l'attention|trouble déficit de l'attention)

On définit ici l'ensemble des termes que nous voulons sélectionner avec notre expression régulière, tous séparés par un | qui veut dire OU. Nous voulons donc retenir les termes ritaline OU concerta OU autisme OU tsa, etc.
Une spécificité ici est que certains mots sont regroupés en quelques caractères. Par exemple, pour regrouper à la fois hyperactif, hyperactive, hyperactifs et hyperactives, j'ai utilisé hyperacti(f|ve)(s)?. Attardons-nous là-dessus. Nous avons ici, à nouveau, trois bloc :

  • hyperacti est le début du mot que nous cherchons, juste après les critères du premier bloc vu plus haut
  • (f|ve) indique qu'après le début du mot, nous voulons le masculin (f) OU le féminin (ve)
  • (s)? permet d'inclure le pluriel - ici le s- tout en le rendant facultatif, ce que permet le point d'interrogation

Ainsi, nous devons avoir un mot qui commence par hyperacti, suivi du masculin f ou ve. Ces points sont obligatoires. Nous pouvons aussi avoir le pluriel s à la fin, mais le mot sera retenu même s'il ne contient pas le s, et ce grâce au ?.

Enfin, le dernier bloc est à l'image du premier, et indique ce que nous devons rencontrer à la fin de notre expression recherché : ($|\s|\b)

  • $ indique une fin de ligne
  • \s indique un espace ou une tabulation
  • \b indique un début ou une fin de mot

Là encore, directement après les termes que nous voulons rechercher, nous devons avoir une fin de ligne OU un espace ou une tabulation OU un début ou une fin de mot. Pour reprendre le même exemple que pour le premier bloc, ceci empêche que soit retenu venteux alors que nous ne voulons que le mot vent.

Voilà, j'espère qu'avec ce court billet vous aurez de quoi construire quelques petites expressions régulières, et pourquoi pas construire un feed BlueSky ;) Moi je les construis avec SkyFeed

Pour une liste plus exhaustive des différents opérateurs d'expressions régulières, je ne peux que vous renvoyer à la page wikipedia correspondante.

lundi 27 mai 2024

Une seconde vie pour le vieux MacBook Air

Comme toute machine vieillissante, mon MacBook Air début 2015 est devenu inutilisable au fil du temps.

Le matériel fonctionne pourtant correctement et comme toujours j'ai du mal à me dire qu'il faut que je le jette. J'ai donc décidé d'essayer de lui donner une seconde vie avec une distribution Linux (Manjaro ici) et un environnement léger, i3wm.

Regardons comment faire.


On récupère l’image iso depuis le site voulu :

$ cd ~/Downloads
$ wget https://download.manjaro.org/i3/23.0.1/manjaro-i3-23.0.1-230921-linux65.iso

On liste les disques présents pour identifier la clé usb concernée, ainsi que son identifiant :

$ diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *500.3 GB   disk0
   1:             Apple_APFS_ISC Container disk1         524.3 MB   disk0s1
   2:                 Apple_APFS Container disk3         494.4 GB   disk0s2
   3:        Apple_APFS_Recovery Container disk2         5.4 GB     disk0s3

/dev/disk3 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +494.4 GB   disk3
                                 Physical Store disk0s2
   1:                APFS Volume Macintosh HD            13.5 GB    disk3s1
   2:              APFS Snapshot com.apple.os.update-... 13.5 GB    disk3s1s1
   3:                APFS Volume Preboot                 12.3 GB    disk3s2
   4:                APFS Volume Recovery                1.9 GB     disk3s3
   5:                APFS Volume Data                    405.8 GB   disk3s5
   6:                APFS Volume VM                      4.3 GB     disk3s6

/dev/disk5 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                                                   *8.0 GB     disk5

Une fois identifiée, on efface la clé usb. Dans mon cas, il s'agit de disk5.

$ diskutil secureErase 1 disk5
Started erase on disk5
Finished erase on disk5

L'option secureErase permet, comme son nom l'indique, d'effacer le disque en utilisant une méthode sécurisée avec un niveau qui peut être défini. Ici, 1 signifie qu'il y aura une passe unique d'écriture de données aléatoires.

On crée ensuite l'image au format .dmg qui ira sur la clé USB :

$ hdiutil convert ~/Downloads/manjaro-i3-23.0.1-230921-linux65.iso -format UDRW -o ~/Downloads/manjaro-i3
Lecture de Master Boot Record (MBR : 0)…
Lecture de MANJARO_I3_2301                  (Apple_ISO : 1)…
.....................................................................................................................................
Lecture de  (Type EF : 2)…
.....................................................................................................................................
Temps écoulé :  3.926s
Vitesse : 870.3 Mo/s
Compression : 0.0%
created: /Users/xakan/Downloads/manjaro-i3.dmg

Notez que l'extension de l'image de sortie n'est pas définie dans le nom du fichier à générer.

Dans notre cas, UDRW signifie qu'on veut une image UDIF avec la possibilité de lire/écrire. UDIF signifiant Apple Mac OS X Universal Disk Image Format.

On démonte ensuite la clé :

$ diskutil unmountDisk /dev/disk5
Unmount of all volumes on disk5 was successful

Enfin, on copie le .dmg sur la clé avec la commande dd.

$ sudo dd if=/Users/xakan/Downloads/manjaro-i3.dmg of=/dev/disk5 bs=1m
Password:
3416+1 records in
3416+1 records out
3582824448 bytes transferred in 970.109954 secs (3693215 bytes/sec)

Alors oui, l'écriture a été très longue, mais c'est pas le sujet, la clé est très vieille !

Maintenant, on redémarre en restant appuyé sur Alt et le tour est joué.

jeudi 25 avril 2024

Operation not permitted

Il existe plusieurs façons d'envoyer des fichiers vers un conteneur sur un hyperviseur Proxmox. Mais toutes ne sont pas toujours sans embuches. Le cas que j'ai rencontré aujourd'hui m'a un peu fait me casser les dents. Pour pas grand chose, au final. Pour refaire rapidement le parcours, j'ai voulu déposer les fichiers statiques du site ZdX sur le conteneur qui le sert. J'ai donc envoyé mes fichiers sur mon user via scp, monté le système de fichiers du conteneur grâce à pct mount, puis j'ai fait un banal mv. Et c'est là que ça se corse.


J'ai donc commencé par envoyer mes fichiers de façon classique avec sftp, dans le home de mon utilisateur xakan. J'ai monté le système de fichier du conteneur avec pct mount <id du conteneur>, et simplement déplacé les fichiers avec mv <mondossier> /var/lib/lxc/<id>/rootfs/var/www/zdx. Un petit lxc-attach <id du conteneur> plus tard, me voici dans le dossier /var/www/ à vérifier les droits sur les fichiers avec ls -al :

drwxr-xr-x 11 nobody   nogroup  4096 Apr 24 19:20 zdx

Qu'à cela ne tienne, toujours depuis le conteneur, on met www-data:www-data comme propriétaires du dossier :

chown -R www-data: zdx
chown: changing ownership of 'zdx/terms/index.html': Operation not permitted

Ah. C'est même pas un accès refusé, c'est une opération non autorisée. lsattr m'indique que l'attribut i n'est pas présent, ce n'est donc pas le problème. En réalité, cela vient du fait que mon conteneur est un conteneur sans privilèges, sans correspondance entre l'utilisateur xakan et les utilisateurs du conteneur. Le dossier est donc assigné à nobody et nogroup.

La solution consiste donc à aller chercher le pid du processus init tel que vu par l'hôte. Il est bien entendu à 1 du côté du conteneur.

lxc-info -p 200
PID:            1204

Du côté de l'hôte, le processus init de mon conteneur est donc 1024. Je peux à partir de là aller chercher l'uid de l'utilisateur qui exécute ce processus, toujours tel que vu depuis l'hôte :

cat /proc/1204/uid_map
         0     100000      65536

De retour sur le conteneur après un nouvel lxc-attach 200, je vérifie l'id de l'utilisateur www-data qui doit posséder le dossier :

id -u www-data
33

D'un point de vue hôte, l'utilisateur qui doit être propriétaire du dossier zdx a donc pour id 100000 + 33 (id de l'utilisateur depuis le conteneur). Il n'y a donc plus qu'à définir les droits depuis l'hôte, en passant par le montage du système de fichier :

chown -R 100033:100033 /var/lib/lxc/200/rootfs/var/www/public

On démonte le système de fichier du conteneur :

pct unmount 200

Et on va sur le conteneur vérifier que les droits sont bons, toujours avec ls -al :

drwxr-xr-x 11 www-data www-data 4096 Apr 24 19:20 zdx

That's it!

vendredi 2 février 2024

Changer la version de Ruby

À chaque fois que la version minimale de Ruby évolue dans les prérequis de Mastodon, je me retrouve à chercher comment la mettre à jour. C'est l'occasion pour moi de poser ça ici pour retrouver mes petits plus facilement.

Voici donc les quelques commandes qui me sont toujours utiles. Attention, j'utilise ici rbenv.

Connaître la version de Ruby :

$ rbenv version

Lister les versions disponibles :

$ rbenv install --list

Si la version voulue n'est pas disponible :

$ git -C /home/mastodon/.rbenv/plugins/ruby-build pull

Installation de la version voulue :

$ rbenv install <version>

Application de la nouvelle version :

$ rbenv local <version>

lundi 11 décembre 2023

PHP, Debian et Sury sont sur un bateau

Debian fournit dans ses paquets une seule version de PHP. Il arrive cependant régulièrement de devoir disposer de plusieurs versions sur une même machine. C'est là que le dépôt Sury intervient.

Le dépôt Sury, maintenu par Ondřej Surý, est la référence en ce qui concerne la gestion des versions PHP sur les distributions Linux basées sur Debian.

Celui-ci permet en effet d'installer les versions de PHP que vous voulez sur la version de Debian que vous voulez. Le pied !

Pour procéder à cette installation, il existe quelques prérequis :

Installons d'abord les dépendances :

apt update && apt install ca-certificates apt-transport-https software-properties-common wget curl lsb-release

Nous pouvons maintenant ajouter le dépôt package.sury.org sur notre machine à l'aide du script fourni directement dans le README.txt, et dont le contenu est relativement simple à comprendre :

#!/bin/sh
# To add this repository please do:

if [ "$(whoami)" != "root" ]; then
    SUDO=sudo
fi

${SUDO} apt-get update
${SUDO} apt-get -y install lsb-release ca-certificates curl
${SUDO} curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
${SUDO} sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
${SUDO} apt-get update

Et donc, pour exécuter directement ce script :

wget -O - https://packages.sury.org/php/README.txt | bash

On remet les paquets à jour :

apt update

On peut désormais installer les différentes versions de php comme bon nous semble :

apt install php7.x php8.x 

 

lundi 11 septembre 2023

Tester localement et facilement une Pull Request

Lorsqu'on travaille sur un projet plus ou moins communautaire, ou à minima en équipe, il est nécessaire de tester les PRs avant de les valider, et donc de les intégrer. Seulement, aller chercher la branche distante du fork de la personne qui a fait la PR se révèle très fastidieux. Il existe néanmoins une solution qui permet de gagner pas mal de temps.


Nous allons ici voir comment récupérer directement le contenu de la PR dans la branche dans laquelle nous souhaitons la fusionner.

On se place dans un premier temps sur la branche concernée :

$ git checkout master

On la met à jour :

$ git pull origin master

On récupère ensuite l'ID de la Pull Request qu'on veut tester. Il s'agit du numéro que l'on trouve dans l'URL de la PR, ou le numéro qui est précédé d'un # sur la page de la PR.
La commande suivante nous permet alors de la récupérer dans une nouvelle branche :

$ git fetch origin pull/<ID>/head:<Nom de la branche>

Et basculez sur la branche en question.

$ git checkout <Nom de la branche>

Par exemple, pour créer la branche "TestPR325" qui correspond à la PR ID 325, il suffit d'utiliser :

$ git fetch origin/pull/325/head:TestPR325
$ git checkout TestPR325

Une fois vos tests terminés, retournez sur la branche de destination des PR, et recommencez la manipulation.

$ git checkout master

mardi 29 août 2023

Cloner tous les dépôts d'une même organisation

Il peut arriver que l'on ait besoin de cloner d'un coup tous les dépôts d'une même organisation. Ne me demandez pas pourquoi, mais ça peut arriver...


Pour commencer, nous aurons ici besoin de github-cli.

Installation avec Homebrew (MacOS oblige) :

$ brew install gh

On s'authentifie avec un jeton d'authentification :

$ gh auth login

On va maintenant se baser sur la commande suivante pour lister les dépôts de l'organisation :

$ gh repo list <nom de l'organisation> --limit 100 # N'est valable que s'il y a moins de 100 dépôts. Le nombre peut être adapté.

On utilise la sortie dans un while pour cloner le dépôt :

$ gh repo list <nom de l'organisation> --limit 70 | while read -r repo _; do gh repo clone "$repo" "$repo"; done

That's it !

lundi 28 août 2023

Désinstaller Node.js sur MacOS

Pour un projet personnel, j'ai dû installer Node.js. Mais j'ai eu la mauvaise idée de l'installer à partir du fichier .pkg fourni sur le site de Node.js.


Ce fichier .pkg ne propose aucune option de désinstallation, et l'application de se retrouve pas dans le dossier Applications qui permet de procéder à une désinstallation par glisser/déposer dans la corbeille.

Ainsi, j'ai dû creuser un peu pour pouvoir désinstaller correctement Node.js et npm. Voici les étapes à suivre :

$ sudo npm uninstall npm -g
$ sudo rm -rf /usr/local/lib/node /usr/local/lib/node_modules /var/db/receipts/org.nodejs.*
$ sudo rm -rf /usr/local/include/node /Users/$USER/.npm
$ sudo rm /usr/local/bin/node
$ sudo rm /usr/local/share/man/man1/node.1
$ sudo rm /usr/local/lib/dtrace/node.d

Ceci permet ensuite de l'installer avec Homebrew, le gestionnaire de paquets pour MacOS que je déteste :

$ brew install node