JavaRush /Курси /Claude code /Автоматизація build і packaging

Автоматизація build і packaging

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
# ЗБІРКА ВИКОНАНА УСПІШНО

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 без незручного «ну в мене ж запускалося».

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ