• безопасность npm Yarn marakgate А теперь про воркфлоу без локфайла, но сначала про ворфлоу с локфайлом.

    Что такое локфайл? Это такой огромный json, в котором зафиксированы все зависимости данного пакета. Локфайл имеет одну особенность — пакетный менеджер (будь то npm или yarn) считывает его только для корневого пакета. Но не для зависимостей. «Но почему?!» — спросите вы.

    Причина такого (правильного) поведения в том, что, если бы учитывался каждый локфайл каждой библиотеки, то, поскольку в них бы для одних и тех же зависимостей разных библиотек были бы вторичные библиотеки разных версий, пришлось бы устанавливать огромное число дубликатов этих библиотек. Непонятно? Вот пример:

    — Мы пишем приложение, пакет называется application.
    — application зависит от библиотеки, скажем, виджета для отрисовки таблиц tables@0.1.0.
    — tables@0.1.0 зависит от маленькой библиотеки, скажем, leftpad@0.2.0
    — но application, кроме этого зависит ещё от одной библиотеки: виджет отрисовки графиков charts@0.1.0
    — а charts@0.1.0, в свою очередь, зависит от leftpad@0.2.1 (обратите внимание, версия другая)

    Что мы имеем в реальной жизни? В реальной жизни при установке зависимостей нашего application, создастся локфайл, в котором пропишется leftpad@0.2.1. Почему так происходит? Потому что и charts, и tables, во-первых зависят от leftpad мягко, то есть вот так: "leftpad": "^0.2.0" и "leftpad": "^0.2.1". Это даёт пакетному менеджеру определённую свободу выбора компромиссной версии, каковой в данном случае будет leftpad@0.2.1.

    А теперь представьте, что было бы, если бы у пакетов charts и tables были бы локфайлы, в которых была бы жёстко зафиксирована зависимость на leftpad двух разных версий (при условии, что эти локфайлы бы принимались всерьёз пакетным менеджером)? В итоговый локфайл приложения попали бы ОБЕ версии. И leftpad@0.2.0 установился бы в node_modules/tables/node_modules, а leftpad@0.2.1 установился бы в node_modules/charts/node_modules. Произошло бы задвоение пакета leftpad. А в реальном проекте таких пакетов были бы сотни, если не тысячи!

    Вывод №1. В библиотеках не работает ни package-lock.json, ни yarn.lock, и это так задумано. Кроме того, иметь нефиксированные номера версий зависимостей — правильно. Это позволяет пакетному менеджеру устанавливать пакеты экономно, без дубликатов.

    Продолжение в комментариях.
    ♡ recommended by @BradleyManning

Replies (9)

  • @janPona, Сместим на время фокус с разработчика приложения application на разработчика библиотеки tables.

    В его библиотеке скорее всего есть локфайл. «Но зачем?!» — спросите вы. А вот зачем: у него есть инструменты сборки, тестирования и прочая обвязка, которую хорошо бы ставить с локфайлом, потому что это, во-первых, быстрее работает, а во-вторых, даёт более предсказуемые результаты. То есть, с точки зрения разработчика библиотеки, его библиотека — это своего рода "приложение" (приложение для тестирования и сборки библиотеки), а приложению локфайлы нужны.

    И в локфайле библиотеки tables прописан leftpad версии 0.2.0, а не более поздний 0.2.1, потому что, несмотря на мягкую зависимость ^0.2.0, которая и могла бы отрезолвиться на 0.2.1, на момент создания локфайла свежий leftpad был версии 0.2.0.

    И CI/CD каждый раз, когда тестирует, устанавливает версию leftpad@0.2.0. А версия не сломана, в ней нет ни трояна, ни политических призывов обиженного разработчика. Поэтому тесты библиотеки tables проходят на ура, она релизится и с точки зрения её мейнтейнера все хорошо.

    А потом кто-то (возвращаемся к разработчику приложения application) инсталлирует tables, оно тянет за собой leftpad, но уже более поздней версии (0.2.1), leftpad ломает tables, tables ломает application. И хорошо ещё если ломает явно и громко (падают тесты, все в панике, приходит синьор и откатывает версию leftpad в локфайле). Но оно ж может и по-тихому начать гадить. Например, персональные данные собирать итд.

    А теперь поставьте себя на место разработчика application. Он решил завести в проект новую либу — tables. Её он знает хорошо, она одобрена коммьюнити, 100к звёзд на гитхабе. Он ознакомился с тестами версии 0.1.0, и даже, может быть, бегло просмотрел исходники. Но он не отвечает за транзитивные (то есть, вторичные) зависимости — за тот же leftpad, который он лично не ставит, поскольку leftpad — это зависимость tables, а не application. Точнее, он может озадачиться и этим, но ведь там же не одна зависимость, а огромнейшее дерево! Тысячи пакетов, без шуток!

    Вывод 2. Локфайл не гарантирует ничего кроме одного: «все зависимости и всё дерево транзитивных зависимостей всегда будет иметь точно такие же версии, какими они были на момент создания локфайла». Он не даёт никаких гарантий, что библиотека, чья стабильность доказана тестами мейнтейнера библиотеки, будет так же стабильно работать в другом проекте! Вообще.
  • @janPona, «Что же делать?», спросите вы.

    1. Пользоваться npm audit. Я лично не пользовался никогда, поэтому ничего посоветовать не могу.
    2. Пользоваться dependabot. Тщательно просматривать его пулл-реквесты и принимать их.
    3. Разумеется, писать тесты, следить за coverage.
    4. Не запускать процессы из-под node на той же машине (в том же контейнере), где лежат не относящиеся dev-окружению данного проекта секреты. Это ОЧЕНЬ опасно. Если это ваша домашняя/рабочая тачка, то там ваши куки, авторизации к AWS, NPM, секретные .ssh-ключи и прочая вкуснота. На этой машине не должно быть такого бинарника — node. Работайте из dev container в VSCode. Другой вариант — работайте из-под другого linux-юзера, с ограниченными правами, у которого нет доступа к вашим секретам.

    Вы спросите: «ну ведь проект рано или поздно придётся запускать на проде, а там ему даются продовские секреты». Отвечу, что пока проект дойдёт до прода, он должен быть аудирован и протестирован по самые помидоры. А вот как раз в девелопмент режиме надо относиться к его зависимостям, как к вражеским лазутчикам и всячески от всего ограждать.
  • @janPona, А если всё-таки избегать мягких зависимостей, а для уменьшения размера заняться увлекательным подбором версий так чтобы зависимости более-менее совпадали?
    Да, я 5 минут назад сделал npm update в одном проекте, но я макака формошлепная, а есть же умные люди, они сдюжат?
  • @janPona, можно озаботиться и написать сотню строчек вида yarnpkg.com
  • @lurker, из коментов к колор-залго:
  • @lurker, Это понятно, и как ямочный ремонт, вполне. Но это работает только пост-фактум. Заранее знать о том, где подстелить resolution, невозможно. И опять же. Работает ли это не с top-level модулями?
  • @janPona, где подстелить resolution
    youtube.com
  • @lurker, можешь запилить вот тул, который конвертнет локфайл в оверрайды. может взлетит )
  • @BradleyManning, Кстати, мож кто уже пробовал и тулзы какие-то запилил? Ну, там, выбор версий либ для минимизации жестких зависимостей...