Aller au contenu
duxt

Héberger les couches fournisseurs dans un monorepo Turborepo

La couche passe dans packages/duxt et le site dans apps/www, chaque paquet publié se versionne et s'étiquette seul en <name>@vX.Y.Z, et la décision de dépôts dédiés consignée dans

Contexte

#74 et #75 ont chacune décidé que les fournisseurs de recherche officiels sont publiés comme couches compagnons indépendantes dans des dépôts dédiés — @kirchdev/duxt-typesense, @kirchdev/duxt-meilisearch. Aucun de ces dépôts n’existe, et ici un nouveau dépôt open source n’est pas à une commande près : il est provisionné via OpenTofu, puis porte toute la couche méta lui-même — stubs de workflows, release-please, Dependabot, CodeQL, une licence, un README et une configuration d’agents. C’est le prix permanent de deux adaptateurs minces au-dessus d’un seul hook de build.

Le contrat que les deux consomment, duxt:search:records, vit dans ce dépôt. Réparti sur trois, chaque changement deviendrait une valse des versions — monter la couche, publier, élargir la plage de chaque fournisseur, publier — sans une seule barrière qui exécute le hook face à un consommateur. Le site de développement ne pouvait pas essayer un fournisseur sans dépendre d’une version publiée.

Le dépôt était en outre taillé pour un seul paquet : sa racine était la couche, et trente modules de build s’étalaient à côté de la configuration méta, séparés d’elle par la seule liste files.

Décision

Un dépôt, un workspace pnpm piloté par Turborepo.

  • packages/duxt est @kirchdev/duxt. Ses modules de build quittent la racine plate pour des dossiers thématiques sous build/ — sources/, sections/, bruno/, openapi/, search/, content/, og-image/, git/, config/, cli/ — et la liste files se réduit à des répertoires. La table exports garde tous ses noms de sous-chemin.
  • apps/www est le site qui développe la couche, avec les vérifications qui lisent son build.
  • La racine n’est pas un paquet. Elle garde la configuration du workspace et la méta, ainsi que docs/, que apps/www publie.
  • Les paquets fournisseurs arrivent à côté de la couche en packages/duxt-typesense et packages/duxt-meilisearch, avec #74 et #75.

Chaque paquet publié est sa propre unité de version. release-please tourne en mode manifeste avec une entrée par paquet, chacune avec sa version et son changelog, et une version ne publie que les paquets qu’elle a montés. Une version partagée a été écartée, car elle republie des paquets inchangés sous un nouveau numéro.

Chaque paquet étiquette <name>@vX.Y.Z, la couche comprise — include-component-in-tag avec tag-separator: "@", si bien que duxt@v0.5.0 côtoie duxt-typesense@v0.1.0. La version elle-même reste un simple nombre dans package.json et sur npm. Les tags simples v0.1.0…v0.4.0 restent tels quels, et last-release-sha pointe une fois sur le commit de la version v0.4.0, pour que release-please retrouve la version précédente de la couche malgré le nouveau motif.

Le changelog de la couche reste à la racine du dépôt, en changelog-path: "/CHANGELOG.md". Le site publie le changelog de chaque version qu’il sert, lu dans le checkout de cette version, et chaque tag existant a le fichier à cet endroit. Le déplacer dans le paquet aurait laissé chaque édition antérieure sans ses pages de versions. Le changelog d’un fournisseur vit dans son propre répertoire ; il n’a pas d’historique à préserver.

Le versionnage par tags de duxt apprend les tags par composant, et doit arriver avant la première version qui suit le déménagement. latest, la découverte des versions et l’ordre du sélecteur lisent <name>@vX.Y.Z comme vX.Y.Z ; le libellé et le segment d’URL n’affichent que la version ; et une source peut nommer un tagComponent pour se limiter aux tags d’un paquet, les tags simples comptant comme son historique antérieur. Sans cela, latest dans apps/www serait resté sur v0.4.0 après duxt@v0.5.0 sans qu’aucun build n’échoue.

C’est le registre qui décide d’une publication. Un paquet est publié quand sa version a un tag <component>@v<version> et que npm n’a pas cette version. Le _publish-npm.yml central publie la racine du dépôt et le _release-please.yml central ne transmet que les sorties du paquet racine : les jobs de publication appartiennent donc au dépôt jusqu’à ce que les corps centraux acceptent un répertoire de travail.

Les fournisseurs dépendent en peer de @kirchdev/duxt avec une plage large, >=0.4.0 <1, la borne inférieure étant ce dont le fournisseur a besoin en premier — jamais ^0.x, qui obligerait chaque fournisseur à publier à chaque mineure de la couche. La barrière du monorepo prouve la compatibilité, et la borne inférieure ne monte que lorsque le contrat du hook change de façon incompatible.

Le cache de Turborepo est uniquement local — pas de cache distant, pas de compte, pas de jeton. Une tâche n’est mise en cache que là où ses entrées déclarées déterminent son résultat : c’est le cas des tests unitaires ; le typecheck, le build et chaque vérification d’un build résolvent latest auprès d’un distant et ne le sont pas.

Conséquences

La décision de dépôts dédiés consignée dans #74 et #75 est remplacée ; ces issues construisent leurs paquets fournisseurs ici.

Chaque chemin relatif à la couche a bougé, et aucun ne se résout comme il se lit : le déménagement ne vaut donc que par sa vérification — la barrière complète, un build Workers avec la vérification du classement des routes, et le tarball empaqueté installé dans un consommateur de test.

Un changement du contrat du hook et de ses consommateurs arrive désormais dans une seule pull request derrière une seule barrière, et le site de développement peut prendre un fournisseur comme dépendance du workspace.

Un monorepo qui étiquette avec release-please peut pointer duxt vers ses propres tags, ce qui fait du support des tags préfixés une fonctionnalité pour les consommateurs et non une simple commodité de ce dépôt.

Les chemins des enregistrements précédents décrivent la structure de leur époque. Ils ne sont pas réécrits, car ces enregistrements ne font que s’allonger.

Cette page vous a-t-elle été utile ?