Почему «npm install» переписывает package-lock.json?

614

Я только недавно обновился до npm @ 5 . Теперь у меня есть файл package-lock.json со всем из package.json . Я ожидаю, что при запуске npm installверсии зависимостей будут извлечены из файла блокировки, чтобы определить, что должно быть установлено в моем каталоге node_modules . Что странно, так это то, что он на самом деле модифицирует и переписывает мой файл package-lock.json .

Например, для файла блокировки была указана машинописная версия версии 2.1.6 . Затем после npm installкоманды версия была изменена на 2.4.1 . Это, кажется, побеждает всю цель файла блокировки.

Что мне не хватает? Как мне заставить npm действительно уважать мой файл блокировки?

Гадюка Бейли
источник
4
Это не отвечает на ваш вопрос, так что, надеюсь, комментарий в порядке, но взгляните на пряжу. Переключение заняло у нас меньше часа.
КаякинКодер
4
Та же проблема, но с использованием пряжи github.com/yarnpkg/yarn/issues/570 (очень поучительно)
Ив М.
2
У меня та же проблема. Мой package-lock.jsonвосстанавливается, когда я бегу npm install. Это пахнет как ошибка npm. Вы используете свой собственный реестр?
HaNdTriX
@YvesM. --no-saveпредотвращает изменение файла блокировки, но это не влияет на глупое обновление зависимостей первого уровня, которое упоминает OP.
Росс Аллен,

Ответы:

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. См. Эту документацию блокировок пакетов для дальнейшего объяснения.

jotaen
источник
43
Что за хак npm updateдля тогда? : o У меня было такое же чувство, что npm installобновленный deps, но я не хочу в это верить ... но кажется, что это печально, правда. В любом случае, есть еще возможность использовать npm shrinkwrapдля блокировки deps, но определенно имя package-lock неверно как это не замораживает, не блокирует зависимости ..
Jurosh
266
Какой беспорядок! Самый большой в мире менеджер пакетов, но у него нет документации о том, как он должен работать. Все гадают о том, что он должен делать, и это превращается в войну мнений. Обсуждение это хорошо, но должно произойти до выхода на волю. В какой-то момент кто-то должен сделать последний звонок, а затем он может быть реализован, задокументирован и выпущен. PHP был разработан комитетом и ad-hoc'd вместе и посмотрите, как это получилось. Я не хотел бы, чтобы то же самое происходило с таким критически важным и широко используемым инструментом.
Лэндон Поч
85
Тогда какой смысл использовать package-lock? Я думал, что это создаст одну и ту же среду в разных рабочих пространствах, но оказалось, что она ничего не делает
laltin
17
«Тогда комбинация package.json и package-lock.json даст воспроизводимые сборки». Какую роль здесь играет «package-lock.json»? Разве один «package.json» уже не дает воспроизводимых сборок, если не используются префиксы версий?
Янис Элмерис
12
@ JānisElmeris Я думаю, что package.json не может заблокировать глубокие зависимости ...
Хуан Мендес
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 только изменяет файл блокировки, если это необходимо, чтобы соответствовать спецификации в packages.json . Так что, если пакеты говорили thatpackage: 1и «блокировка» говорит ..: 1.0.4, dev может редактировать, чтобы сказать thatpackage: 2- и это заставит файл блокировки измениться, потому что 1.0.4он не совместим с новым указанным диапазоном. Если не изменить packages.json, останется заблокированным в точной версии, пока не удалите файл блокировки. [Если не будут заблокированы, и не изменял packages.json, отчет об ошибке.]
ToolmakerSteve
1
@ Джордж На основании информации, которую я прочитал (для последних версий npm), и моего ограниченного тестирования: да, обоим.
Venryx
95

Используйте недавно представленный

npm ci

npm ci обещает наибольшую пользу крупным командам. Предоставление разработчикам возможности «подписаться» на блокировку пакета способствует более эффективной совместной работе больших групп, а возможность установки именно того, что находится в файле блокировки, может сэкономить десятки, если не сотни часов разработчиков в месяц, что освобождает команды тратить больше времени на сборку и доставку удивительных вещей.

Представляем npm ciдля более быстрой и надежной сборки

Гал Маргалит
источник
3
мне кажется это правильно? кто-нибудь еще может подтвердить?
phouse512
6
@ phouse512 Это правильно. Мы в основном используем только и используем npm ciтолько npm installпри обновлении или установке новых пакетов.
Джейкоб Сиверс
1
Последние комментарии и т. Д. Это ответ, с которым я собираюсь. Жаль, что они не могут исправить ужасную снафу, но если новое Евангелие - «npm ci», тогда хорошо. Я могу адаптироваться.
Свен
Жаль, что он всегда удаляет существующий node_modulesкаталог и восстанавливает его локально, даже если это пустая, но важная символическая ссылка. :(
Джо Ацбергер,
2
@ToolmakerSteve Не задерживай дыхание! Я думаю, что удаление содержимого каталога будет медленнее, чем просто удаление каталога. Вам нужно будет перечислить содержимое и затем выполнить серию команд удаления, а не только одну команду удаления для O / S. С учетом проблем с производительностью, которые ранее были устранены при npm, и улучшения, использующего, npm ciя полагаю, они будут очень неохотно представлять все, что может снизить производительность для довольно необычного варианта использования. Возможно, вы захотите проверить pnpm.js.org, хотя он использует жесткие ссылки для уменьшения использования диска.
Caltor
64

Короткий ответ:

  • npm install уважает package-lock.json, только если он удовлетворяет требованиям package.json.
  • Если он не удовлетворяет этим требованиям, пакеты обновляются и блокировка пакетов перезаписывается.
  • Если вы предпочитаете не выполнять сборку, а не перезаписывать пакетную блокировку, когда это происходит, используйте npm ci.

Вот сценарий, который может объяснить вещи (проверено с помощью NPM 6.3.0)

Вы объявляете зависимость в package.json как:

"depA": "^1.0.0"

Затем вы делаете, npm installчто сгенерирует package-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 отслеживает последнее обновление package.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.jsonpackage.jsonpackage-lock
Ахмад
1
Если у вас уже есть модуль в node_modules, отвечающий требованиям package.json, то npm installон ничего не делает, независимо от package-lock.json. Мы должны явно обновлять пакеты, даже когда есть доступные обновления, которые соответствуют semver, указанному в package.json. По крайней мере, это был мой опыт в течение многих лет.
carlin.scott
1
@ToolmakerSteve Я также скептически относился к поведению @ carlin.scott, но я только что проверил его, и на самом деле он прав. Если версия в пределах node_modulesудовлетворяет диапазону package.json, и нет package-lock.jsonфайла, npm не будет обновлять модуль при работе npm install. Я думаю, это нормально, так как вы можете использовать npm update(или npm-checkдля последних) обновление зависимостей, и это происходит быстрее в случае, когда кто-то просто добавляет одну запись package.json, и не хочет, чтобы несвязанные пакеты обновляли себя до последней версии, которая удовлетворяет полу-версии. спектр.
Venryx
19

Используйте npm ciкоманду вместо npm install.

«ci» означает «непрерывная интеграция».

Он установит зависимости проекта на основе файла package-lock.json вместо зависимостей файла lenient package.json.

Он будет производить идентичные сборки для ваших товарищей по команде, и это также намного быстрее.

Вы можете прочитать больше об этом в этом блоге: https://blog.npmjs.org/post/171556855892/introduction-npm-ci-for-faster-more-reliable

Даниэль Тонон
источник
2
ciотносится к «непрерывной интеграции», как упоминалось в документации и сообщении в блоге, объявляющем о команде: blog.npmjs.org/post/171556855892/…
Джо Ацбергер,
Спасибо, Джо. Я обновил свой ответ с правильным именем и добавил ссылку на пост в блоге. 😊 (для тех, кто читает это, ранее я говорил, что это означает «чистая установка»)
Даниэль Тонон
«И это также намного быстрее» - он удалит 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/issues/18286 для отслеживания этой функции.

Тимоти Хигинботтом
источник
Я сомневаюсь в этом. Как, если зависимости различаются для разных операционных систем, как вы можете принудительно установить то, что не будет работать?
Евгений Афанасьев
4
@YevgeniyAfanasyev Вместо этого флага, он был реализован, npm ciчто также обрабатывает ваш вопрос.
spex
8

Кажется, эта проблема исправлена ​​в npm v5.4.2

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

(Прокрутите вниз до последнего комментария в теме)

Обновить

Фактически исправлено в 5.6.0. В 5.4.2 была кросс-платформенная ошибка, которая вызывала проблему.

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

Обновление 2

Смотрите мой ответ здесь: https://stackoverflow.com/a/53680257/1611058

npm ci это команда, которую вы должны использовать при установке существующих проектов сейчас.

Даниэль Тонон
источник
5
Я использую 5.4.2, и это все еще приводит к модификации моего package-lock.json, когда npm i. Например, модуль fseventsудаляется, когда я npm iна машине, которая не поддерживает, fseventsи затем модуль повторно добавляется, когда один npm iснова на машине, которая поддерживает.
hrdwdmrbl
Тогда вы должны поднять новую проблему в репозитории Gpm на npm, объясняя это. Если это не работает так, как они говорят, что это должно работать, то они считают это высокоприоритетной ошибкой, которую нужно срочно исправить.
Даниэль Тонон
@hrdwdmrbl Я вижу то же fseventsпадение, что package-lock.jsonи npm@5.5при работе с участниками Mac OS X. Если вы не открыли вопрос, я буду.
AL X 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) устанавливает npm и получает, typescript v2.4.1но widgetбыл обновлен до widget v1.0.1. Теперь ваш модуль узла не синхронизирован. Это то, что package-lock.jsonмешает.

Или в более общем плане:

В качестве примера рассмотрим

пакет А:

{"name": "A", "version": "0.1.0", "dependencies": {"B": "<0.1.0"}}

пакет B:

{"name": "B", "version": "0.0.1", "dependencies": {"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 установит:

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: Итак, давайте посмотрим, правильно ли я понимаю. Вы говорите, что файл блокировки определяет версии вторичных зависимостей, но все еще использует нечеткое соответствие package.json для определения зависимостей верхнего уровня. Это точно?

Ответ: Нет. Package-lock блокирует все дерево пакетов, включая корневые пакеты, описанные в 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

Matt
источник
29
Но это не объясняет, почему «npm install» изменит файл блокировки. Разве файл блокировки не предназначен для создания воспроизводимой сборки? Если это так, независимо от значения semver, он все равно должен использовать ту же версию 2.1.6.
Гадюка Бейли
3
И это то, что я говорю. Мой файл блокировки пакета говорит typescript@2.1.6, но когда я запускаю npm install, запись заменяется на typescript@2.4.1.
Гадюка Бейли
5
Я испытал эту же проблему. В нашем CI / CD происходит сброс, package-lock.jsonа затем мы запускаем npm install, но package-lock.jsonфайл изменяется, и мы должны выполнить сброс, прежде чем сможем извлечь следующие изменения.
BayssMekanique
15
Я не понимаю Как это файл «блокировки», если последующие установки могут все еще делать обновления ?!
Росс Аллен
5
Я думаю, что они начали с идеи иметь этот файл как «info» и «lock», а затем решили, что это будет только «info» файл. Лучшее имя будет "package-info.json". Я хотел бы иметь "npm install -lock", который установит из "package-lock.json" и проигнорирует "package.json"
Джереми Чон
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/issues/18712

Эта проблема наиболее серьезна, когда разработчики используют разные операционные системы.

hrdwdmrbl
источник
Предусмотрены переписки в package-lock, проблема не является следствием этого
Z. Khullah
0

РЕДАКТИРОВАТЬ: название «замок» хитрый, его NPM пытается догнать пряжи. Это не заблокированный файл вообще. package.jsonявляется фиксированным пользователем файлом, который после "установки" сгенерирует дерево папок node_modules, и это дерево будет записано в package-lock.json. Итак, вы видите, все наоборот - версии зависимостей будут извлечены package.jsonкак всегда, и package-lock.jsonих следует вызыватьpackage-tree.json

(надеюсь, это сделало мой ответ более ясным после стольких отрицательных голосов)


Упрощенный ответ: сохраняйте package.jsonсвои зависимости как обычно, в то время как package-lock.jsonэто «точное и, что более важно, воспроизводимое дерево node_modules» (взято из самой документации npm). ).

Что касается хитрого имени, его NPM пытается догнать пряжу.

З. Хулла
источник
1
Потому что, если вы запустите npm install, пакет-блокировка будет обновлена.
Жан-Батист