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
55 lines
2.1 KiB
Docker
55 lines
2.1 KiB
Docker
# Trade-In MVP frontend — Next.js 15 + React 19 + TanStack Query.
|
||
# Без Sentry/Tailwind/Leaflet/echarts — лёгкая версия только для trade-in.
|
||
|
||
# ---- deps ----
|
||
FROM node:20-alpine AS deps
|
||
WORKDIR /app
|
||
# Лок ОБЯЗАТЕЛЕН (без `*`): `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 ci --legacy-peer-deps --no-audit --no-fund
|
||
|
||
|
||
# ---- builder ----
|
||
FROM node:20-alpine AS builder
|
||
WORKDIR /app
|
||
ENV NEXT_TELEMETRY_DISABLED=1
|
||
|
||
# basePath + API-prefix baked-in во время build (Next.js — build-time only).
|
||
# В CI workflow для prod:
|
||
# --build-arg NEXT_PUBLIC_BASE_PATH=/trade-in
|
||
# --build-arg NEXT_PUBLIC_API_BASE_URL=/trade-in
|
||
# В локальном dev: оба пусто = no-op (same-origin).
|
||
ARG NEXT_PUBLIC_BASE_PATH=""
|
||
ENV NEXT_PUBLIC_BASE_PATH=$NEXT_PUBLIC_BASE_PATH
|
||
ARG NEXT_PUBLIC_API_BASE_URL=""
|
||
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
|
||
ARG NEXT_PUBLIC_ENABLE_PREVIEW=""
|
||
ENV NEXT_PUBLIC_ENABLE_PREVIEW=$NEXT_PUBLIC_ENABLE_PREVIEW
|
||
|
||
COPY --from=deps /app/node_modules ./node_modules
|
||
COPY . .
|
||
RUN npm run build
|
||
|
||
|
||
# ---- runner ----
|
||
FROM node:20-alpine AS runner
|
||
WORKDIR /app
|
||
# HOSTNAME=0.0.0.0 — Next.js standalone server.js биндится по этому env.
|
||
# Без него Docker ставит HOSTNAME=<container-id> и сервер недоступен по
|
||
# имени сервиса из других контейнеров (Caddy → 502).
|
||
ENV NODE_ENV=production \
|
||
NEXT_TELEMETRY_DISABLED=1 \
|
||
HOSTNAME=0.0.0.0 \
|
||
PORT=3000
|
||
|
||
COPY --from=builder --chown=node:node /app/public ./public
|
||
COPY --from=builder --chown=node:node /app/.next/standalone ./
|
||
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
|
||
|
||
USER node
|
||
EXPOSE 3000
|
||
CMD ["node", "server.js"]
|