Aller au contenu

CronLab

mars 2026 → octobre 20265 min de lecture

Code sourceTéléchargements

Tout est parti de ma synchro GitLab vers GitHub. Je voulais la faire tourner tous les jours, sous Windows. Sous Linux, une ligne dans la crontab suffit. Sous Windows, il y a le Planificateur de tâches. Il fait le travail, mais son interface est une antiquité, et il faut passer par pas mal de fenêtres pour une simple commande qui revient chaque jour. Autant m’en faire un à ma façon.

CronLab, c’est ça. On lui donne une commande, le dossier où la lancer et quand : tous les jours à une heure précise, toutes les X minutes, certains jours de la semaine ou selon une expression cron. Il reste dans la zone de notification, lance la commande à l’heure, garde la sortie des dernières exécutions et envoie une notification Windows à la fin. Chez moi, une tâche lance la synchro chaque jour et me prévient si elle échoue. L’installeur se télécharge sur la page des versions.

Voilà la liste des tâches, avec le temps qui reste avant la prochaine exécution de chacune :

Le haut de la fenêtre de CronLab avec une seule tâche, la synchro GitLab vers GitHub : prochain lancement dans 19 h 58, dernière exécution réussie.

Tauri plutôt qu’Electron

C’est ma première vraie application de bureau. Pour en faire une avec une interface web, le choix classique, c’est Electron, qui embarque un Chromium complet dans chaque appli. Tauri, lui, se sert de la WebView déjà présente dans Windows 11. L’interface est en React, le cœur en Rust, et l’installeur fait moins de 4 Mo. Pour un petit outil qui tourne en permanence à côté de l’horloge, je ne voulais pas d’un navigateur entier en mémoire. Et c’était l’occasion de toucher à Rust, que je ne connaissais pas.

L’interface utilise les composants Fluent UI de Microsoft, pour avoir l’air d’une vraie appli Windows 11. Elle est en français et en anglais.

Le cœur en Rust

Côté Rust, un thread se réveille toutes les trente secondes, regarde quelles tâches sont dues et les lance. Chaque exécution a ensuite son propre thread, qui démarre la commande sans ouvrir de console, attend qu’elle se termine, garde ses sorties et envoie la notification. Si la commande dépasse son délai, ce thread tue tout l’arbre de processus avec taskkill /T, pas seulement le parent. Sinon, un script qui lance d’autres programmes les laisserait tourner derrière lui.

Ces threads et l’interface partagent la même configuration, protégée par un Mutex. C’est là que Rust devient concret : sans ce verrou, le programme ne compile pas. Dans d’autres langages, deux threads qui écrivent au même endroit en même temps, ça donne un bug qui n’apparaît que de temps en temps, à l’exécution. Celui-là, je n’ai pas eu à le chasser.

Une config lisible

Tout est rangé dans ~/.cronlab/, avec un config.json pour les réglages et les tâches, et un historique par tâche dans logs/. C’est du JSON lisible, exprès. On peut le modifier à la main ou le copier sur un autre PC pour y retrouver ses tâches. Je préfère un fichier que je peux ouvrir moi-même à une config enfermée dans un format opaque.

Livrer une appli à installer

Sur le web, quand je corrige un bug, je redéploie et tout le monde a la correction. Une appli installée, elle, reste dans la version qu’on a téléchargée. Il fallait donc une chaîne de livraison et un vrai canal de mise à jour. C’est la partie dont je suis le plus fier, parce qu’elle m’a demandé le plus de travail invisible.

À chaque push sur main, un premier job GitHub Actions regarde si la version de l’appli a déjà sa release. Si ce n’est pas le cas, un second job compile l’appli sous Windows, crée l’installeur, signe la mise à jour et publie la release. Dans l’appli, un bouton des réglages va chercher cette dernière version sur GitHub, vérifie sa signature avec la clé publique embarquée, puis l’installe. Si la signature ne correspond pas, il refuse le paquet.

Avec Claude Code

CronLab est un des premiers projets que j’ai faits avec Claude Code. Un soir de mars, il s’est passé deux heures entre le premier commit et la première version publiée, installeur compris.

Le lendemain, il a fallu reprendre le cœur du programme, parce que les tâches ne se lançaient jamais à l’heure prévue. Le planificateur comparait l’heure actuelle avec la prochaine exécution. Sauf qu’une fois l’heure passée, la prochaine exécution, c’était déjà le lendemain, donc la condition n’était jamais vraie. Maintenant, il regarde le créneau qui vient de passer, sur une fenêtre de cinq minutes, et retient ceux qu’il a déjà lancés.

Fin mars, j’ai corrigé deux autres oublis. L’interrupteur « Lancer au démarrage » ne faisait rien, parce que rien ne s’inscrivait dans Windows, et chaque tâche ouvrait une console vide le temps de s’exécuter.

Avec CronLab, j’ai aussi commencé à changer d’habitude. Avant, quand un outil me manquait, je me disais tant pis : je n’avais ni le temps ni toutes les compétences pour le construire. Là, je me suis mis à en fabriquer de plus en plus, simplement pour me faire gagner du temps. CronLab et la synchro GitLab sont les premiers de cette série.

Où en est le projet

La dernière version, en octobre, corrige deux bugs. Les tâches en expression cron avaient gardé le bug du début et ne partaient jamais toutes seules, et une tâche qui écrivait beaucoup restait bloquée jusqu’à la fin de son délai. Celle d’août faisait déjà démarrer CronLab dans la zone de notification sans ouvrir sa fenêtre, et réessayer la recherche de mise à jour, parce que la connexion à GitHub saute assez souvent pour qu’un seul essai échoue.

Technologies utilisées