Pourquoi «npm install» réécrit-il package-lock.json?

614

Je viens récemment de passer à npm @ 5 . J'ai maintenant un fichier package-lock.json avec tout de package.json . Je m'attendrais à ce que, lorsque j'exécute,npm install les versions de dépendance soient extraites du fichier de verrouillage pour déterminer ce qui devrait être installé dans mon répertoire node_modules . Ce qui est étrange, c'est qu'il finit par modifier et réécrire mon fichier package-lock.json .

Par exemple, le fichier de verrouillage a été spécifié pour être à la version 2.1.6 . Ensuite, après la npm installcommande, la version a été changée en 2.4.1 . Cela semble aller à l'encontre de l'objectif global d'un fichier de verrouillage.

Qu'est-ce que je rate? Comment faire pour que npm respecte réellement mon fichier de verrouillage?

Viper Bailey
la source
4
Cela ne répond pas à votre question donc j'espère qu'un commentaire est correct, mais jetez un œil à Yarn. Le changement nous a pris moins d'une heure.
KayakinKoder du
4
Le même problème mais en utilisant le fil github.com/yarnpkg/yarn/issues/570 (très instructif)
Yves M.
2
J'ai le même problème. Mon package-lock.jsonse régénère quand je cours npm install. Cela sent comme un bogue npm. Utilisez-vous votre propre registre?
HaNdTriX
@YvesM. --no-saveempêche la modification du fichier de verrouillage, mais cela n'affecte pas la mise à niveau de dépendance de premier niveau maladroite que l'OP mentionne.
Ross Allen

Réponses:

423

Mise à jour 3: Comme le soulignent d'autres réponses, la npm cicommande a été introduite dans npm 5.7.0 comme moyen supplémentaire pour obtenir des builds rapides et reproductibles dans le contexte CI. Voir la documentation et le blog npm pour plus d'informations.


Mise à jour 2: le problème de mise à jour et de clarification de la documentation est le problème GitHub # 18103 .


Mise à jour 1: le comportement décrit ci-dessous a été corrigé dans npm 5.4.2: le comportement actuellement prévu est décrit dans le problème GitHub n ° 17979 .


Réponse originale: Le comportement de a package-lock.jsonété modifié dans npm 5.1.0 comme indiqué dans le numéro # 16866 . Le comportement que vous observez est apparemment prévu par npm à partir de la version 5.1.0.

Cela signifie que cela package.jsonpeut remplacer package-lock.jsonchaque fois qu'une nouvelle version est trouvée pour une dépendance dans package.json. Si vous souhaitez épingler efficacement vos dépendances, vous devez maintenant spécifier les versions sans préfixe, par exemple, vous devez les écrire en tant que 1.2.0au lieu de ~1.2.0ou ^1.2.0. Ensuite, la combinaison de package.jsonet package-lock.jsonproduira des versions reproductibles. Pour être clair:package-lock.json seul ne verrouille plus les dépendances au niveau racine!

Que cette décision de conception soit bonne ou non est discutable, il y a une discussion en cours résultant de cette confusion sur GitHub dans le numéro # 17979 . (À mes yeux, c'est une décision discutable; au moins le nomlock n'est plus vrai.)

Une autre remarque: il existe également une restriction pour les registres qui ne prennent pas en charge les packages immuables, comme lorsque vous extrayez des packages directement depuis GitHub au lieu de npmjs.org. Consultez cette documentation sur les verrous de package pour plus d'explications.

jotaen
la source
43
À quoi sert le hack npm updatealors? : o J'ai eu le même sentiment que les npm installdeps mis à jour, mais je ne veux pas le croire .. mais il semble que c'est malheureusement vrai .. Quoi qu'il en soit, il y a toujours une option à utiliser npm shrinkwrappour verrouiller les deps, mais le nom du package-lock est définitivement incorrect car il ne gèle pas, ni ne bloque les dépendances ..
Jurosh
266
Quel bordel! Le plus grand gestionnaire de paquets au monde, mais il n'a pas de documentation sur la façon dont cela devrait fonctionner. Tout le monde devine ce qu'il doit faire et cela se transforme en une guerre d'opinions. La discussion est bonne, mais devrait avoir lieu avant une libération dans la nature. À un moment donné, quelqu'un doit faire le dernier appel, puis peut être implémenté, documenté et publié. PHP a été conçu par un comité et ad-hoc ensemble et regardez comment cela s'est avéré. Je détesterais voir la même chose arriver à un outil aussi critique et largement utilisé.
Landon Poch
85
Alors, quel est l'intérêt d'utiliser package-lock? Je pensais que cela créerait le même environnement dans différents espaces de travail, mais il s'avère qu'il ne fait rien
laltin
17
"Ensuite, la combinaison de package.json et package-lock.json produira des versions reproductibles." Quel rôle a "package-lock.json" ici? "Package.json" ne produit-il pas déjà à lui seul des builds reproductibles si aucun préfixe de version n'est utilisé?
Jānis Elmeris
12
@ JānisElmeris Je pense que package.json ne peut pas verrouiller les dépendances profondes ...
Juan Mendes
165

J'ai trouvé qu'il y aura une nouvelle version de NPM 5.7.1 avec la nouvelle commande npm ci, qui installera à partir de package-lock.jsonseulement

La nouvelle commande npm ci s'installe UNIQUEMENT à partir de votre fichier de verrouillage. Si votre package.json et votre fichier de verrouillage ne sont pas synchronisés, il signalera une erreur.

Cela fonctionne en jetant vos node_modules et en les recréant à partir de zéro.

Au-delà de vous garantir que vous n'obtiendrez que ce qui se trouve dans votre fichier de verrouillage, il est également beaucoup plus rapide (2x-10x!) Que l'installation de npm lorsque vous ne démarrez pas avec un node_modules.

Comme vous pouvez le déduire de son nom, nous nous attendons à ce que ce soit une aubaine pour les environnements d'intégration continue. Nous prévoyons également que les personnes qui effectuent des déploiements de production à partir de balises git verront des gains majeurs.

Ivan Shcherbakov
la source
133
Cela devrait être le comportement par défaut s'il existe un fichier de verrouillage.
nullabilité
13
Ils ont donc changé la façon dont npm i fonctionne, seulement pour le ramener sous npm ci des mois plus tard?
Scott Flack
1
Je suis encore confus. La documentation indique "Assurez-vous que vous disposez d'un package-lock et d'une installation à jour: npm install" avant d'exécuter la commande npm cidans ce projet. Ne pas npm installremplacer le fichier package-lock.json?
adiga
1
AFAIK: @adiga - à partir de la version 5.4, npm ne modifie le fichier de verrouillage que si nécessaire pour le faire, pour répondre aux spécifications de packages.json . Donc, si les packages avaient l'habitude de dire thatpackage: 1, et lock dit ..: 1.0.4, dev peut modifier pour dire thatpackage: 2- et cela forcera le fichier de verrouillage à changer, car il 1.0.4n'est pas compatible avec la nouvelle plage spécifiée. Si vous ne changez pas packages.json, la version exacte restera verrouillée jusqu'à la suppression du fichier de verrouillage. [Si ne reste pas verrouillé et n'a pas modifié packages.json, déposez un rapport de bogue.]
ToolmakerSteve
1
@George D'après les informations que j'ai lues (pour les versions récentes de npm) et mes tests limités: oui aux deux.
Venryx
95

Utilisez le nouveau

npm ci

npm ci promet le plus d'avantages aux grandes équipes. Donner aux développeurs la possibilité de «se déconnecter» sur un verrou de package favorise une collaboration plus efficace entre les grandes équipes, et la possibilité d'installer exactement ce qui se trouve dans un fichier de verrouillage a le potentiel d'économiser des dizaines, voire des centaines d'heures de développement par mois, libérant les équipes pour passer plus de temps à construire et à expédier des choses incroyables.

Présentation npm cipour des versions plus rapides et plus fiables

Gal Margalit
la source
3
cela me semble correct? quelqu'un d'autre peut-il confirmer?
phouse512
6
@ phouse512 C'est correct. Nous utilisons à peu près uniquementnpm ci , et n'utilisons que npm installsi la mise à jour ou l'installation de nouveaux packages.
Jacob Sievers
1
Commentaires récents, etc. C'est la réponse avec laquelle je vais. Dommage qu'ils ne puissent pas réparer l'horrible snafu, mais si le nouvel évangile est "npm ci", alors très bien. Je peux m'adapter.
Svend
Dommage, il supprime toujours un node_modulesrépertoire existant et reconstruit localement, même s'il s'agit d'un lien symbolique vide mais important. :(
Joe Atzberger
2
@ToolmakerSteve Ne retenez pas votre souffle! Je pense que la suppression du contenu d'un répertoire serait plus lente que la simple suppression du répertoire. Vous devez énumérer le contenu, puis émettre une série de commandes de suppression plutôt que la seule commande de suppression vers le système d'exploitation. Avec les problèmes de performances précédemment nivelés à npm et l'amélioration de l'utilisation, npm cije pense qu'ils seraient très réticents à introduire tout ce qui pourrait réduire les performances pour un cas d'utilisation assez rare. Vous voudrez peut-être consulter pnpm.js.org qui utilise des liens durs pour réduire l'utilisation du disque.
Caltor
64

Réponse courte:

  • npm install honore package-lock.json uniquement s'il satisfait aux exigences de package.json.
  • S'il ne satisfait pas à ces exigences, les packages sont mis à jour et package-lock est remplacé.
  • Si vous échouez plutôt à la compilation, plutôt que de réécrire package-lock lorsque cela se produit, utilisez npm ci.

Voici un scénario qui pourrait expliquer les choses (vérifié avec NPM 6.3.0)

Vous déclarez une dépendance dans package.json comme:

"depA": "^1.0.0"

Ensuite, vous npm installgénérez un package-lock.json avec:

"depA": "1.0.0"

Quelques jours plus tard, une nouvelle version mineure de "depA" est publiée, par exemple "1.1.0", puis ce qui suit est vrai:

npm ci       # respects only package-lock.json and installs 1.0.0

npm install  # also, respects the package-lock version and keeps 1.0.0 installed 
             # (i.e. when package-lock.json exists, it overrules package.json)

Ensuite, vous mettez à jour manuellement votre package.json pour:

"depA": "^1.1.0"

Relancez ensuite:

npm ci      # will try to honor package-lock which says 1.0.0
            # but that does not satisfy package.json requirement of "^1.1.0" 
            # so it would throw an error 

npm install # installs "1.1.0" (as required by the updated package.json)
            # also rewrites package-lock.json version to "1.1.0"
            # (i.e. when package.json is modified, it overrules the package-lock.json)
Ahmad Abdelghany
la source
4
C'est en effet le comportement recherché d'un fichier "verrouillé". Apparemment, ce n'était pas le cas avec les anciennes versions de NPM.
Blockost
1
Alors, comment npm suit-il la dernière mise à jour de package.json? Que se passe-t-il lorsque vous déplacez votre package.json et package-lock.json vers un autre ordinateur? Comment npm dans un nouvel ordinateur sait-il si package.lock est l'original ou s'il a été mis à jour, pour décider s'il doit mettre à jour package-lock.json ou non?
Lahiru Chandima
3
@LahiruChandima Il ne suit pas vraiment les mises à jour. npm installutilisera les versions verrouillées de package-lock.jsonsauf s'il ne satisfait pas le package.jsoncas auquel il installe package.json et reconstruit package-lock.json en conséquence. Si vous l'avez modifié package.jsonde telle manière que le verrouillage de package existant satisfait toujours la mise package.jsonà jour, il continuera à l'utiliserpackage-lock
Ahmad Abdelghany
1
Si vous avez déjà un module dans node_modules qui répond aux exigences de package.json, alors npm installne fait rien, indépendamment de package-lock.json. Nous devons mettre à jour explicitement les packages même lorsqu'il existe des mises à jour qui correspondent au semver spécifié dans package.json. C'est du moins mon expérience depuis des années.
carlin.scott
1
@ToolmakerSteve J'étais également sceptique quant au comportement signalé par @ carlin.scott, mais je viens de le tester, et en fait il a raison. Si la version à l'intérieur node_modulessatisfait la plage de package.json, et qu'il n'y a pas de package-lock.jsonfichier, npm ne mettra pas à jour le module lors de l'exécution npm install. Je suppose que c'est bien puisque vous pouvez utiliser npm update(ou npm-checkpour les dernières) pour mettre à jour les dépendances, et ce comportement est plus rapide pour le cas de quelqu'un qui ajoute juste une entrée à package.json, et ne veut pas que les paquets sans rapport se mettent à jour vers la dernière qui satisfait le semi-ver gamme.
Venryx
19

Utilisez la npm cicommande au lieu denpm install .

"ci" signifie "intégration continue".

Il installera les dépendances du projet basées sur le fichier package-lock.json au lieu des dépendances du fichier lenient package.json.

Il produira des builds identiques à ceux de vos coéquipiers et il est également beaucoup plus rapide.

Vous pouvez en savoir plus à ce sujet dans cet article de blog: https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable

Daniel Tonon
la source
2
cifait référence à "l'intégration continue", comme mentionné dans les documents et le blog annonçant la commande: blog.npmjs.org/post/171556855892/…
Joe Atzberger
Merci Joe. J'ai mis à jour ma réponse avec le nom correct et lié à l'article de blog. 😊 (pour ceux qui lisent ceci, j'ai dit précédemment que cela signifie "installation propre")
Daniel Tonon
"Et c'est aussi beaucoup plus rapide" - il supprimera le node_modulesdossier et le recréera à partir de zéro. Est-ce vraiment beaucoup plus rapide? Le dossier est- il également npm installsupprimé node_modules?
izogfif
Je pense que la vitesse vient de npm n'ayant pas besoin de calculer les packages à télécharger. Pensez-y comme npm installdoit résoudre toutes les dépendances du package lors de son exécution. npm ciest juste une liste d'achats de "obtenir ces modules exacts".
Daniel Tonon
8

À l'avenir, vous pourrez utiliser un --from-lock-fileindicateur (ou similaire) pour installer uniquement à partir dupackage-lock.json sans le modifier.

Cela sera utile pour les environnements CI, etc. où les builds reproductibles sont importants.

Voir https://github.com/npm/npm/issues/18286 pour le suivi de la fonctionnalité.

Timothy Higinbottom
la source
J'en doute. Comment, si les dépendances sont différentes pour différents systèmes d'exploitation, comment pouvez-vous forcer l'installation de quelque chose qui ne fonctionnerait pas?
Yevgeniy Afanasyev le
4
@YevgeniyAfanasyev Au lieu de ce drapeau, il a été implémenté comme celui npm ciqui traite également votre question.
spex
8

Il semble que ce problème soit résolu dans npm v5.4.2

https://github.com/npm/npm/issues/17979

(Faites défiler jusqu'au dernier commentaire du fil)

Mise à jour

Actuellement corrigé dans 5.6.0. Il y avait un bogue multiplateforme dans 5.4.2 qui provoquait toujours le problème.

https://github.com/npm/npm/issues/18712

Update 2

Voir ma réponse ici: https://stackoverflow.com/a/53680257/1611058

npm ci est la commande que vous devez utiliser lors de l'installation de projets existants maintenant.

Daniel Tonon
la source
5
J'utilise 5.4.2 et cela entraîne toujours la modification de mon package-lock.json quand npm i. Par exemple, le module fseventsest supprimé lorsque je npm isur une machine qui ne prend pas en charge fsevents, puis le module est ajouté à npm inouveau lorsque l'on sur une machine qui le fait.
hrdwdmrbl
Ensuite, vous devriez soulever un nouveau problème dans le dépôt npm GitHub expliquant cela. Si cela ne fonctionne pas comme ils disent que cela devrait fonctionner, ils le voient comme un bogue de haute priorité qui doit être corrigé de toute urgence.
Daniel Tonon
@hrdwdmrbl Je vois la même fseventsbaisse dans mon package-lock.jsonavec [email protected]tout en collaborant avec les contributeurs Mac OS X. Si vous n'avez pas ouvert de problème, je le ferai.
AL le X
@hrdwdmrbl J'ai trouvé cela (et le long fil de problèmes associés) après avoir laissé mon commentaire et j'ai oublié de revenir à SO pour mettre à jour mon commentaire. Merci d'avoir récupéré. Tout va bien.
AL le X
4

Vous avez probablement quelque chose comme:

"typescript":"~2.1.6"

dans package.jsonlequel npm met à jour la dernière version mineure, dans votre cas,2.4.1

Edit: Question de OP

Mais cela n'explique pas pourquoi "npm install" changerait le fichier de verrouillage. Le fichier de verrouillage n'est-il pas destiné à créer une version reproductible? Si tel est le cas, quelle que soit la valeur semver, il doit toujours utiliser la même version 2.1.6.

Réponse:

Ceci est destiné à verrouiller votre arbre de dépendance complet. Disons qu'il typescript v2.4.1faut widget ~v1.0.0. Lorsque vous installez npm, il prend widget v1.0.0. Plus tard, votre collègue développeur (ou build CI) fait une installation npm et obtient typescript v2.4.1mais widgeta été mis à jour vers widget v1.0.1. Maintenant, votre module de nœud n'est pas synchronisé. C'est ce qui package-lock.jsonempêche.

Ou plus généralement:

Par exemple, considérez

paquet A:

{"nom": "A", "version": "0.1.0", "dépendances": {"B": "<0.1.0"}}

paquet B:

{"nom": "B", "version": "0.0.1", "dépendances": {"C": "<0.1.0"}}

et package C:

{"nom": "C", "version": "0.0.1"}

S'il s'agit des seules versions de A, B et C disponibles dans le registre, une installation npm normale A s'installera:

[email protected] - [email protected] - [email protected]

Cependant, si [email protected] est publié, une nouvelle installation npm A s'installera:

[email protected] - [email protected] - [email protected] en supposant que la nouvelle version n'a pas modifié les dépendances de B. Bien sûr, la nouvelle version de B pourrait inclure une nouvelle version de C et un certain nombre de nouvelles dépendances. Si de tels changements ne sont pas souhaitables, l'auteur de A pourrait spécifier une dépendance à [email protected]. Cependant, si l'auteur de A et l'auteur de B ne sont pas la même personne, il n'y a aucun moyen pour l'auteur de A de dire qu'il ne veut pas insérer de nouvelles versions publiées de C lorsque B n'a pas changé du tout.


OP Question 2: Alors laissez-moi voir si je comprends bien. Ce que vous dites, c'est que le fichier de verrouillage spécifie les versions des dépendances secondaires, mais repose toujours sur la correspondance floue de package.json pour déterminer les dépendances de niveau supérieur. Est-ce exact?

Réponse: Non. Package-lock verrouille la totalité de l'arborescence des packages, y compris les packages racine décrits dans package.json. Si typescriptest verrouillé 2.4.1dans votre package-lock.json, il doit le rester jusqu'à ce qu'il soit modifié. Et disons que la typescriptversion de demain sortira 2.4.2. Si je vérifie votre branche et que je cours npm install, npm respectera le fichier de verrouillage et installera 2.4.1.

Plus sur package-lock.json:

package-lock.json est généré automatiquement pour toutes les opérations où npm modifie l'arborescence node_modules ou package.json. Il décrit l'arborescence exacte qui a été générée, de sorte que les installations suivantes peuvent générer des arborescences identiques, indépendamment des mises à jour de dépendance intermédiaires.

Ce fichier est destiné à être validé dans les référentiels sources et sert à diverses fins:

Décrivez une représentation unique d'une arborescence de dépendances telle que les coéquipiers, les déploiements et l'intégration continue sont garantis pour installer exactement les mêmes dépendances.

Fournir aux utilisateurs la possibilité de "voyager dans le temps" vers les états précédents de node_modules sans avoir à valider le répertoire lui-même.

Pour faciliter une meilleure visibilité des changements d'arbre grâce à des différences de contrôle de source lisibles.

Et optimisez le processus d'installation en permettant à npm d'ignorer les résolutions de métadonnées répétées pour les packages précédemment installés.

https://docs.npmjs.com/files/package-lock.json

Mat
la source
29
Mais cela n'explique pas pourquoi "npm install" changerait le fichier de verrouillage. Le fichier de verrouillage n'est-il pas destiné à créer une version reproductible? Si tel est le cas, quelle que soit la valeur semver, il doit toujours utiliser la même version 2.1.6.
Viper Bailey
3
Et c'est ce que je dis. Mon fichier de verrouillage de package indique [email protected] mais lorsque j'exécute l'installation de npm, l'entrée est remplacée par [email protected].
Viper Bailey
5
J'ai rencontré ce même problème. Dans notre CI / CD, le fichier est package-lock.jsontiré vers le bas puis nous exécutons npm install, mais le package-lock.jsonfichier est modifié et nous devons effectuer une réinitialisation avant de pouvoir tirer les modifications suivantes.
BayssMekanique
15
Je ne comprends pas. Comment est-ce un fichier "verrouillé" si les installations suivantes peuvent encore effectuer des mises à niveau?!
Ross Allen
5
Je pense qu'ils ont commencé avec l'idée d'avoir ce fichier comme "info" et "lock" et ont ensuite décidé que ce ne serait qu'un fichier "info". Un meilleur nom serait "package-info.json". J'aimerais avoir un "npm install -lock" qui s'installera à partir de "package-lock.json" et ignorera "package.json"
Jeremy Chone
2

Vous devriez probablement utiliser quelque chose comme ça

npm ci

Au lieu de l'utiliser npm install si vous ne souhaitez pas modifier la version de votre package.

Selon la documentation officielle, à la fois npm installet npm ciinstallez les dépendances nécessaires au projet.

La principale différence est, npm installinstalle les packages en prenant packge.jsoncomme référence. Dans le cas où npm ci, il installe les packages en prenant package-lock.jsoncomme référence, en s'assurant à chaque fois que le package exact est installé.

Sengottaian Karthik
la source
1

Il y a un problème ouvert pour cela sur leur page github: https://github.com/npm/npm/issues/18712

Ce problème est plus grave lorsque les développeurs utilisent différents systèmes d'exploitation.

hrdwdmrbl
la source
Les réécritures dans package-lock sont prévues, le problème n'est pas une conséquence de cela
Z. Khullah
0

EDIT: le nom "lock" est délicat, son NPM essayant de rattraper Yarn. Ce n'est pas un fichier verrouillé. package.jsonest un fichier fixe par l'utilisateur qui, une fois "installé", générera une arborescence de dossiers node_modules et cette arborescence sera alors écrite package-lock.json. Donc, vous voyez, c'est l'inverse - les versions de dépendance seront extraites package.jsoncomme d'habitude et package-lock.jsondevraient être appeléespackage-tree.json

(j'espère que cela a clarifié ma réponse, après tant de votes négatifs)


Une réponse simpliste: package.jsonayez vos dépendances comme d'habitude, alors que package-lock.jsonc'est "un arbre node_modules exact et plus reproductible" (tiré des documents npm lui-même ).

Quant au nom délicat, son NPM essaie de rattraper Yarn.

Z. Khullah
la source
1
Parce que si vous exécutez l'installation de npm, package-lock sera mis à jour.
Jean-Baptiste