Aller au contenu

Publier une version

Publier, c’est produire des tags cohérents sur les six dépôts en une seule commande, depuis l’espace de co-développement.

DépôtSource de véritéRègle
darkone-nixos-frameworkVERSIONRéférence du train
dnf-generatorCargo.tomlVersion propre, épinglée par le framework
dnf-fleet-updatepackage.jsonVersion propre, empaquetée par le framework
dnf-docpackage.jsonMAJEUR.MINEUR du framework, correctif propre
dnf-boilerplate, dnf-exampledernier tagMiroir du framework

Les six dépôts tiennent un CHANGELOG.md généré, modèles compris.

Le projet privé consommateur n’est jamais taggué : il reste en co-développement et sert de banc d’intégration permanent.

Le changelog est généré à partir des messages de commit. Un type hors liste disparaît silencieusement des notes de version, donc le format est vérifié : une seule ligne <type>(<scope>): <sujet>, 80 caractères max, type de la liste suivante.

feat fix perf refactor docs test build ci chore security revert

Le même script, scripts/check-commit-msg.sh, contrôle chaque push vers main et chaque pull request dans l’intégration continue, et chaque commit en local via le hook commit-msg, à installer une fois par clone :

Fenêtre de terminal
ln -s ../../scripts/check-commit-msg.sh .git/hooks/commit-msg

Un scope n’est jamais un type : feat(matrix): …, jamais matrix(admin): ….

TypeSection du changelog
featAdded
fixFixed
securitySecurity
perf, refactor, revertChanged
docsDocumentation
chore(deps)Dependencies
sujet en drop ou removeRemoved
autres chore, ci, test, buildnon publié

chore(deps) est le commit d’épinglage du train : l’entrée du framework nomme ainsi le générateur et le fleet-update qu’elle embarque.

RecetteEffet
just versionsVersion, dernier tag et branche de chaque dépôt
just changelogAperçu de la prochaine entrée, n’écrit rien
just bump [niveau]Version, changelog, commit et tag d’un dépôt
just release [niveau]Le train complet sur les six dépôts (+ la racine)
just versionVersion, révision et générateur épinglé du framework

Le niveau vaut auto par défaut : git-cliff 🡕 déduit le numéro suivant des commits. patch, minor, major ou un X.Y.Z littéral forcent la décision. Des commits non publiés seuls (un chore(deps), par exemple) donnent un correctif.

just bump ne pousse jamais : il affiche la commande de poussée. Il refuse de publier si HEAD porte déjà le dernier tag v*. Trois drapeaux passent au script :

  • --dry-run : montre l’entrée puis restaure le fichier, sans rien commiter ;
  • --yes : enchaîne sans confirmation ni passage dans $EDITOR ;
  • --next : affiche la version que prendrait la release, puis s’arrête.

Le bloc généré ne contient que des messages de commit. Un préambule, un tableau de versions compagnes ou une note de migration s’écrivent sous ## [Unreleased] dans le CHANGELOG.md : à la publication, le script vide cette section et replie son contenu en tête de l’entrée, avant les rubriques générées. Le train ne s’arrête pas pour éditer : ces notes s’écrivent avant de le lancer.

just release s’exécute à la racine de l’espace de co-développement, qui doit contenir les six dépôts. Chaque dépôt part dans l’ordre de ses dépendances, et seulement s’il a bougé depuis son dernier tag.

  1. Contrôles préalables : outils présents (git-cliff, jq, nix-update), dépôts propres, sur main, à jour de origin. Rien de long ne tourne avant.

  2. Plan : version actuelle et prochaine de chaque dépôt, aperçu des entrées de changelog, puis une seule confirmation. Refuser n’écrit rien. Ensuite, tout est automatique.

  3. Barrière globale : just check (code et tests unitaires).

  4. Générateur, s’il a bougé : just gen-test, release et poussée. Il possède le schéma etc/config.yaml, donc il part avant ce qui en dépend. Son tag est ensuite épinglé dans dnf/flake.nix (commit chore(deps)), pour que le contrat entre framework et générateur se lise dans un diff. Le générateur épinglé régénère enfin dnf/config/config.schema.json, le schéma que les projets lisent par leur lien dnf/ (commit chore(schema) s’il change).

  5. fleet-update, s’il a bougé : barrières ci et audit avec l’outillage de son nix develop, release et poussée. Son tag est ensuite épinglé dans dnf/pkgs/fleet-update par just pkg-update, et le paquet est construit (commit chore(deps)).

  6. Framework : just simulate all tourne après les épinglages, donc sur les dépendances réellement livrées. Ces scénarios ne tournent pas dans l’intégration continue : un scénario rouge arrête le train ici. Puis VERSION, l’épinglage du modèle de projet (templates/project/flake.nix), CHANGELOG.md, commit, tag et poussée.

  7. Documentation : just codegen régénère la référence, puis traduction et normalisation. S’il y a quelque chose à publier, le site est construit (avant le tag, pour qu’un lien cassé bloque), la version s’aligne sur la ligne MAJEUR.MINEUR du framework, puis le site est déployé.

  8. Boilerplate et exemple : le boilerplate est d’abord recopié depuis templates/project/ au tag du framework (CHANGELOG.md et flake.lock conservés), avec un bandeau « copie publiée » sous le titre de ses README. On ne le modifie donc jamais à la main. Puis, pour les deux, le tag du framework est écrit dans leur flake.nix, le flake.lock est commité (c’est lui qui rend un clone reproductible), puis changelog et tag identique à celui du framework. Chaque hôte de l’exemple est évalué jusqu’à system.build.toplevel contre le nouveau framework, matériel et secrets remplacés par des bouchons (dnf/just/scripts/eval-template-hosts.nix) : un modèle n’a ni l’un ni l’autre. Le boilerplate, livré sans var/generated/, n’a rien à évaluer avant just generate.

  9. Racine : just update-dnf, commit du seul flake.lock, puis poussée de la branche courante (sans tag), en tout dernier : son verrou pointe sur les releases publiées juste avant. La racine n’est jamais tagguée.

Seul le framework suit le niveau passé à la recette. Générateur et fleet-update restent en auto, la documentation suit la ligne du framework, les modèles son numéro.

Chaque dépôt est poussé avec son tag seul (git push --atomic origin main refs/tags/vX.Y.Z), jamais avec --tags : les tags locaux de travail restent locaux.

Sur un tag v*, release.yml refuse de publier si :

  • le fichier VERSION ne correspond pas au tag ;
  • CHANGELOG.md ne contient pas de section pour cette version ;
  • le verrou pointe un dnf-generator pris sur une branche plutôt que sur une release publiée.

Les trois contrôles de ci.yml (tests unitaires, évaluation, lint) sont rejoués via workflow_call, puis la section du changelog devient les notes de la release GitHub.

Le workflow installer-test.yml joue darkone new de bout en bout dans une VM QEMU (KVM du runner, vrai réseau) : l’ISO générique démarre, l’assistant est piloté par sa console série (pexpect), puis le système installé redémarre et se contrôle par SSH. Deux scénarios, en parallèle, environ 25 minutes chacun :

  • scripté : fichier de réponses, --non-interactive, disque en clair, phrase de passe de la sauvegarde dans un fichier ;
  • interactif : chaque question répondue à la console, disque chiffré, clé USB de sauvegarde débranchée puis rebranchée (QMP), premier démarrage déverrouillé par le sshd d’initrd.

Contrôles : lancement automatique sur tty1, système running sans unité en échec, /etc/nixos propre, page d’accueil installée, seconde sauvegarde par just backup-admin depuis le système installé (la première gardée sous sa date), sauvegarde ouverte depuis l’hôte. Le journal série est joint au run.

Trois déclenchements :

  • tag : appelé par release.yml, un échec retient l’ISO hors de la release ;
  • chaque lundi, sur main : une casse venue de nixpkgs unstable se voit avant le tag suivant ;
  • à la main : gh workflow run installer-test.yml --ref <branche>, avant une release ou après un changement de l’ISO.

Même test en local, sur l’ISO de l’arbre courant ou une autre :

Fenêtre de terminal
just test-installer # les deux scénarios
just test-installer interactive /chemin/de/l.iso

Disques et journaux : ~/.cache/dnf-installer-test/<scénario>/, environ 25 Go par scénario. Le code : dnf/tests/installer/.

Pour essayer l’arbre courant à la main, dans une fenêtre QEMU :

Fenêtre de terminal
just try # disque gardé
just try reset chemin/de/dnf-generator # disque neuf, générateur local

À la différence de nix run …#try, le projet écrit par l’assistant est épinglé sur le framework de l’ISO (path:) et non sur la release du template : le système installé porte le code non publié. Disque : ~/.local/share/dnf-try-local.

Le store de la machine sert de cache de paquets (nix-serve-ng sur 127.0.0.1:5000, lancé par just try si le port est libre). L’ISO fixe DNF_URL et DNF_CACHE : une relance de darkone new dans la VM les garde.

Un seul git-cliff 🡕 pour les six dépôts, dans dnf/just/scripts/cliff.toml. Le script dnf/just/scripts/bump.sh découvre la source de version du dépôt (VERSION, Cargo.toml, package.json, ou à défaut le dernier tag), assemble le fichier autour du bloc généré et préserve l’historique écrit à la main.