build(tradein/frontend): состав зависимостей перестаёт зависеть от даты сборки (#2770)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Successful in 44s
CI Trade-In / frontend-checks (pull_request) Successful in 1m1s
CI Trade-In / backend-tests (pull_request) Successful in 3m53s

`pnpm-lock.yaml` лежал в репозитории 121 КБ и не читался НИКЕМ: Dockerfile
копировал `package-lock.json*` (которого не было) и ставил `npm install`,
`ci-tradein.yml` — тоже `npm install`, слова `pnpm` в репозитории больше нет
нигде, кроме комментария в самом workflow. Последняя правка — #2312, и с тех
пор `package.json` уезжал мимо него; #2769 (devDeps под vitest) развёл их
окончательно.

То есть это был не лок, а снимок дерева, которое никто не собирает. Хуже, чем
просто мусор: `pnpm install` из него отработал бы и дал дерево, отличное от
уехавшего в прод-образ, — расхождение обнаружилось бы позже и не там.

Из двух вариантов issue взят второй, потому что задуманное ВЫРАЗИМО и уже
выражено рядом: у соседнего `frontend/` ровно эта схема — tracked
`package-lock.json` + `npm ci`. Первый вариант (просто удалить) закрепил бы
невоспроизводимую установку: до этого коммита версии в CI и в прод-образе
выбирались независимо, каждая по дате своей сборки, и гейт проверял не тот
код, который деплоится.

Что сделано:
  • `tradein-mvp/frontend/package-lock.json` — сгенерирован и TRACKED
    (483 пакета, lockfileVersion 3, 12 записей swc-linux → ставится на linux
    из лока, снятого на macOS, как у соседнего frontend/);
  • `.gitignore` — снята строка `frontend/package-lock.json`. В issue сказано
    «в .gitignore не попадал — просто не коммитился»; это НЕВЕРНО, он там был
    (tradein-mvp/.gitignore:18), поэтому лок и не мог появиться сам;
  • Dockerfile: `npm install` → `npm ci`, `package-lock.json*` → без `*`
    (отсутствие лока обязано ронять сборку, а не молча включать install);
  • ci-tradein.yml: `npm install` → `npm ci` + `cache: npm` в setup-node;
  • .claude/rules/frontend.md — правило «правишь package.json → регенерируй
    лок в том же PR» распространено на tradein-фронт.

Верификация локально:
  • `npm ci` в node:20-alpine внутри `docker build` — образ собран целиком
    (deps → next build → runner), контейнер поднят, `/` отвечает 307;
  • все четыре шага гейта на новом локе: type-check ✓, vitest 13 passed ✓,
    next lint ✓ (только преды, довоенные), mera-public isolation ✓.

Closes #2770
This commit is contained in:
bot-backend 2026-08-07 13:34:46 +05:00
parent 5046ac7b4e
commit 443de71ded
6 changed files with 8665 additions and 3721 deletions

View file

@ -49,6 +49,7 @@ cd frontend && npm install --legacy-peer-deps --no-audit --no-fund
- Pre-push check: `git diff main..HEAD -- frontend/package.json frontend/package-lock.json` — если только один из двух тронут → STOP, regen lock.
- Imports без deps entry (TypeScript авто-resolve через transitive) — **latent bomb** до first `npm ci`.
- Reference incident: PR #344 (2026-05-17) добавил `lucide-react` без regen lockfile → deploy #135 fail → P0 hotfix PR #345 (commit `6ee20294f2`).
- **То же правило для `tradein-mvp/frontend/`** (#2770): там теперь тоже tracked `package-lock.json` + `npm ci` в Dockerfile и в `ci-tradein.yml`. До #2770 лока не было вовсе (лежал `pnpm-lock.yaml`, из которого никто не ставил), и состав зависимостей прод-образа определялся датой сборки.
## Prettier / lint

View file

@ -285,20 +285,23 @@ jobs:
- name: Set up Node
# Node 20 — major из tradein-mvp/frontend/Dockerfile (node:20-alpine).
# npm-кэш setup-node НЕ настраиваем: в tradein-mvp/frontend нет
# package-lock.json (Dockerfile ставит через npm install), а cache=npm
# без lockfile падает. Кэш wheels/node тут не критичен для type-check/lint.
# cache: npm включён с #2770 — package-lock.json теперь tracked.
uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: tradein-mvp/frontend/package-lock.json
- name: Install deps (npm install, no lockfile)
# ТОЧНЫЕ флаги из tradein-mvp/frontend/Dockerfile (deps stage):
# --legacy-peer-deps — Tailwind/React 19 peer-dep mismatches;
# --no-audit --no-fund — тише и быстрее в CI. `install` (не `ci`):
# в tradein-mvp/frontend НЕТ package-lock.json (есть pnpm-lock.yaml, но
# Dockerfile ставит именно npm install) → `npm ci` упал бы.
run: npm install --legacy-peer-deps --no-audit --no-fund
- name: Install deps (npm ci)
# ТОЧНЫЕ флаги из tradein-mvp/frontend/Dockerfile (deps stage), чтобы гейт
# видел то же дерево, что уедет в образ. `ci`, а не `install` (#2770): до
# него лока не было вовсе (лежал мёртвый pnpm-lock.yaml, из которого никто
# не ставил), и версии в CI и в прод-образе выбирались независимо по дате
# сборки — гейт проверял не тот код, который деплоится.
#
# Правишь package.json — регенерируй лок в том же PR: `npm ci` требует
# точного match и иначе роняет и этот job, и build образа.
run: npm ci --legacy-peer-deps --no-audit --no-fund
- name: Type-check (tsc --noEmit)
# Blocking: любая TS-ошибка → job RED.

View file

@ -15,7 +15,10 @@ frontend/node_modules/
frontend/.next/
frontend/out/
frontend/dist/
frontend/package-lock.json
# frontend/package-lock.json — TRACKED с #2770: Dockerfile и CI ставят `npm ci`,
# которому лок обязателен. Правишь frontend/package.json — регенерируй лок
# (`npm install --legacy-peer-deps --no-audit --no-fund`) в том же PR, иначе
# `npm ci` роняет сборку образа и весь deploy откатывается.
frontend/next-env.d.ts
# Docker

View file

@ -4,9 +4,13 @@
# ---- deps ----
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
# Лок ОБЯЗАТЕЛЕН (без `*`): `npm ci` без него падает, и это правильно —
# отсутствие лока раньше молча давало `npm install`, т.е. состав зависимостей
# определялся датой сборки (#2770).
COPY package.json package-lock.json ./
# --legacy-peer-deps: React 19 peer-dep mismatches (как в соседнем frontend/).
RUN --mount=type=cache,target=/root/.npm \
npm install --legacy-peer-deps --no-audit --no-fund
npm ci --legacy-peer-deps --no-audit --no-fund
# ---- builder ----

8641
tradein-mvp/frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff