Comments 33
Все это от того, что npm не создает lock-файл, как это делает bundler/carton/composer, файл который создавался бы после выполнения «nmp install» и фиксировал бы точные версии всех модулей, которые нужны(даже косвенно) для нашего приложения. Вот lock-файл и нужно добавлять в git.
Не верно, в package.json вполне можно фиксировать версии, и npm install --save так и делает, суть в другом, т.к. локальные модули имеют приоритет перед глобальными, наличие кода модулей в репе, гарантирует то, что они будут использоваться.
А lock для _всех_ модулей создать в npm нельзя, так как они имеют иерархическую структуру, и каждая зависимость имеет свой package.json на содержимое которого мы влиять не можем.
Почитайте внимательнее, все очень подробно расписано, в том числе и вопрос фиксации версий.
А lock для _всех_ модулей создать в npm нельзя, так как они имеют иерархическую структуру, и каждая зависимость имеет свой package.json на содержимое которого мы влиять не можем.
Почитайте внимательнее, все очень подробно расписано, в том числе и вопрос фиксации версий.
Нельзя только потому что это не предусмотрено. То есть приняли решение не фиксировать.
А как вы вообще представляете реализацию? Хранить версии для всего дерева? типа:
Ну может быть это и реализуется технически, но зачем? Ведь можно просто добавить в реп node_modules и не иметь проблем.
lock был актуален для плоской схемы, в которой нельзя было иметь 2 версии одной и той же библиотеки, а глобальные модули имели приоритет. С npm это проблемы просто нет.
{
'brfs': '0.0.5',
'brfs/through': '2.2.7',
'browserify': '2.22.0',
'browserify/through': '2.3.4',
'foo/bar/buzz': '123'
}
Ну может быть это и реализуется технически, но зачем? Ведь можно просто добавить в реп node_modules и не иметь проблем.
lock был актуален для плоской схемы, в которой нельзя было иметь 2 версии одной и той же библиотеки, а глобальные модули имели приоритет. С npm это проблемы просто нет.
Ну вот открыл тестовый проектик: в composer.json 1 пакет (phpunit/phpunit), в composr.lock — кроме него ещё 7-8 его зависимостей с зафиксированными версиями, при том что в composer.json версия phunit/phpunit указана достаточно свободно.
У композера не иерархическая структура модулей, в нем нельзя иметь две версии одной библиотеки в проекте, иерархическая система модулей в npm дает такую возможность.
Модуль A может зависеть от модуля C v1.7, а модуль B от C v2.0.11 при этом оба модуля А и В бесконфликтно установятся в проект и подтянут свои зависимости локально.
В моем комменте выше акцент был на том, что мы имеем 2 различные версии библиотеки through. Это кстати пример из реального проекта, с актуальными версиями.
Но суть даже не в том, что мы можем лочить или нет версии зависимых библиотек, суть в том, что этого вообще не требуется, т.к. можно безбоязненно коммитить node_modules в реп и быть уверенным в том, что проект получит одни и те же библиотеки на всех инсталляциях.
Модуль A может зависеть от модуля C v1.7, а модуль B от C v2.0.11 при этом оба модуля А и В бесконфликтно установятся в проект и подтянут свои зависимости локально.
В моем комменте выше акцент был на том, что мы имеем 2 различные версии библиотеки through. Это кстати пример из реального проекта, с актуальными версиями.
Но суть даже не в том, что мы можем лочить или нет версии зависимых библиотек, суть в том, что этого вообще не требуется, т.к. можно безбоязненно коммитить node_modules в реп и быть уверенным в том, что проект получит одни и те же библиотеки на всех инсталляциях.
Отчасти пакетные менеджеры для того и создавались, чтобы не таскать с проектом все его зависимости. Не то чтобы одно или другое однозначно плохо или однозначно хорошо, но лучше иметь альтернативу, чем не иметь. Не согласны?
В том же Bundler, который и был предтечей всех современных систем контроля зависимостей, Gemfile.lock именно и предназначен для полной фиксации всех зависимостей приложений, на всю глубину дерева зависимостей.
Типичный пример:
Из
Получаем
Типичный пример:
Из
source "https://rubygems.org"
group :jekyll do
gem "jekyll"
gem "json"
gem "nokogiri"
gem "redcarpet"
gem "textpow", git: 'https://github.com/regru/textpow.git'
gem "ultraviolet", git: 'https://github.com/regru/ultraviolet.git'
end
Получаем
➜ bem-guide git:(master) cat Gemfile.lock GIT remote: https://github.com/regru/textpow.git revision: ff81fdebcc72baeed82d80dffd09a58d6aa9802f specs: textpow (1.3.1) plist (>= 3.0.1) GIT remote: https://github.com/regru/ultraviolet.git revision: a507f9e422b4b870d3437751b8d2f054165e97de specs: ultraviolet (1.0.1) GEM remote: https://rubygems.org/ specs: classifier (1.3.3) fast-stemmer (>= 1.0.0) colorator (0.1) commander (4.1.3) highline (~> 1.6.11) directory_watcher (1.4.1) fast-stemmer (1.0.2) highline (1.6.19) jekyll (1.0.3) classifier (~> 1.3) colorator (~> 0.1) commander (~> 4.1.3) directory_watcher (~> 1.4.1) kramdown (~> 1.0.2) liquid (~> 2.3) maruku (~> 0.5) pygments.rb (~> 0.5.0) safe_yaml (~> 0.7.0) json (1.8.0) kramdown (1.0.2) liquid (2.5.0) maruku (0.6.1) syntax (>= 1.0.0) mini_portile (0.5.0) nokogiri (1.6.0) mini_portile (~> 0.5.0) plist (3.1.0) posix-spawn (0.3.6) pygments.rb (0.5.1) posix-spawn (~> 0.3.6) yajl-ruby (~> 1.1.0) redcarpet (2.3.0) safe_yaml (0.7.1) syntax (1.0.0) yajl-ruby (1.1.0) PLATFORMS ruby DEPENDENCIES jekyll json nokogiri redcarpet textpow! ultraviolet!
Bundler позволяет иметь в одном окружении 2 версии одной и той же библиотеки?
Нет.
То есть там фактически и нет дерева зависимостей. Список библиотек — плоский, по одной штуке каждой версии. А если требуется поставить 2 зависимости, которые в свою очередь зависят от одной библиотеки, но разных версий, получим конфликты. npm принципиально отличается в этом плане, проблема в том, что люди тащат свои старые привычки в новую среду, не понимая особенностей инструмента, с которым работают. lock был нужен, так как не было гарантии, что приложение будет использовать код из репозитория в новой среде, даже если мы закоммитим зависимости. Теперь он просто не нужен, хотя возможность его сделать есть, как указали в комментах npm shrinkwrap. Но в любом случае, необходимость отпала, нужно просто переступить через свои старые привычки :)
UFO just landed and posted this here
Хм:
$ npm shrinkwrap
npm WARN package.json dateformat@1.0.2-1.2.3 No repository field.
npm WARN package.json vows@0.7.0 No repository field.
npm WARN package.json growl@1.7.0 No repository field.
npm WARN package.json ms@0.3.0 No repository field.
npm WARN package.json eyes@0.1.8 No repository field.
wrote npm-shrinkwrap.json
$ cat npm-shrinkwrap.json
{
"name": "kantaina",
"version": "0.1.5",
"dependencies": {
"lodash": {
"version": "1.2.1",
"from": "lodash@~1.2"
},
"when": {
"version": "2.1.1",
"from": "when@~2.1"
},
"dep-graph": {
"version": "1.1.0",
"from": "dep-graph@~1.1",
"dependencies": {
"underscore": {
"version": "1.2.1",
"from": "underscore@1.2.1"
}
}
},
"chai": {
"version": "1.6.1",
"from": "chai@~1.6"
},
"chai-as-promised": {
"version": "3.3.1",
"from": "chai-as-promised@~3.3"
},
"sinon-chai": {
"version": "2.4.0",
"from": "sinon-chai@~2.4"
},
"sinon": {
"version": "1.7.3",
"from": "sinon@~1.7",
"dependencies": {
"buster-format": {
"version": "0.5.5",
"from": "buster-format@~0.5",
"dependencies": {
"buster-core": {
"version": "0.6.4",
"from": "buster-core@>=0.6.2"
}
}
}
}
},
...
Ухты, не знал :)
Но тем не менее, в описании shrinkwrap есть:
Но тем не менее, в описании shrinkwrap есть:
If you wish to lock down the specific bytes included in a package, for example to have 100% confidence in being able to reproduce a deployment or build, then you ought to check your dependencies into source control, or pursue some other mechanism that can verify contents rather than versions.
npm shrinkwrap уже посоветовали, и это хорошо. Однако не защищает от ситуации, когда мейнтейнер случайно или по злому умыслу публикует новую версию под тем же номером. И у вас что-то ломается.
Мы решили эту проблему так — вместе с проектом идёт небольшой кеширующий веб-сервер, который подменяет собой официальный NPM-репозитарий, и кэширует все версии всех пакетов, которые загружаются во время npm install. Кэш потом чекинится в репозиторий нашего проекта, и мы имеем железобетонную гарантию, что ни один байт зависимых пакетов не поменяется.
Если хотим сделать апгрейд, частично или полностью сносим кэш и делаем npm install заново. Кэш наполняется актуальными версиями пакетов.
Мы решили эту проблему так — вместе с проектом идёт небольшой кеширующий веб-сервер, который подменяет собой официальный NPM-репозитарий, и кэширует все версии всех пакетов, которые загружаются во время npm install. Кэш потом чекинится в репозиторий нашего проекта, и мы имеем железобетонную гарантию, что ни один байт зависимых пакетов не поменяется.
Если хотим сделать апгрейд, частично или полностью сносим кэш и делаем npm install заново. Кэш наполняется актуальными версиями пакетов.
А в чем причина отказа от хранения node_modules в репе?
Как минимум, потому что там встречаются бинарные модули.
Мне кажется, это не проблема. npm rebuild при деплое их соберёт. Я понимаю, что процесс деплоя уже может быть давно отлажен, хорошо работает и нет смысла что-то менять. Просто ищу аргументы против node_modules в репе. Пока внятных не нашел :)
Теоретически, build-скрипт может что-то поменять в сорцах, и эти изменения будут попадать в репозиторий при последующих коммитах. Практически, может такого никогда и не встретится. Я не стал рисковать в этом плане.
Хм… Не сталкивался с такими build-скриптами, и имею привычку смотреть git status и git diff перед коммитом. Может быть какой-то смысл в этом есть, но тут уже ответственность полностью на разработчике, и всегда есть возможность откатиться. Ну а от всего не застрахуешься, по-пьяни можно и rm -rf /* на сервере набрать.
UFO just landed and posted this here
Насколько я могу судить, на сáмом-то деле всё именно так и устроено.
Автор пакета не может опубликовать новую версию под старым номером совершенно случайно, потому что никто не наберёт случайно«npm publish --force» вместо простого «npm publish». (Это была бы не такая случайность, когда подбрасываешь монету и выпадает решка — а такая случайность, когда подбрасываешь монетку, а выпадает полуторатонный вилочный штабелеукладчик.) Если автор пакета вдруг команду «npm publish --force» набрал и запустил, то можно быть совершенно уверенным в том, что именно это он и имел в виду, то есть руководствовался некоторыми соображениями — скорее всего, совершенно разумными.
Поэтому вызывает понятное удивление то обстоятельство, что Роджерс сперва пишет «Как сообщество, мы должны распределять между собой часть интеграционного тестирования», а затем теми же руками сомневается в том, что автор пакета знает, что он делает. И в том же aml сомневается.
Впрочем, надо признаться, что в известной мере я сужу здесь по себе. Последний разя «npm publish --force» запускал буквально сегодня утром — и хорошо знаю о себе, что у меня была на то веская причина: я изменил текст README, а больше ничего. И так как кто угодно (и когда угодно) может зайти на Гитхаб и прочесть там свежайший текст README, то не великá беда, если у кого-то в пакете README останется старым из-за того, что «npm update имяМодуля» не сработает без роста номера версии. С другой стороны, новым потребителям пакета (которые только-только ещё «npm install имяМодуля» набирают) неплохо бы иметь новую версию README в нём. Так что в этаком случае уместно подменить последнюю версию пакета, а не выпускать новую версию, которая только реестр npm засоряет.
Думаю, может быть наивно, что и у остальных авторов пакетовкакие-нибудь подобные уважительные соображения имеются.
Автор пакета не может опубликовать новую версию под старым номером совершенно случайно, потому что никто не наберёт случайно
Поэтому вызывает понятное удивление то обстоятельство, что Роджерс сперва пишет «Как сообщество, мы должны распределять между собой часть интеграционного тестирования», а затем теми же руками сомневается в том, что автор пакета знает, что он делает. И в том же aml сомневается.
Впрочем, надо признаться, что в известной мере я сужу здесь по себе. Последний раз
Думаю, может быть наивно, что и у остальных авторов пакетов
А расскажите, пожалуйста, пример из жизни, когда в одном проекте было необходимо наличие двух конфликтующих версий библиотек?
Пакет A зависит от пакета B версии 1.
Пакет C тоже зависит от пакета B, но версии 2.
Пакет C тоже зависит от пакета B, но версии 2.
Не проверял их на конфликтность но на моем текущем проекте есть по 3 версии commander, methods и mkdirp, и по 2 версии bson, cookie, esprima, formidable, lru-cache, mime, minimatch, mongodb, pause, pkginfo, sigmund, sliced, uglify-js, underscore. Такое происходит, когда разные библиотеки, которые ты зарекваирил тянут за собой в зависимостях разные версии одной и той же библиотеки. Ситуация вполне типичная для большого проекта.
Одна из рекомендаций для разработиков — как можно более явно указывать версию зависимости, несмотря на возможность указать любой диапазон или вообще "*" в значении «любая последняя версия». npm install --save тоже дописывает конкретную версию пакета. В результате, отслеживание и обновление зависимостей ложится на маинтейнеров пакетов. Если вы посмотрите, к примеру, дерево зависимостей cloud9 IDE — обязательно наткнетесь на несколько разных версий. Для отслеживания обновления зависимостей удобно пользоваться gemnasium.com — он присылает письмо, если какая-то из зависимостей обновлена.
Мне хочется подметить, и подмечу, что за полтора года, прошедших со времени публикации Роджерсом этой блогозаписи, я так и не мог совершенно понять, возобладала ли его точка зрения или не особенно возобладала.
А всё потому, что за это время я повидалмного-много репозиториев с кодами модулей и библиотек для Node (которые массово «node_modules» добавляют у себя в .gitignore да в .npmignore, но которым это и Роджерс рекомендует). Зато повидал очень мало репозиториев с кодами приложений и сайтов. (Может быть, и ни одного не повидал такого.)
Это у совета Роджерса невеликá аудитория, или это у меня невелик кругозор?
Может ли кто-нибудь из тутошних читателей назвать с полдесятка (а не то и с десяток) наиболее популярных приложений и сайтов с открытым исходным кодом для Node — чтобы можно было посмотреть в их репозиториях списки игнорируемых файлов да сделать вывод о том, насколько Роджерс был услышан, насколько методика его пошла в массы?
А всё потому, что за это время я повидал
Это у совета Роджерса невеликá аудитория, или это у меня невелик кругозор?
Может ли кто-нибудь из тутошних читателей назвать с полдесятка (а не то и с десяток) наиболее популярных приложений и сайтов с открытым исходным кодом для Node — чтобы можно было посмотреть в их репозиториях списки игнорируемых файлов да сделать вывод о том, насколько Роджерс был услышан, насколько методика его пошла в массы?
Я совсем никакой не разработчик, но мне кажется, это как-то неправильно, помещать в репозиторий с кодом проекта на может пару сотен, ну тысяч строк динамично обновляющуюся папку, которая может содержать десятки тысяч изменяющегося кода. С каждым коммитом помимо истории непосредственно изменений проекта будут кучи diff'ов файлов, до которых по сути нет никакого дела.
Если и хранить где-нибудь node_modules, то в отдельном репозитории, связанном субмодулем с основным. Но всё равно как-то не особо правильно. Было бы здорово, если бы был .lock файл, что выше говорили. Чтобы можно было написать npm lock, оно вернуло json с текущими зависимостями с конкретными версиями (которые сейчас локально), а потом или npm install менял своё поведение при наличии такого файла (при этом ругаясь, если он расходится с зависимостями в package.json), или отдельная команда, которая бы подтягивала конкретные зависимости. Хотя вариант с install лучше, мне кажется. Всякие там heroku бы правильно подтягивали бы зависимости, без каких-либо модификаций.
Если и хранить где-нибудь node_modules, то в отдельном репозитории, связанном субмодулем с основным. Но всё равно как-то не особо правильно. Было бы здорово, если бы был .lock файл, что выше говорили. Чтобы можно было написать npm lock, оно вернуло json с текущими зависимостями с конкретными версиями (которые сейчас локально), а потом или npm install менял своё поведение при наличии такого файла (при этом ругаясь, если он расходится с зависимостями в package.json), или отдельная команда, которая бы подтягивала конкретные зависимости. Хотя вариант с install лучше, мне кажется. Всякие там heroku бы правильно подтягивали бы зависимости, без каких-либо модификаций.
Хранить всё в репозитории это конечно хорошо. Потому что деплоить мы будем ровно то, что тестировали. И при этом не зависим от всяких сторонних тормозных сервисов типа npm.org. Более того, по аналогии с heroku в репозиторий стоит заливать и образ системы на которой всё это будет крутиться. Ой, как-то дофига весить оно будет ;-)
На самом деле всё проще: фиксируем версии или нет — не важно. деплоим на тестовый сервер, прогоняем тесты, если всё хорошо — клонируем тестовый сервер на боевой. не надо на каждом продакшен сервере запускать по деплою с его выкачиванием исходников, выкачиванием модулей, сборкой скриптов/стилей/спрайтов, компиляцией в байт-код… недавно ж даже битторрент выпустил приблуду для п2п синхронизации директорий.
а вот насчёт «офигенной фичи» иметь несколько модулей разных версий — тут я бы поспорил. Что есть в новой версии какого-то модуля пофиксили дыру в безопасности? Или существенно увеличили скорость? Да и вообще, иметь несколько версий одного модуля — лишнее потребление памяти. особенно, если модуль держит внутри себя кэш. Нафиг такое счастье. Как разработчик приложения я хочу чтобы все библиотеки были последней версии. И было бы классно, чтобы разработчики библиотек тоже шли в ногу со временем. А если не поспевают — им помогут те, кому больше всех надо. А поощрять использование устаревших версий — это плохо. В конце концом, если вылезла какая-то совсем большая несовместимость, которую самому не поправить — ну что ж, не будем обновлять этот злополучный модуль (а остальные будем) или найдём ему замену.
На самом деле всё проще: фиксируем версии или нет — не важно. деплоим на тестовый сервер, прогоняем тесты, если всё хорошо — клонируем тестовый сервер на боевой. не надо на каждом продакшен сервере запускать по деплою с его выкачиванием исходников, выкачиванием модулей, сборкой скриптов/стилей/спрайтов, компиляцией в байт-код… недавно ж даже битторрент выпустил приблуду для п2п синхронизации директорий.
а вот насчёт «офигенной фичи» иметь несколько модулей разных версий — тут я бы поспорил. Что есть в новой версии какого-то модуля пофиксили дыру в безопасности? Или существенно увеличили скорость? Да и вообще, иметь несколько версий одного модуля — лишнее потребление памяти. особенно, если модуль держит внутри себя кэш. Нафиг такое счастье. Как разработчик приложения я хочу чтобы все библиотеки были последней версии. И было бы классно, чтобы разработчики библиотек тоже шли в ногу со временем. А если не поспевают — им помогут те, кому больше всех надо. А поощрять использование устаревших версий — это плохо. В конце концом, если вылезла какая-то совсем большая несовместимость, которую самому не поправить — ну что ж, не будем обновлять этот злополучный модуль (а остальные будем) или найдём ему замену.
Sign up to leave a comment.
Особенности npm и хранение node_modules в git