Chez Adaltas, nous maintenons plusieurs projets open-source Node.js organisés en monorepos Git et publiés sur NPM. Nous avons partagé notre expérience pour travailler avec Monorepos de Lerne dans une série d’articles :
C’est maintenant au tour de notre populaire open-source CSV de nœud projet à migrer vers un monorepo. Cet article vous guidera à travers les approches, techniques et outils disponibles utilisés pour migrer plusieurs projets Node.js hébergés sur GitHub vers le monorepo Lerna. A la fin, nous proposons une script bash nous avons utilisé pour migrer le projet Node CSV. Ce script peut être appliqué à un projet différent avec juste une petite modification.
Exigences pour la migration
La CSV de nœud le projet combine 4 packages NPM pour travailler avec des fichiers CSV dans Node.js enveloppés par le parapluie csv
forfait. Chaque package NPM a son riche historique de validation, et nous voulions enregistrer le maximum d’informations des anciens référentiels. Voici nos exigences pour la migration :
- conserver l’historique des commits avec un maximum d’informations (telles que les balises, ses messages et les commits de fusion)
- améliorer les messages de validation pour suivre les Engagements conventionnels spécification
- préserver les problèmes GitHub
Structure monorepo
Eh bien, nous avons 5 packages NPM à migrer vers le monorepo Lerna :
Nous voulons obtenir une structure de répertoires qui ressemble à ceci :
packages/
csv/
csv-generate/
csv-parse/
csv-stringify/
stream-transform/
lerna.json
package.json
Choisir la stratégie de journalisation Git
Lors de la migration de référentiels vers un monorepo, vous fusionnez leurs journaux de validation. Il y a 3 stratégies suggérées dans l’image ci-dessous.
- Succursale unique
Il fournit un journal simple contenant uniquement les commits sur les branches par défaut (master) de tous les packages. Différents journaux sont joints séquentiellement en ajoutant le dernier commit du package précédent en tant que commit parent au premier commit du package suivant. Cette stratégie rompt le tri du journal par date de validation. - Plusieurs branches avec un parent commun
Cela améliore la perception visuelle du journal en divisant les branches de différents référentiels. Un nouveau commit parent est ajouté à tous les premiers commits des branches. Au final, toutes les branches sont fusionnées dans la branche par défaut. - Plusieurs branches avec des parents différents
Cette stratégie ne réécrit pas les premiers commits des anciens référentiels. Cela nécessite une intervention minimale dans l’historique des commits et semble logiquement plus correct car initialement, les référentiels n’avaient pas de parent commun.
Fusionner les journaux de validation
Lerne a un mécanisme intégré pour rassembler les packages NPM autonomes existants dans un monorepo préservant l’historique des commits. La lerna import
La commande importe un package d’un référentiel externe dans packages/
. La séquence de commandes est assez simple : vous devez initialiser les référentiels Git et Lerna, effectuer le premier commit, puis commencer à importer des packages à partir de référentiels Git clonés localement. Vous pouvez trouver des instructions d’utilisation de base dans la documentation ici.
Utilisant lerna import
, vous ne pouvez suivre que la 1ère ou la 2ème stratégie de log Git décrite ci-dessus. Pour le 2e, vous devez créer une branche distincte par référentiel d’importation comme ceci :
git checkout -b package-1
lerna import /path/to/package-1
git checkout master
git checkout -b package-2
lerna import /path/to/package-2
lerna import
fournit un outil facile à utiliser pour migrer les référentiels vers le monorepo Lerna. Cependant, il aplatit l’historique des commits en réduisant les commits de fusion et il ne migre pas les balises et leurs messages. Malheureusement, ces limitations ne répondaient pas à notre exigence de sauvegarder un maximum d’informations à partir des référentiels existants et nous avons dû utiliser un outil différent.
Le natif git merge
permet de fusionner des historiques non liés à l’aide de la --allow-unrelated-histories
option. Il conserve l’historique complet des commits d’une branche ciblée avec ses balises. Dans ce cas, vous réaliserez la 3ème stratégie de log Git.
Fusion d’un historique de validation d’un référentiel externe dans un référentiel actuel à l’aide de --allow-unrelated-histories
aussi simple que d’exécuter 2 commandes :
git remote add -f <external-repo-name> <external-repo-path>
git merge --allow-unrelated-histories <external-repo-name>/<branch-name>
Réécriture des messages de validation
Pour mettre plus d’ordre et de transparence dans le journal de validation combiné, nous préfixons tous les messages de validation avec leurs noms de package. De plus, nous les rendons compatibles avec les Engagements conventionnels spécification que nous suivons dans nos derniers projets. Cette spécification standardise les messages de validation en les rendant plus lisibles et faciles à automatiser.
Pour implémenter cela, nous devons réécrire tous les messages de validation en les préfixant avec la chaîne comme chore(
.
Nous avons choisi le
chore
type juste pour le rendre compatible avec la spécification, et nous ne voulions pas créer d’expressions régulières complexes pour le supporter pleinement.
Il existe 2 outils pour réécrire les messages de commit :
Suivant la recommandation de Git, nous choisissons le git filter-repo
. Après avoir installé l’outil à l’aide de ces consignesla commande pour réécrire les messages de validation d’un référentiel actuel est :
git filter-repo --message-callback 'return b"chore(<package-name>): " + message'
Pour voir plus d’exemples d’utilisation de la réécriture de l’historique du référentiel avec git filter-repo
vous pouvez suivre cette documentation.
Transférer des problèmes GitHub
Après avoir migré les référentiels et publié un nouveau monorepo sur GitHub, nous souhaitons transférer les Problèmes liés à GitHub des anciens dépôts. Les problèmes peuvent être transférés d’un référentiel à un autre à l’aide de l’interface GitHub. Vous pouvez suivre ceci guide pour apprendre les consignes.
Malheureusement, au moment d’écrire ces lignes, il n’y a aucune possibilité d’effectuer un transfert de problèmes en masse. Les problèmes doivent être transférés un par un. Mais cela peut vous donner une excuse pour “oublier” de transférer les problèmes ennuyeux en attente créés par la communauté du projet 😉
Qu’en est-il de Demandes d’extraction GitHub? Il y aura une perte et nous devons vivre avec. Une bonne chose est que les liens entre les problèmes écrits dans les commentaires et les demandes de tirage liées seront sauvegardés grâce à la redirection.
Scénario de migration
Le script bash de migration exploite les approches choisies et les outils décrits ci-dessus. Il génère la ./node-csv
répertoire contenant le CSV de nœud dossiers de projet réorganisés en un monorepo Lerna.
#!/bin/sh
set -e
REPOS=(
https://github.com/adaltas/node-csv
https://github.com/adaltas/node-csv-generate
https://github.com/adaltas/node-csv-parse
https://github.com/adaltas/node-csv-stringify
https://github.com/adaltas/node-stream-transform
)
OUTPUT_DIR=node-csv
PACKAGES_DIR=packages
rm -rf $OUTPUT_DIR && mkdir $OUTPUT_DIR && cd $OUTPUT_DIR
git init .
git remote add origin $REPOS[0]
for repo in $REPOS[@]; do
splited=($repo//// )
package=$splited[$#splited[@]-1]/node-/
rm -rf $TMPDIR/$package && mkdir $TMPDIR/$package && git clone $repo $TMPDIR/$package
git filter-repo \
--source $TMPDIR/$package \
--target $TMPDIR/$package \
--message-callback "return b'chore($package): ' + message"
git remote add -f $package $TMPDIR/$package
git merge --allow-unrelated-histories $package/master -m "chore($package): merge branch 'master' of $repo"
mkdir -p $PACKAGES_DIR/$package
files=$(find . -maxdepth 1 | egrep -v ^./.git$ | egrep -v ^.$ | egrep -v ^./$PACKAGES_DIR$)
for file in $files// /[@]; do
mv $file $PACKAGES_DIR/$package
done
git add .
git commit -m "chore($package): move all package files to $PACKAGES_DIR/$package"
git branch init/$package $package/master
done
rm $PACKAGES_DIR/**/CONTRIBUTING.md
rm $PACKAGES_DIR/**/CODE_OF_CONDUCT.md
rm -rf $PACKAGES_DIR/**/.github
git add .
git commit -m "chore: remove outdated packages files"
Pour exécuter ce script, il suffit de créer un fichier exécutable, par exemple avec le nom migrate.sh
collez-y le contenu du script et exécutez-le avec la commande :
chmod u+x ./migrate.sh
./migrate.sh
Noter! N’oubliez pas d’installer
git-filter-repo
avant d’exécuter le script.
Notes pour chaque étape du script :
1.
Configurer
Les variables de configuration définissent la liste des référentiels à migrer, le répertoire de destination du nouveau monorepo Lerna et le dossier des packages qu’il contient. Vous pouvez modifier ces variables pour réutiliser ce script pour votre projet.2.
Initialiser un nouveau dépôt
Nous initialisons un nouveau référentiel. Le premier référentiel est également enregistré en tant que serveur distantorigin
dépôt.3.
Migrer les référentiels3.1.
Obtenir le nom du package
Il extrait les noms de packages à partir de leurs liens de référentiels. Dans notre cas, les référentiels sont préfixés parnode-
que nous ne voulons pas conserver.3.2.
Réécrire les messages de validation via un référentiel temporaire
Pour ajouter un préfixe aux commits de chaque paquet en utilisant le modèlechore(
, nous devons le créer séparément pour chaque référentiel. Ceci est possible via un référentiel cloné localement dans un dossier temporaire.): 3.3.
Fusionner le référentiel dans monorepo
Dans un premier temps, nous ajoutons un référentiel cloné localement en tant que distant au monorepo. Ensuite, nous fusionnons son historique de validation en spécifiant un message de validation de fusion.3.4.
Déplacer les fichiers du référentiel vers le dossier packages
Après la fusion, les fichiers du référentiel fusionné apparaissent sous le répertoire racine monorepo. Suivant le structure nous voulons atteindre, nous déplaçons ces fichiers vers lepackages
répertoire et validez-le.3.5.
Créer une nouvelle branche
L’historique des commits est désormais associé à nos monorepos via un référentiel distant. L’historique sera perdu si le référentiel d’origine est effacé. Pour stocker l’historique dans le monorepo, nous créons une branche qui suit le référentiel distant et le préfixe avecinit/
.
4.
Nettoyer et supprimer les fichiers obsolètes Par souci d’illustration, nous nettoyons certains fichiers de package qui sont obsolètes grâce à la migration. Certains de ces fichiers doivent être déplacés vers le répertoire racine du référentiel.
Prochaines étapes
Le référentiel GIT est maintenant prêt et, en tant que tel, est qualifié de monorepo. Pour le rendre utilisable, des fichiers supplémentaires doivent être créés comme une racine package.json
dossier, le lerna.json
fichier de configuration si vous utilisez Lerna et un README
dossier. Référez-vous au premier article de notre série pour appliquer les modifications nécessaires et initialisez votre monorepo avec Lerna.
Conclusion
La migration de projets open-source existants vous demande d’être ordonné et minutieux car une petite erreur peut ruiner le travail de vos utilisateurs. Toutes les étapes doivent être soigneusement analysées et bien testées. Dans cet article, nous avons couvert l’étendue des travaux pour migrer plusieurs projets Node.js vers le monorepo Lerna. Nous avons envisagé différentes approches, techniques et outils disponibles pour automatiser la migration sur l’exemple de notre CSV de nœud projet open source.
More Stories
Test des écouteurs Jabra Elite 5 ANC : superbe design, bon son
La filiale londonienne du CWU dit aux ingénieurs de BT de rejeter l’offre de rémunération
Revue du générateur solaire Jackery Explorer 1500 : la protection contre les pannes de courant à son meilleur