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

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

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

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

Ответ 1

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

Ответ 2

Я обнаружил, что будет новая версия npm 5.7.1 с новой командой npm ci, которая будет устанавливаться только с package-lock.json

Новая команда npm ci устанавливается только из вашего файла lock-file. Если ваш пакет.json и ваш файл блокировки не синхронизированы, он сообщит об ошибке.

Он работает, отбрасывая ваши node_modules и воссоздавая его с нуля.

Помимо гарантии того, что вы получите только то, что находится в вашем файле блокировки, оно намного быстрее (2x-10x!), Чем установка npm, когда вы не начинаете с node_modules.

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

Ответ 3

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

npm ci

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

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

Ответ 4

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

  • Когда существует package-lock.json, он отменяет package.json
  • Когда package.json изменен, он отменяет package-lock.json

Вот сценарий, который может объяснить вещи (проверено с помощью 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)

Ответ 5

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

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

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

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

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

Ответ 6

Похоже, эта проблема исправлена в 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

Смотрите мой ответ здесь: fooobar.com/questions/62973/...

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

Ответ 7

У вас, вероятно, есть что-то вроде:

"typescript":"~2.1.6"

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

Изменить: вопрос из OP

Но это не объясняет, почему "npm install" изменит файл блокировки. Разве файл блокировки не предназначен для создания воспроизводимой сборки? Если так, независимо от значения semver, он все равно должен использовать тот же 2.1.6 версия.

Ответ:

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

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

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

пакет A:

{ "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 install A установит:

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

Однако, если [email protected] публикуется, то новая версия npm install A будет устанавливать:

[email protected] - [email protected]     - [email protected], предполагая, что новая версия не изменила зависимости B. Конечно, новая версия B может включать новую версию C и любое количество новых зависимостей. Если такие изменения нежелательно, автор A может указать зависимость от [email protected] Однако, если автор и автор B не являются одним и тем же лицом, нет возможности для автора сказать, что он или она не хочет втягивать недавно опубликованные версии C, когда B вообще не изменился.


OP Вопрос 2. Итак, дайте мне понять, правильно ли я понимаю. Что ты есть говоря, что файл блокировки указывает версии вторичного зависимостей, но все же полагается на нечеткое сопоставление package.json для определения зависимостей верхнего уровня. Это верно?

Ответ: Нет. Блокировка пакетов блокирует все дерево пакетов, включая корневые пакеты, описанные в package.json. Если typescript заблокировано в 2.4.1 в package-lock.json, он должен оставаться таким, пока не будет изменилось. И пусть завтра typescript выпускает версию 2.4.2. Если я проверю вашу ветку и запустил npm install, npm будет уважать lockfile и установить 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

Ответ 8

В будущем вы сможете использовать флаг --from-lock-file (или аналогичный) для установки только из package-lock.json без его изменения.

Это будет полезно для CI и т.д. Сред, где важны воспроизводимые сборки.

См. Https://github.com/npm/npm/issues/18286 для отслеживания этой функции.

Ответ 9

Существует открытая проблема для этого на странице github: https://github.com/npm/npm/issues/18712

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

Ответ 10

EDIT: имя "lock" является сложным, его NPM пытается догнать пряжу. Это не заблокированный файл. package.json - это файл с фиксированным пользователем, который после "установки" будет генерировать дерево папок node_modules, и тогда это дерево будет записано в package-lock.json. Итак, вы видите, что все наоборот - версии зависимостей будут вытаскиваться из package.json как всегда, и package-lock.json следует называть package-tree.json

(надеюсь, это сделало мой ответ яснее, после стольких downvotes)


Упрощенный ответ: package.json имеет ваши зависимости, как обычно, в то время как package-lock.json является "точным и, что важнее воспроизводимым деревом node_modules" (взято из самого файла npm).

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

Ответ 11

Это, похоже, также случается с npm6 - я запускал npm i ничего не меняя, и мой package-lock.json был изменен (версии под всеми пакетами requires изменения).

Кажется, предназначено и ничего не сломать? Больше информации здесь