Для чего нужна команда npm ci
Что проверяют
Понимаете ли вы, как добиться воспроизводимой установки зависимостей и чем она отличается от установки при разработке. Следом часто спрашивают, почему в CI нельзя просто написать npm install и что кешировать в пайплайне.
Как отвечать
npm ci ставит зависимости строго по lock-файлу и ничего не решает сама. Её поведение:
- нужен lock-файл:
package-lock.jsonилиnpm-shrinkwrap.json. Без него команда падает с ошибкой; - lock и
package.jsonдолжны совпадать: если вpackage.jsonдобавили пакет, а lock не обновили,npm ciне станет чинить расхождение, а завершится с ошибкой.npm installв той же ситуации молча перепишет lock; - ничего не пишет: ни
package.json, ни lock команда не меняет; - удаляет
node_modulesперед установкой, если папка есть, поэтому результат не зависит от того, что там осталось; - ставит всё дерево целиком: добавить отдельный пакет через
npm ciнельзя, это задачаnpm install <пакет>.
Работает она быстрее, потому что не разрешает версии: дерево уже описано в lock-файле. Скрипты жизненного цикла вроде postinstall при этом выполняются, отключаются флагом --ignore-scripts. Для продового образа dev-зависимости отсекают через --omit=dev.
Правильный сценарий такой: локально разработчик меняет зависимости через npm install и коммитит lock, а CI, Docker-сборка и деплой гоняют npm ci. У других менеджеров есть аналоги: pnpm install --frozen-lockfile (в CI это поведение pnpm по умолчанию) и yarn install --immutable у Yarn Berry.
Где спотыкаются
Кешируют в CI папку node_modules. npm ci всё равно её удалит, кешировать нужно глобальный кеш ~/.npm, как это делает actions/setup-node с cache: npm.
Держат lock-файл в .gitignore или не коммитят его после изменений. Тогда npm ci падает у коллег и в пайплайне, а без lock-файла сборки разъезжаются по версиям.
Лечат ошибку рассинхронизации заменой npm ci на npm install в CI. Ошибка исчезает, но вместе с ней пропадает гарантия, что в прод уехало ровно то, что проверяли локально.