Чому “npm install” переписує package-lock.json?


612

Я нещодавно оновлений до npm @ 5 . Зараз у мене є файл package-lock.json із усім, що від package.json . Я б очікував, що коли я запускаю, npm installщо версії залежності будуть витягнуті з файлу блокування, щоб визначити, що слід встановити в моєму каталозі node_modules . Що дивного в тому, що він фактично закінчує зміну та перезапис мого файлу package-lock.json .

Наприклад, у файлі блокування був вказаний typecript, який має бути у версії 2.1.6 . Потім після npm installкоманди версію було змінено на 2.4.1 . Це, здається, перешкоджає цілі блокування файлу.

Що я пропускаю? Як мені отримати npm, щоб фактично поважати мій файл блокування?


4
Це не відповідає на ваше запитання, тому, сподіваємось, коментар у порядку, але подивіться на Пряжу. Перемикання нам зайняло менше години.
KayakinKoder

4
Ця ж проблема, але використання пряжі github.com/yarnpkg/yarn/isissue/570 (дуже повчально)
Ів М.

2
У мене те саме питання. Моє package-lock.jsonрегенерується, коли я бігаю npm install. Це пахне помилкою npm. Ви використовуєте власний реєстр?
HaNdTriX


@YvesM --no-saveперешкоджає зміні файлу блокування, але це не впливає на оновлення залежності рівня першого рівня, яке згадує ОП.
Росс Аллен

Відповіді:


423

Оновлення 3: Як вказують й інші відповіді, npm ciкоманда була введена в npm 5.7.0 як додатковий спосіб досягти швидких та відтворюваних збірок у контексті CI. Для отримання додаткової інформації див. Документацію та блог npm .


Оновлення 2: Проблема щодо оновлення та уточнення документації - це випуск GitHub №18103 .


Оновлення 1: Поведінка, описана нижче, зафіксована у npm 5.4.2: поточна цільова поведінка викладена у випуску GitHub №17979 .


Оригінальна відповідь: Поведінку package-lock.jsonбуло змінено у npm 5.1.0, як обговорювалось у випуску № 16866 . Поведінка, яку ви спостерігаєте, очевидно, призначена npm, починаючи з версії 5.1.0.

Це означає, що package.jsonможе змінюватись, package-lock.jsonколи буде знайдена нова версія для залежності package.json. Якщо ви хочете ефективно закріпити свої залежності, тепер ви повинні вказати версії без префікса, наприклад, їх потрібно записати як 1.2.0замість ~1.2.0або ^1.2.0. Тоді комбінація package.jsonта package-lock.jsonдасть відтворювані побудови . Щоб було зрозуміло: package-lock.jsonпоодинці більше не блокуються залежності кореневого рівня!

Чи було це рішення дизайну хорошим чи ні, можна сперечатися, триває дискусія, що виникає внаслідок цієї плутанини на GitHub у випуску №17979 . (На моїх очах це сумнівне рішення; принаймні ім’я lockвже не відповідає дійсності.)

Ще одна бічна примітка: існує також обмеження для реєстрів, які не підтримують незмінні пакети, наприклад, коли ви перетягуєте пакунки безпосередньо з GitHub замість npmjs.org. Для подальшого пояснення дивіться цю документацію блокування пакетів .


43
Для чого тоді хак npm update? : o У мене було таке ж відчуття, що npm installоновлені deps, але я не хочу в це вірити .. але, здається, це сумно правда .. У будь-якому випадку, все ще є варіант використання npm shrinkwrapдля блокування деп, але, безумовно, назва пакету-блокування неправильне як це не замерзає, не
блокується

266
Який безлад! Найбільший у світі менеджер пакунків, але він не має документації про те, як він повинен працювати. Усі здогадуються про те, що йому робити, і це перетворюється на війну думок. Обговорення хороший, але має відбуватися до виходу в дику природу. У якийсь момент комусь потрібно здійснити остаточний дзвінок, і тоді він може бути реалізований, задокументований та звільнений. PHP був розроблений комітетом і ad hoc'd разом і подивіться, як вийшло. Мені б не хотілося, щоб те саме сталося з інструментом, який є таким критичним і широко застосовується.
Ландон Поч

85
Тоді, який сенс використовувати пакет-замок? Я думав, що це створить однакове середовище в різних робочих просторах, але виявляється, що він нічого не робить
laltin

17
"Тоді комбінація package.json і package-lock.json дасть відтворювані збірки." Яку роль тут має "package-lock.json"? Чи не сам "package.json" вже не дає відтворюваних збірок, якщо не використовуються префікси версій?
Яніс Елмеріс

12
@ JānisElmeris Я думаю, що package.json не може закрити глибокі залежності ...
Juan Mendes

165

Я виявив, що з'явиться нова версія npm 5.7.1 з новою командою npm ci, яка буде встановлюватися package-lock.jsonтільки

Нова команда npm ci встановлюється ТІЛЬКИ з вашого файлу блокування. Якщо ваш package.json і ваш файл блокування не синхронізовані, він повідомить про помилку.

Це працює, викидаючи ваші node_modules і відтворюючи його з нуля.

Крім того, що ви гарантуєте, що ви отримаєте лише те, що є у вашому файлі блокування, він також набагато швидший (2x-10x!), Ніж установка npm, коли ви не починаєте з node_modules.

Як ви можете взяти з назви, ми очікуємо, що це буде великим благом для безперервних інтеграційних середовищ. Ми також очікуємо, що люди, які виробляють виробництво з тегів git, побачать значні успіхи.


133
Це має бути поведінка за замовчуванням, якщо існує файл блокування.
знесення нанівець

13
Тож вони змінили, як працює npm я, тільки щоб повернути це як npm ci місяці пізніше?
Скотт Флак

1
Я все ще плутаюсь. У документації написано "Переконайтеся, що у вас є блокування пакету та оновлена ​​установка: npm install" перед запуском команди npm ciв цьому проекті. Не npm installзамінює файл package-lock.json?
адига

1
AFAIK: @adiga - починаючи з версії 5.4, npm лише змінюючи файл блокування, якщо це необхідно, щоб відповідати специфікації в пакетах.json . Отже, якщо пакети, що раніше використовувались thatpackage: 1, і блокування говорить ..: 1.0.4, dev може редагувати, щоб сказати thatpackage: 2- і це змусить файл блокування змінитись, оскільки 1.0.4він не сумісний із нещодавно заданим діапазоном. Якщо це не зміниться packages.json, залишатиметься заблокованим у точній версії, поки не видалить файл блокування. [Якщо не будуть заблоковані, і залишався вірним packages.json, звіт про помилку.]
ToolmakerSteve

1
@George З інформації, яку я прочитав (для останніх версій npm), і моє обмежене тестування: так для обох.
Венрікс

95

Скористайтеся нещодавно представленим

npm ci

npm ci обіцяє найбільшу користь великим командам. Надання розробникам можливості "вийти" з блокування пакета сприяє більш ефективному співробітництву між великими командами, а можливість встановити саме те, що є у блокуванні файлів, може економити десятки, якщо не сотні годин розробника на місяць, звільняючи команди витратити більше часу на створення та доставку дивовижних речей.

Представляємо npm ciдля швидшого, надійнішого побудови


3
це здається мені правильним? хтось ще може підтвердити?
phouse512

6
@ phouse512 Це правильно. Ми досить багато тільки використовувати npm ci, і використовувати тільки npm installпри оновленні або установці нових пакетів.
Яків Сіверс

1
Останні коментарі тощо. Це відповідь, з якою я йду. Шкода, що вони не зможуть виправити жахливий сніфу, але якщо нове Євангеліє "npm ci", то добре. Я можу адаптуватися.
Svend

Шкода, що він завжди видаляє існуючий node_modulesкаталог та відновлює локально, навіть якщо це інакше порожнє, але важливе посилання. :(
Джо Атцбергер

2
@ToolmakerSteve Не затримуй дихання! Я думаю, що видалення вмісту каталогу буде на величину повільніше, ніж просто видалення каталогу. Вам потрібно буде перерахувати вміст, а потім видати серію команд видалення, а не лише одну команду видалення для O / S. З питаннями щодо продуктивності, які раніше були вирівняні на npm, та покращенням використання, npm ciя очікую, що вони будуть дуже неохоче впроваджувати все, що може знизити продуктивність для досить незвичного випадку використання. Ви можете перевірити pnpm.js.org, хоча для використання диска використовуються жорсткі посилання.
Калтор

64

Коротка відповідь:

  • npm install вшановує package-lock.json лише в тому випадку, якщо він відповідає вимогам package.json.
  • Якщо вони не відповідають цим вимогам, пакети оновлюються, а пакет блокування перезаписується.
  • Якщо ви скоріше провалили збірку, ніж перезаписуйте замок пакета, коли це станеться, використовуйте npm ci.

Ось сценарій, який може пояснити речі (перевірено NPM 6.3.0)

Ви оголошуєте залежність у package.json, як:

"depA": "^1.0.0"

Тоді ви робите, npm installщо генерує пакет-lock.json з:

"depA": "1.0.0"

Через кілька днів виходить нова, другорядна версія "depA", скажімо, "1.1.0", тоді справедливо наступне:

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)

Далі ви вручну оновите пакет package.json до:

"depA": "^1.1.0"

Потім повторіть:

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)

4
Це дійсно призначена поведінка файлу "замок". Мабуть, це було не так у старих версіях NPM.
Блоккост

1
Тоді як npm відстежує останнє оновлення пакета.json? Що відбувається при переміщенні пакета package.json і package-lock.json на інший комп'ютер? Як npm на новому комп’ютері знає, чи є package.lock оригінальним чи оновленим, щоб вирішити, чи потрібно оновити package-lock.json чи ні?
Лахіру Чандіма

3
@LahiruChandima Це дійсно не відстежує оновлення. npm installбуде використовувати заблоковані версії, package-lock.jsonякщо тільки вони не задовольняють, package.jsonу такому випадку він встановлює package.json і відповідно відновлює package-lock.json. Якщо ви змінили package.jsonтак, що існуючий замок пакета все ще задовольняє оновлений, package.jsonвін буде продовжувати використовувати цеpackage-lock
Ахмад Абдельгані

1
Якщо у вас вже є модуль у node_modules, який відповідає вимогам package.json, тоді npm installнічого не робиться, незалежно від package-lock.json. Ми повинні явно оновлювати пакети, навіть якщо є оновлення, які відповідають семеру, вказаному в package.json. Принаймні, це був мій досвід протягом багатьох років.
carlin.scott

1
@ToolmakerSteve Я також скептично ставився до поведінки @ carlin.scott, але я просто перевірив це, і він справді правильний. Якщо версія в межах node_modulesзадовольняє діапазон в package.json, і немає package-lock.jsonфайлу, npm не оновлює модуль під час роботи npm install. Я думаю, що це нормально, оскільки ви можете використовувати npm update(або npm-checkнайновіше) для оновлення залежностей, і така поведінка швидше для того, щоб хтось лише додав один запис до package.json, а не бажав, щоб не пов'язані пакети оновлювали себе до останнього, що задовольняє напівверсію дальність.
Венрікс

19

Використовуйте npm ciкоманду замість npm install.

"ci" означає "безперервну інтеграцію".

Він встановить проектні залежності на основі файлу package-lock.json замість м'яких залежностей файлу package.json.

Це створить однакові збірки для ваших товаришів по команді, і це також набагато швидше.

Детальніше про це ви можете прочитати у цьому дописі в блозі: https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable


2
ciйдеться про "безперервну інтеграцію", як згадується в документах та публікації блогу, що оголошує команду: blog.npmjs.org/post/171556855892/…
Джо Атцбергер

Дякую Джо. Я оновив свою відповідь правильною назвою та пов’язав із публікацією в блозі. 😊 (для тих, хто читає це, раніше я казав, що це означає "чиста установка")
Daniel

"І це також набагато швидше" - вона видалить node_modulesпапку і знову створить її з нуля. Це справді набагато швидше? Чи npm installвидаляє node_modulesпапку теж?
izogfif

Я думаю, що швидкість приходить від npm, не потрібно обчислювати, які пакунки потрібно завантажити. Подумайте про це, як npm installслід вирішити всі залежності пакету при запуску. npm ciце лише список покупок "отримати ці точні модулі".
Даніель Тонон

8

Надалі ви зможете використовувати --from-lock-file(або подібний) прапор для встановлення лише з package-lock.jsonне змінюючи його.

Це буде корисно для середовищ CI та ін., Де важливі відтворювані складання.

Див. Https://github.com/npm/npm/isissue/18286 для відстеження функції.


Я сумніваюся в цьому. Як, якщо залежності відрізняються для різних операційних систем, як можна змусити встановити щось, що не працює?
Євген Афанасьєв

4
@YevgeniyAfanasyev Замість цього прапора він був реалізований, як npm ciце також вирішує ваше запитання.
spex

8

Здається, ця проблема виправлена ​​в npm v5.4.2

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

(Прокрутіть до останнього коментаря в потоці)

Оновлення

Фактично зафіксовано в 5.6.0. У 5.4.2 сталася помилка між платформою, яка спричиняла проблему.

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

Оновлення 2

Дивіться мою відповідь тут: https://stackoverflow.com/a/53680257/1611058

npm ci це команда, яку ви повинні використовувати під час встановлення існуючих проектів зараз.


5
Я використовую 5.4.2, і це все ще призводить до зміни мого пакета-lock.json коли npm i. Наприклад, модуль fseventsвидаляється, коли я перебуваю npm iна машині, яка не підтримує, fseventsа потім модуль знову додається, коли він npm iзнову на машині, яка це робить.
hrdwdmrbl

Тоді вам слід підняти нове питання в npm GitHub repo, пояснюючи це. Якщо це не працює, як вони кажуть, що це повинно працювати, вони вважають помилку високого пріоритету, яка терміново потребує виправлення.
Даніель Тонон

@hrdwdmrbl я бачу те ж саме fseventsпадіння в моєму package-lock.jsonз npm@5.5, співпрацюючи з Mac OS X учасниками. Якщо ви не відкрили питання, я зроблю.
AL the X

@hrdwdmrbl Я виявив, що (і довга нитка пов'язаних проблем) після того, як я залишив коментар і забув повернутися до SO, щоб оновити свій коментар. Дякую за те, що я повернувся. Все добре.
AL X X

4

Напевно у вас є щось на кшталт:

"typescript":"~2.1.6"

у вашому, package.jsonякий npm оновлює останню незначну версію, у вашому випадку2.4.1

Редагувати: Запитання від ОП

Але це не пояснює, чому "npm install" змінив би файл блокування. Чи не зафіксований файл блокування для створення відтворюваної збірки? Якщо так, незалежно від значення semver, він все одно повинен використовувати ту саму версію 2.1.6.

Відповідь:

Це призначено для блокування вашого дерева повних залежностей. Скажімо, typescript v2.4.1вимагає widget ~v1.0.0. Коли ви npm встановлюєте, це хапає widget v1.0.0. Пізніше ваш інший розробник (або CI build) встановлює npm і отримує, typescript v2.4.1але widgetбув оновлений до widget v1.0.1. Тепер ваш модуль вузла не синхронізований. Саме це package-lock.jsonзаважає.

Або загалом:

Як приклад, розглянемо

пакет A:

{"name": "A", "version": "0.1.0", "залежності": {"B": "<0.1.0"}}

пакет B:

{"name": "B", "version": "0.0.1", "залежності": {"C": "<0.1.0"}}

і пакет C:

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

Якщо це єдині версії A, B і C, доступні в реєстрі, тоді встановиться звичайний npm для установки A:

A@0.1.0 - B@0.0.1 - C@0.0.1

Однак якщо B@0.0.2 опубліковано, то свіжа npm install A встановить:

A@0.1.0 - B@0.0.2 - C@0.0.1, якщо нова версія не змінила залежності B. Звичайно, нова версія B може включати нову версію C та будь-яку кількість нових залежностей. Якщо такі зміни є небажаними, автор A може вказати залежність від B@0.0.1. Однак якщо автор A і автор B не є однією і тією ж людиною, немає ніякого способу для автора A сказати, що він або вона не хоче втягувати в опубліковані версії C, коли B зовсім не змінився.


Питання 2 ОП: Тож дозвольте мені зрозуміти, чи правильно я розумію. Що ви говорите, це те, що файл блокування визначає версії вторинних залежностей, але все-таки покладається на нечітке відповідність пакета.json для визначення залежностей верхнього рівня. Це точно?

Відповідь: Ні. Пакет-блокування блокує все дерево пакетів, включаючи кореневі пакети, описані в package.json. Якщо ви typescriptзаблоковані 2.4.1у вашому package-lock.json, він повинен залишатися таким, доки він не зміниться. І скажемо, завтра typescriptвипускає версію 2.4.2. Якщо я перевіряю вашу філію та запускаю npm install, npm буде дотримуватися файлу блокування та встановити 2.4.1.

Більше на тему package-lock.json:

package-lock.json автоматично генерується для будь-яких операцій, де npm модифікує або дерево node_modules, або package.json. Він описує точне дерево, яке було сформовано таким чином, що наступні установки можуть генерувати однакові дерева, незалежно від проміжних оновлень залежності.

Цей файл призначений для введення в сховища джерел і служить різним цілям:

Опишіть одне представлення дерева залежностей таким чином, що колеги, розгортання та безперервна інтеграція гарантовано встановлюють однакові залежності.

Забезпечте можливість для користувачів "подорожувати в часі" до попередніх станів node_modules без необхідності виконувати сам каталог.

Для полегшення більшої видимості змін дерев через читабельний контроль джерела відрізняється.

І оптимізуйте процес встановлення, дозволяючи npm пропускати повторні розділення метаданих попередньо встановлених пакетів.

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


29
Але це не пояснює, чому "npm install" змінив би файл блокування. Чи не зафіксований файл блокування для створення відтворюваної збірки? Якщо так, незалежно від значення semver, він все одно повинен використовувати ту саму версію 2.1.6.
Viper Bailey

3
І це те, що я говорю. У моєму файлі блокування пакунків пише typecript@2.1.6, але коли я запускаю npm install, запис замінюється на typecript@2.4.1.
Viper Bailey

5
Я пережив це саме питання. У нашому CI / CD package-lock.jsonвитягується файл, після чого ми запускаємо npm install, але package-lock.jsonфайл модифікується, і ми повинні виконати скидання, перш ніж ми зможемо здійснити наступні зміни.
BayssMekanique

15
Я не розумію. Як це файл "блокування", якщо наступні встановлення все-таки можуть зробити оновлення ?!
Росс Аллен

5
Я думаю, що вони почали з ідеї мати цей файл як "info" та "lock", а потім вирішили, що це буде лише файл "info". Кращою назвою було б "пакет-info.json". Я хотів би мати "npm install -lock", який встановлюватиметься з "package-lock.json" та ігнорувати "package.json"
Jeremy Chone

2

Напевно, вам слід скористатися чимось подібним

npm ci

Замість використання, npm install якщо ви не хочете змінювати версію пакету.

Згідно офіційній документації, так npm installі npm ciвстановити залежність , які необхідні для реалізації проекту.

Основна відмінність полягає в тому, npm installчи встановлюють пакети, беручи за основу packge.json. У випадку npm ci, коли він встановлює пакети, беручи за основу package-lock.json, переконуючись, щоразу встановлюється точний пакет.


1

Про це відкрите питання на їхній сторінці github: https://github.com/npm/npm/isissue/18712

Ця проблема є найбільш гострою, коли розробники використовують різні операційні системи.


Переписування в пакетному блокуванні призначене, питання не є наслідком цього
З. Хулла

0

EDIT: назва "замок" є хитрою, її NPM намагається наздогнати пряжу. Це взагалі не заблокований файл. package.json- це фіксований користувачем файл, який, як тільки "встановлений", генерує дерево папок node_modules, і це дерево буде записано в package-lock.json. Отож, бачите, навпаки - версії залежності будуть витягнуті, package.jsonяк завжди, і їх package-lock.jsonслід називатиpackage-tree.json

(сподіваюся, що це зробило мою відповідь яснішою після стількох зворотних подій)


Спрощена відповідь: package.jsonмайте свої залежності як завжди, тоді як package-lock.jsonце "точне і, що ще важливіше, відтворюване дерево node_modules" (взято з самих ncs docs ).

Що стосується хитрої назви, її НПМ намагається наздогнати Пряжу.


1
Тому що якщо ви запустите npm install, блокування пакунків буде оновлено.
Жан-Батист
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.