Ошибки и коды · Инструкция
pnpm frozen-lockfile в CI: почему падает установка
pnpm frozen-lockfile в CI падает, когда `pnpm-lock.
Короткий ответ
Не отключайте frozen-lockfile как первый фикс. Локально запустите `pnpm install` той же major-версией pnpm, проверьте изменения `pnpm-lock.yaml`, закоммитьте lockfile вместе с `package.json` или workspace manifest и повторите CI.
Почему локально всё работает, а CI падает
Обычный локальный `pnpm install` может обновить lockfile, чтобы привести его в соответствие с `package.json` и workspace-манифестами. В CI логика строже: frozen-lockfile запрещает неявное изменение файла. Поэтому незакоммиченная правка зависимости превращается в ошибку вместо тихого обновления. Это полезно: сборка проверяет, что репозиторий содержит воспроизводимое описание зависимостей. Частая причина — разработчик изменил `package.json`, но не добавил новый `pnpm-lock.yaml` в commit. Другой вариант — lockfile сгенерирован несовместимой версией pnpm или merge conflict разрешён не полностью. Исправлять нужно рассинхронизацию, а не защитный режим.
Как исправить frozen-lockfile без маскировки проблемы
- 1: В CI-логе найдите сообщение о том, что lockfile out of sync, требует update или отсутствует.
- 2: Сравните версию pnpm локально и в CI. Закрепите её через ваш стандартный способ управления package manager.
- 3: В чистой рабочей копии выполните `pnpm install` без frozen-флага, чтобы легально обновить `pnpm-lock.yaml`.
- 4: Просмотрите diff: изменения lockfile должны соответствовать реальным правкам manifest.
- 5: Закоммитьте manifest и lockfile вместе, затем локально проверьте `pnpm install --frozen-lockfile` перед повторным CI.
Совет: Локальный frozen-install перед push ловит рассинхронизацию до удалённой сборки.
Типичные причины и правильные действия
- Изменили package.json, не закоммитили lockfile. Обычный `pnpm install`, проверить diff, commit обоих файлов
- Merge conflict в pnpm-lock.yaml. Разрешить manifest-конфликт и регенерировать lockfile совместимой версией pnpm
- Разные major-версии pnpm локально и CI. Закрепить package manager и обновить lockfile ожидаемой версией
- Lockfile намеренно не нужен. Явно пересмотреть стратегию job; не отключать защиту случайно
Когда допустимо использовать --no-frozen-lockfile
Иногда CI-процесс действительно должен разрешать обновление lockfile — например, специальный job обновления зависимостей, который сам создаёт commit. В таком случае `--no-frozen-lockfile` может быть осознанной настройкой. Но в обычной сборке pull request это ухудшает контроль: CI способен получить новое разрешение зависимостей вместо того, что зафиксировано репозиторием. Если задача job — проверить код, а не менять зависимостные файлы, frozen-режим логичен. Поэтому отключение флага должно быть архитектурным решением конкретного workflow, а не универсальным ответом на красную сборку CI. В большинстве случаев правильный фикс — синхронизировать manifest и lockfile.
Чем --lockfile-only отличается от frozen-lockfile
Опция `--lockfile-only` разрешает обновить lockfile и manifest-данные, но не пишет зависимости в `node_modules`. Она полезна в автоматизации, где нужно пересчитать lockfile без полноценной установки. `--frozen-lockfile`, наоборот, запрещает генерацию или обновление и завершает установку ошибкой, если состояние не соответствует manifest. Эти режимы противоположны по намерению. Не добавляйте `--lockfile-only` в обычный CI только потому, что frozen упал: так проверка неизменности превращается в скрытую модификацию, которая может вообще не вернуться в репозиторий.
Проверка перед повторным запуском CI
- `package.json` и `pnpm-lock.yaml` находятся в одном commit.
- Версия pnpm локально и в CI закреплена и ожидаемо совпадает.
- Нет незавершённого merge-конфликта в workspace manifest или lockfile.
- Локальная команда `pnpm install --frozen-lockfile` проходит в чистой рабочей копии.
- В monorepo проверены все workspace package.json, которые менялись в PR.
Предупреждение: Не коммитьте гигантский неожиданный diff lockfile вслепую: сначала проверьте версию pnpm и историю merge.
Почему frozen-lockfile полезен для воспроизводимости
Lockfile фиксирует конкретное разрешение дерева зависимостей, а manifest задаёт желаемые диапазоны. Если CI разрешит молча менять lockfile, две сборки одного commit могут получить различающееся состояние после изменений в registry или метаданных. Frozen-режим заставляет репозиторий содержать согласованное описание зависимостей. Это особенно важно для monorepo, где один install работает сразу со всеми workspace-проектами. Ошибка становится ранним сигналом, что изменение зависимостей оформлено не полностью. Хороший процесс выглядит так: разработчик обновляет зависимость, pnpm обновляет lockfile, оба файла проходят review, CI повторяет установку без права менять зафиксированное состояние.
Как воспроизвести CI-ошибку локально максимально близко
Лучший тест выполняется в чистой рабочей копии без незакоммиченных файлов. Установите ту же версию Node.js и pnpm, которую использует CI-процесс, удалите локальные артефакты, которые не входят в репозиторий, и запустите ровно ту же команду install с `--frozen-lockfile`. В monorepo убедитесь, что рабочая директория и набор workspace совпадают с CI. Если локальный тест проходит, а CI нет, сравните переменные окружения, конфигурацию registry и версию pnpm из логов. Если тест падает локально тем же сообщением, задача упрощается: исправьте manifest/lockfile, пока команда не станет зелёной без специальных исключений. Такой подход гораздо надёжнее, чем многократно перезапускать CI после случайных правок YAML.
Почему lockfile нужно ревьюить как код
`pnpm-lock.yaml` может быть большим, но его изменения всё равно должны соответствовать намерению pull request. Если вы обновили один пакет, а lockfile внезапно переписался целиком, это сигнал проверить версию pnpm, настройки workspace и историю merge. Осмысленный review не требует читать каждую строку: достаточно увидеть, какие пакеты и версии изменились, не появились ли неожиданные источники и не затронуты ли десятки несвязанных зависимостей. Такой контроль особенно полезен после конфликтов или перехода между major-версиями package manager. Чем меньше случайных перегенераций попадает в main, тем реже frozen-lockfile ловит неожиданные расхождения уже на CI.
Как понять, какое изменение сделало lockfile устаревшим
Начните с diff между последним успешным commit и текущей веткой. Ищите изменения в `package.json`, workspace manifests и настройках, влияющих на разрешение зависимостей. Если manifest изменился, а соответствующий `pnpm-lock.yaml` нет, причина почти очевидна: локально зависимость могла быть установлена в уже существующее окружение, но чистый CI обязан проверить согласованность с lockfile. Если lockfile изменился огромным блоком без ожидаемого изменения зависимостей, сравните версию pnpm у разработчика и в CI-процесс. Затем запустите обычный `pnpm install`, внимательно посмотрите diff lockfile и только после этого повторите `pnpm install --frozen-lockfile`. Цель — получить состояние, при котором frozen-команда проходит без права что-либо переписать. Такой порядок сохраняет воспроизводимость и делает исправление видимым в pull request, вместо того чтобы прятать его флагом CI.
Что проверять в monorepo, когда ошибка выглядит нелогично
В workspace один `pnpm install` может охватывать несколько проектов, поэтому несоответствие не обязательно находится в пакете, который непосредственно собирает текущий job. Проверьте manifests соседних workspace, корневой `pnpm-workspace.yaml` и изменения, появившиеся после merge. Если ошибка возникла сразу после разрешения конфликта в lockfile, безопаснее восстановить согласованное состояние через обычный install, а не вручную редактировать десятки записей. После генерации снова просмотрите diff: неожиданные массовые изменения могут указывать на другую версию pnpm или изменение настроек разрешения. В CI также закрепляйте одну и ту же версию package manager для всех job, иначе разные стадии способны по-разному интерпретировать lockfile. Frozen-режим полезен именно тем, что останавливает сборку до того, как подобная рассинхронизация станет частью релиза.
Что учитывать
Обычный локальный `pnpm install` может обновить lockfile, чтобы привести его в соответствие с `package.json` и workspace-манифестами. В CI логика строже: frozen-lockfile запрещает неявное изменение файла. Поэтому незакоммиченная правка зависимости превращается в ошибку вместо тихого обновления. Это полезно: сборка проверяет, что репозиторий содержит воспроизводимое описание зависимостей. Частая причина — разработчик изменил `package.json`, но не добавил новый `pnpm-lock.yaml` в commit. Другой…
Источники и проверка
Фактическая часть сверена по первичным источникам (в т.ч. pnpm install). Пример и формулировки — редакция N1RO на 2026-09-20.