JavaRush /Курсы /Claude code /Build и packaging automation

Build и packaging automation

Claude code
21 уровень , 3 лекция
Открыта

1. Build-артефакт: документ или доказательство

Когда вы впервые просите Claude собрать Dockerfile или скрипт упаковки, очень хочется обрадоваться раньше времени. Claude собрал вам Dockerfile: команды выглядят умно, переменные названы прилично. Красота. И ловушка. Build automation не читают — ей пользуются. Пока артефакт не собрался и не запустился, это не решение, а хорошо оформленное предположение.

Здесь полезно держать в голове очень земную аналогию. Dockerfile без успешной сборки — рецепт борща, который никто не варил. Можно спорить о специях, о последовательности шагов, даже о философии картошки, но ложку в кастрюлю вы так и не опустили. Нас интересует кастрюля, а не критика рецепта.

Как только у вас есть CI-каркас с понятными permissions и artifacts, следующий вопрос становится совсем земным: чем наполнять job, кроме checkout и setup? Packaging evidence. Для Commerce OS это критично: backend на Spring Boot, frontend на Next.js, docker-compose.yml, README.md, из которого другой разработчик или runner понимает, что запускать. У build-артефакта несколько слоёв доказательства.

flowchart TD
    A[Task spec] --> B[Claude готовит build-файлы]
    B --> C[Локальная сборка]
    C --> D[Smoke-check]
    D --> E[README синхронизирован]
    E --> F[EVIDENCE_LOG.md обновлён]
    F --> G[Артефакт можно принимать]

Полезно смотреть на packaging не как на один Dockerfile, а как на маленький пакет договорённостей:

Что лежит в diff Что это доказывает Когда принимаем
Dockerfile
сервис можно собрать в образ
docker build
проходит
docker-compose.yml
или run-команда
сервис можно поднять в окружении контейнер стартует без ручной магии
README
другой человек повторит ваши шаги команды работают из чистого checkout
EVIDENCE_LOG.md
build не «кажется рабочим», а проверен есть команда, результат и smoke-evidence

Из-за этого хорошая build automation почти никогда не заканчивается одной командой «сгенерируй Dockerfile», а этим путём целиком. И да, это тот редкий случай, когда три лишние строки в EVIDENCE_LOG.md важнее пятидесяти строк «очень умного» Dockerfile.

2. Формулируйте задачу как контракт

Когда дело доходит до упаковки проекта, Claude особенно сильно страдает от слишком широких просьб: «Сделай Dockerfile» звучит для него как «дострой за меня дом, я потом скажу, где кухня». Чтобы packaging не стал археологией догадок, формулируйте задачу как маленький инженерный контракт.

Сильная постановка отвечает на пять простых вопросов. Что упаковываем: сервис или весь репозиторий. Какой артефакт правильный: jar, image, bundle. Чем проверяем: конкретная команда сборки и конкретный smoke endpoint. Что менять нельзя: не трогать production-код, не изобретать toolchain, не подменять архитектуру ради картинки. Что появится в конце кроме кода: README и build-evidence.

В Commerce OS у вас уже есть важная подсказка — CLAUDE.md, ставший на прошлых уровнях проектной памятью. Там и держите команды ./gradlew bootJar, npm run build, путь к health endpoint и запрет на «улучшения заодно». Документация Anthropic для GitHub Actions тоже советует держать CLAUDE.md коротким: он помогает Claude понимать стандарты проекта, а не самодеятельничать.

Вот как может выглядеть нормальный запрос для нашего orders-service:

Исследуй текущие Gradle-команды и структуру orders-service.
Собери Dockerfile только для этого сервиса.
Не меняй код приложения и не добавляй новые зависимости.
После генерации:
1) выполни локальную сборку образа,
2) запусти smoke-check GET /actuator/health,
3) обнови README командами сборки и запуска,
4) добавь build-evidence в EVIDENCE_LOG.md.
Если сборка падает — сначала объясни причину, потом предлагай fix.

Обратите внимание: это не «суперпромпт», а обычный task spec из ранних модулей: задача ограничена, запреты явные, проверка встроена. Claude меньше фантазирует.

Если вы запускаете подобный сценарий не интерактивно, а в scripted режиме, полезно заставить Claude оставлять структурированный вывод. В CLI-справке Claude Code для print mode описаны -p / --print, --output-format json и --max-turns; удобно для bounded automation. Но тут важно не влюбляться в конкретные флаги — сверяйте их по актуальной справке своей версии.

Практически это означает очень простую вещь. Порядок: исследовать, сгенерировать ровно один packaging-артефакт, собрать доказательство. Поменяете местами — Claude начнёт латать дыры на ходу, и вы получите сериал «угадай, почему контейнер упал на пятой серии».

3. Собираем backend-артефакт для Commerce OS

Теперь давайте приземлим всю теорию на сквозной проект курса. Baseline на уровне PR по-прежнему охватывает весь репозиторий — CI проверяет весь backend-контур. Но packaging удобнее разбирать на уровне сервиса, поэтому давайте приблизим orders-service внутри Commerce OS: build через Gradle, стандартная точка входа Spring Boot, smoke endpoint для проверки живости.

Первое, что важно зафиксировать до генерации Dockerfile, — где реально появляется jar после сборки, а не «где, наверное, должен быть». Пропустите это — и Claude скопирует в образ половину репозитория в надежде, что jar сам материализуется. Увы, Java так не работает.

Ниже — компактный multi-stage Dockerfile, который Claude предложит при нормальных границах:

FROM eclipse-temurin:25-jdk AS builder
WORKDIR /src
COPY . .
RUN ./gradlew :orders-service:bootJar --no-daemon

FROM eclipse-temurin:25-jre
WORKDIR /app
COPY --from=builder /src/orders-service/build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Этот файл хорош не двумя FROM, а тем, что у каждой строки доказуемый смысл: первая стадия строит jar в контролируемом окружении, вторая забирает готовый артефакт, не таща весь toolchain. WORKDIR убирает путаницу с путями, ENTRYPOINT честно показывает, чем стартует контейнер.

Но Dockerfile сам по себе ещё ничего не доказал. Доказательство начинается после реальной сборки:

./gradlew :orders-service:bootJar
# BUILD SUCCESSFUL

docker build -t commerce-orders:dev .
# образ собран

docker image ls commerce-orders:dev
# commerce-orders   dev   ...

Здесь важно не пропустить одну скучную, но очень взрослую мысль. Если build падает на bootJar, не просите Claude «обойти проблему в Dockerfile». Проблема не в Dockerfile — не собирается исходный build-артефакт. Разные стадии, и смешивать их — любимое развлечение хаотичного vibe coding.

Иногда на этом этапе выясняется ещё одна неприятная вещь: локально сервис собирается потому, что машина тянет хвост привычек: правильная версия Java, кэш Gradle, нужный .env, соседний сервис со вчера. Сборка контейнера быстро отнимает эти иллюзии. Она очень честная. Даже слегка бестактная.

Если сервису нужен более явный run-контракт, зафиксируйте его в docker-compose.yml или минимальной run-команде. Не для красоты, а чтобы не зависеть от памяти автора:

services:
  orders:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: docker

Это уже packaging-контур: не только «как собрать образ», но и «как поднять его предсказуемо». И вот именно здесь Claude особенно полезен: он быстро сравнивает Gradle, Dockerfile, compose и текущий профиль. Но принять результат можете только вы — после сборки и запуска, а не после красивого diff.

4. Smoke-check как минимальная проверка

Build без smoke-check кажется завершённым только первые пять минут. Потом приходит момент истины: контейнер стартует, но порт не тот, профиль не тот, приложение умирает на инициализации или поднимает не тот jar. Smoke-check отвечает на один вопрос: «оно хотя бы живое?»

Для Spring Boot-сервиса Commerce OS самым естественным smoke-check становится health endpoint: короткая, повторяемая проверка не всего сценария, а одного факта — контейнер поднялся и отвечает.

docker run -d --name orders-smoke -p 8080:8080 commerce-orders:dev
sleep 8
curl -fsS http://localhost:8080/actuator/health
# {"status":"UP"}
docker rm -f orders-smoke

В этой маленькой последовательности уже есть вся взрослая логика build automation: вы не верите образу на слово — и такой smoke-check легко читать человеку, Claude и CI runner.

Если вместо {"status":"UP"} вы получаете тишину, таймаут или stack trace — остановитесь, не отправляйте Claude в режим «исправь всё подряд». Сначала посмотрите, почему smoke упал. Не совпал порт, сервис не стартует без переменной окружения, health endpoint не готов через восемь секунд — в последнем случае чинить надо не код, а сценарий ожидания. Не подменяйте диагностику импровизацией.

Кстати, в учебной реальности разработчик часто забывает удалить контейнер, и следующий запуск падает: имя или порт занят. Поэтому короткий smoke script лучше «я руками что-то запустил и вроде увидел логи»: скрипт повторяем, ручная память — не очень.

5. README и EVIDENCE_LOG.md как часть packaging

На этом этапе многие начинают внутренне скучать: образ собрался, smoke прошёл — зачем ещё README? Через два дня другой разработчик запускает команды по памяти автора, ничего не выходит — и README перестаёт быть бюрократией. Документация — часть артефакта, а не заметка на полях.

Если вы изменили способ сборки, вы обязаны синхронизировать три вещи: build-файл, run-команду и README. Иначе появятся две реальности: в первой всё работает у автора, во второй проект «официально» запускается не теми шагами. Claude, кстати, любит такие расхождения находить, но чинит все три места только по явному заданию.

Минимальный README-блок для orders-service:

## Сборка
docker build -t commerce-orders:dev .

## Запуск
docker run --rm -p 8080:8080 commerce-orders:dev

## Smoke-check
curl -fsS http://localhost:8080/actuator/health

Это не литературное произведение, и в этом его сила: reviewer видит три команды и понимает ваш packaging-контур. Не работает одна — проблема проверяема, а не философская.

Рядом с README живёт EVIDENCE_LOG.md. В этом модуле он не монструозный отчёт — достаточно короткого и честного:

## Доказательства сборки
- Команда: docker build -t commerce-orders:dev .
- Результат: success
- Smoke: GET /actuator/health -> {"status":"UP"}
- README синхронизирован: да

Почему это важно именно сейчас, а не «потом в CI»? Лежит один Dockerfile — reviewer видит обещание. Лежат рядом README и короткий блок build-evidence — доказательство. И ещё одно тонкое место: не превращайте EVIDENCE_LOG.md в свалку всего подряд. Лог фиксирует, какая команда запущена, чем закончилась, какой smoke signal получен. Не мегабайт stdout — остальное смотрят в полном terminal log.

6. Локально зелёно, на runner'е красно

Самая интересная часть build automation начинается после локальной победы: automation падает на runner'е. Локальная правда и воспроизводимая правда — родственники, но не близнецы. Здесь не нужно драматично шептать «CI меня ненавидит» — причина обычно приземлённая.

Чаще всего локальная и удалённая сборка расходятся в типовых местах:

Симптом Частая причина Чем доказываете
локально build ок, в CI нет jar другая рабочая директория путь к артефакту в логах job
контейнер не стартует на runner'е отсутствует env или профиль diff run-команды и env
frontend собирается дольше вечности в context попал мусор размер build context и .dockerignore
локально работает, в CI нет скрытая зависимость на вашу машину чистый runner reproduces failure

Очень часто проблему спасает банальный .dockerignore. Если его не добавить, вы таскаете в build context всё подряд: .git, локальные артефакты, node_modules, временные файлы, иногда даже то, о чём проект предпочёл бы молчать. Container от этого не становится умнее, а runner — добрее.

.gradle
node_modules
.next
.git
.env

Это маленький пример файла, но эффект от него взрослый: сборка быстрее, образ чище, поведение предсказуемее. Да, здесь тоже работает тот же общий принцип курса — убираем лишний контекст, чтобы и Claude, и automation меньше спотыкались о шум.

Если вы переносите packaging-проход в GitHub Actions с Claude Code, в официальной документации Anthropic отдельно советуют хранить ключи в GitHub Secrets, ограничивать permissions, держать CLAUDE.md коротким и ставить разумные таймауты; там же claude_args используются для ограничений вроде --max-turns. Для нас это не абстрактная безопасность, а продолжение той же мысли: automation должна быть bounded, иначе одна неудачная итерация превращает build job в дорогой и бессмысленный сериал.

На практике это означает очень простую вещь. Когда build проходит локально, вы не считаете работу законченной. Вы проверяете, насколько ваш артефакт независим от вашей машины. Если команды действительно воспроизводимы, smoke действительно минимален и README действительно синхронизирован, runner обычно соглашается с вашей версией реальности. И вот в этот момент Dockerfile перестаёт быть красивой гипотезой и становится принятым packaging-артефактом, который можно показывать в review без неловкого «ну у меня же запускалось».

1
Задача
Claude code, 21 уровень, 3 лекция
Недоступна
Исправление Dockerfile для Spring Boot сервиса
Исправление Dockerfile для Spring Boot сервиса
1
Задача
Claude code, 21 уровень, 3 лекция
Недоступна
Маленькая packaging automation для локального использования
Маленькая packaging automation для локального использования
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ