# Node зовётся контейнером, а не ставится на машину разработчика - **Дата:** 2026-08-15 - **Источник:** [../../openspec/changes/archive/2026-08-15-spa-skeleton/design.md](../../openspec/changes/archive/2026-08-15-spa-skeleton/design.md), раздел «Node не ставится на машину, а зовётся контейнером» ## Решение Шаг сборки приложения гоняет установщик пакетов и сборщик **внутри контейнера**, а не вызывает их из `PATH`: > Требованием к машине разработчика становится docker, которым и так собирается > образ, — второго устанавливаемого окружения сверх `ffmpeg` не появляется > вовсе. Образ сборочного окружения берётся из ступени `Dockerfile`, а не объявляется вторым числом в `Taskfile.yml`. ## Почему Довод в дизайне назван прямо: > Так снимается расхождение, которое иначе завелось бы молча: версия Node на > машине разработчика и версия в образе — два разных числа, и собранное ими > приложение различается ровно тогда, когда различаются они. Отвергнуты два очевидных подхода, и оба с названной ценой. **Поставить Node на машину** — вводит второе устанавливаемое окружение и разъезжается с версией в образе. **Дать выбор — контейнер или локальный Node** — это второй способ делать одно и то же, и собранное ими различалось бы в зависимости от того, у кого что стоит. ## Почему это ADR Запись проходит триггер **намеренным отказом** от очевидного подхода: поставить Node на машину — ровно то, что делают по умолчанию, и отказ от этого надо объяснить один раз, а не на каждом вопросе «почему у нас нельзя просто `npm run build`». ## Что это меняет в прежнем решении [ADR-2026-08-11-spa-on-vue](ADR-2026-08-11-spa-on-vue.md) записал последствием, что «машина разработчика получает второе требуемое окружение сверх `ffmpeg`», и подразумевал под ним Node. Окружением оказался **docker**. Сам выбор фреймворка и наличие шага сборки это не пересматривает, поэтому статуса «заменено на» у той записи нет: заменена не она, а толкование одного её последствия. ## Последствия - `+` версия сборочного окружения живёт **одним** местом — ступенью `Dockerfile`, — и своего шага сверки ей не нужно. - `+` собранное в наборе проверок и собранное в образе совпадает, потому что совпадает окружение сборки, а не потому что «обычно совпадает». - `−` **набор проверок перестаёт работать без docker**, и отказ этот приходит кодом окружения. Тем же кодом приходит отказ реестра пакетов: сетезависимых шагов в наборе становится два вместо одного. - `−` контейнер ходит под тем же пользователем, что и вызвавший, а кэш установщика уводится наружу — обе частности обязательны: без них собранное ляжет от `root`, а зависимости будут тянуться заново каждый прогон. - `−` **вес и время самой ступени в образе неизвестны**: финальный образ от неё не растёт (ступень в рабочий слой не копируется), а время сборки решением владельца от 2026-08-15 не замеряется вовсе.