La première fois que j’ai travaillé en équipe, ça a été le fiasco total !
Aucun des deux développeurs avec lesquels je travaillais n’a réussi à démarrer le projet que je réalisais.
Le hic, c’est que je partais en vacances et il fallait que mes collègues puissent le reprendre pour continuer sans moi.
De mon côté, c’était l’éternel :
« Je ne comprends pas, ça marche sur ma machine ! »
Que s’est il passé ?
C’était il y a quelques années, NPM, le gestionnaire de dépendances JavaScript venait de passer en version 5, et avec lui, l’ajout d’une fonctionnalité de marque : le fichier de verrouillage des dépendances (le fameux « package-lock.json »).
C’est une fonctionnalité qui permet de figer l’arbre des dépendances installées et de reproduire cette installation à l’identique autant de fois que nécessaire.
Comment ça marche ?
Supposons que vous souhaitez installer la dépendance « axios » (un client HTTP) dans votre super application.
Vous allez taper la commande suivante dans votre terminal :
npm install axios
Votre fichier « package.json » contiendra alors l’information suivante :
{ "name": "my-super-app", "version": "1.0.0", "license": "UNLICENSED", "dependencies": { "axios": "^0.21.1" } }
Et votre fichier « package-lock.json » contiendra quelque chose comme ça :
{ "name": "my-super-app", "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { "axios": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", "requires": { "follow-redirects": "^1.10.0" } }, "follow-redirects": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" } } }
Le fichier de verrouillage va stocker la version exacte installée à cet instant de axios (même si votre « package.json » autorise plusieurs versions) avec l’URL de téléchargement et la signature de la dépendance installée.
Et surtout, le plus important, il va faire la même chose avec les dépendances de vos dépendances (ce que l’on appelle les dépendances transitives).
Dans notre exemple, axios dépend du paquet « follow-redirects » (il en a besoin pour fonctionner), ce dernier va donc également se retrouver dans ce fameux « package-lock.json » avec une version figée, même si axios en accepte plusieurs dans son propre fichier « package.json ».
La prochaine fois que vous ferez un ‘npm install’, NPM utilisera le fichier de verrouillage pour reproduire la même installation (il suffira d’aller chercher les URL sauvegardées à l’intérieur).
NPM peut ensuite vérifier que c’est toujours exactement la même chose qui est installé en calculant la signature de la dépendance installée et en la comparant à celle du fichier de verrouillage.
Ce mécanisme permet de garantir que votre installation de dépendances est reproductible.
Sauf que, quand NPM s’est mis à jour sur ma machine, la seule chose que j’ai constaté, c’est qu’un nouveau fichier généré – que je n’avais pas demandé (le fameux package-lock.json) – était apparu et venait poluer mon beau projet.
Comme je ne comprenais pas ce package-lock.json, j’ai trouvé la configuration me permettant de m’en débarrasser et de retourner à mes petites habitudes bien rangées en deux temps trois mouvements (il suffit de créer un fichier « .npmrc » à la racine de votre répertoire de travail avec la configuration suivante).
package-lock=false
Le problème, c’est que certaines dépendances transitives avaient été mises à jour entre le moment où j’avais fait mon installation et celui où mes collègues l’ont effectué.
Et pas de bol, l’une d’entre elle avait introduit un changement cassant, autorisant notre dépendance directe à l’installer.
Sans le fichier de verrouillage pour figer la version qui marchait pour moi, mes collègues n’étaient pas en mesure de reproduire une installation identique, et dans ce cas particulier, ne pouvait même plus lancer le projet à cause du changement cassant (une fonction avait été renommée) !
Le fichier de verrouillage est donc de nos jours un élément indispensable pour tous les projets, que l’on travaille seul ou à plusieurs, au vu du nombre exponentiellement grandissant de bibliothèques utilisées (ce qui est d’ailleurs un problème, si le sujet de la performance et de la lourdeur des logiciels vous intéresse, je vous recommande la lecture de ma traduction de l’excellent article de Nikita Prokopov : https://blog.romainfallet.fr/desenchantement-logiciel).
Dans mon cas, si j’avais supprimé mon dossier node_modules, j’aurais rencontré exactement le même problème que mes collègues en essayant de réinstaller mes dépendances.
Pour profiter des bienfaits du fichier de verrouillage il faut donc bien l’ajouter dans vos commits et l’envoyer sur votre dépôt Git.
C’est donc après quelques heures de recherche et une petite remise en question que je suis parvenu à remettre en place une installation reproductible après avoir rétrogradé la version du paquet qui posait problème.
J’ai pu ensuite partir sereinement en vacances !
Mais mais mais, on ne peut pas se quitter là-dessus…
Si les mainteneurs du paquet incriminé avaient suivi la spécification de gestion sémantique des versions (Semantic Versionning ou SemVer en anglais), tout ceci ne serait pas arrivé !
Qu’est-ce que c’est ? Ce sera le sujet du prochain article !
Abonnez-vous sur https://compagnon.artisandeveloppeur.fr/veille et les numéros de version n’auront plus de secret pour vous (très utile si vous ne connaissez pas la signification des « ^ » et « ~ » dans votre package.json ;)).
Au plaisir d’échanger !
A préciser que depuis une certaine version de NPM, ce dernier ne prend plus en compte le package-lock.json pendant un nom install.
La seule manière propre de récupérer les dépendances correspondantes au package-lock.json est d’utiliser la commande npm ci.
Pourtant, même sur la 8, la commande `npm install` utilise toujours le package-lock.json :
https://docs.npmjs.com/cli/v8/commands/npm-install
Ça doit dépendre de la version de NPM, en effet. J’utilise une version 6 embarquée dans la 14 LTS de Node.
Ce que j’ai constaté, c’est qu’un npm install modifie le package-lock.json et peut mettre à jour des dépendances alors qu’un npm ci applique bêtement le contenu du package-lock.json. Cf. ici : https://stackoverflow.com/questions/45022048/why-does-npm-install-rewrite-package-lock-json
A noter tout de même que la commande npm ci est recommandée pour avoir un environnement iso de manière plus rapide que npm install : https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable
J’ai eu exactement le même problème il y a quelques mois en arrivant sur un projet parce que le package-lock.json était dans le .gitignore !
Si le sujet t’intéresse et que tu veux creuser un peu plus, j’ai trouvé un article super complet qui explique en détail comment ce fichier fonctionne et comment npm l’utilise:
https://blog.boris.sh/blog/package-lock-les-mauvaises-pratiques-a-bannir-pour-un-projet-stable
Bonne journée !