Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Питання на співбесіді: Хуки, CI й автоматизація

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

14 питань

Хуки - виконувані скрипти, які Git запускає в певні моменти: перед комітом, після checkout, перед push. Лежать у .git/hooks/ (Git створює там приклади з суфіксом .sample) і мають бути виконуваними (chmod +x).

Клієнтські хуки, які використовують найчастіше:

Хук Коли Типове використання
pre-commit перед створенням коміту форматування, лінтер, швидкі перевірки
prepare-commit-msg перед відкриттям редактора повідомлення підставити номер задачі з назви гілки
commit-msg після введення повідомлення перевірити формат (Conventional Commits)
post-checkout після checkout/switch оновити залежності, очистити кеш
pre-push перед відправкою на сервер запустити тести

Ненульовий код виходу скасовує операцію - так pre-commit може заборонити коміт:

#!/bin/sh
# .git/hooks/pre-commit
vendor/bin/pint --test || {
    echo "Код не відформатовано. Запустіть vendor/bin/pint"
    exit 1
}

Що отримують хуки:

  • commit-msg - шлях до файла з повідомленням коміту (першим аргументом);
  • pre-push - назву й адресу віддаленого репозиторію в аргументах, а в стандартному вводі - рядки з посиланнями й SHA, які відправляються.

Важливі обмеження:

  • .git/hooks не потрапляє в репозиторій - хуки не поширюються командою автоматично (для цього - core.hooksPath чи інструменти на кшталт Husky і CaptainHook);
  • хуки легко обійти: git commit --no-verify (чи -n) пропускає pre-commit і commit-msg, git push --no-verify - pre-push. Тому хуки - зручність для розробника, а гарантію дає CI;
  • хуки мають бути швидкими: pre-commit на хвилину - і люди почнуть використовувати --no-verify постійно. Повний набір тестів - у pre-push чи CI.

Докладніше в документації: githooks

Каталог .git/ не версіонується, тож хуки, покладені в .git/hooks, живуть лише на вашій машині. Є кілька способів зробити їх спільними.

1. core.hooksPath - вбудований механізм Git:

mkdir .githooks
# покласти туди pre-commit, commit-msg... і зробити виконуваними
chmod +x .githooks/*
git config core.hooksPath .githooks

Хуки лежать у репозиторії й проходять рев'ю, але кожен розробник має один раз виконати git config. Його зручно додати в скрипт налаштування проєкту:

{
    "scripts": {
        "post-install-cmd": ["git config core.hooksPath .githooks || true"]
    }
}

2. CaptainHook - для PHP-проєктів: конфігурація в captainhook.json, хуки встановлюються командою vendor/bin/captainhook install (або автоматично через Composer-плагін):

{
    "pre-commit": {
        "enabled": true,
        "actions": [
            { "action": "vendor/bin/pint --test" },
            { "action": "vendor/bin/phpstan analyse --no-progress" }
        ]
    }
}

3. GrumPHP - інший PHP-інструмент: набір готових задач (phpcs, phpstan, phpunit, перевірка повідомлень комітів), конфігурація в grumphp.yml, хуки ставить сам.

4. Husky - якщо в проєкті є Node: npx husky init створює каталог .husky/ і через скрипт prepare у package.json налаштовує core.hooksPath при кожному npm install. Часто разом з lint-staged, що запускає інструменти лише на змінених файлах.

Як обрати: PHP-бекенд без Node - CaptainHook чи GrumPHP; є фронтенд і npm install - Husky; мінімум залежностей - core.hooksPath.

Пам'ятайте: хуки - зручність, а не гарантія. Будь-хто може їх не встановити чи пропустити через --no-verify, тому ті самі перевірки обов'язково дублюються в CI.

Докладніше в документації: git config: core.hooksPath

Laravel Pint - форматувальник коду на основі PHP CS Fixer. Корисні режими:

vendor/bin/pint                 # виправити всі файли
vendor/bin/pint --test          # лише перевірити, ненульовий код при помилках
vendor/bin/pint --dirty         # лише файли з незакоміченими змінами
vendor/bin/pint --diff=main     # лише файли, що відрізняються від гілки main
vendor/bin/pint --repair        # виправити, але повернути ненульовий код, якщо було що виправляти

Простий pre-commit хук:

#!/bin/sh
files=$(git diff --cached --name-only --diff-filter=ACMR -- '*.php')
[ -z "$files" ] && exit 0

vendor/bin/pint $files
git add $files
  • git diff --cached - лише файли, додані в індекс (тобто ті, що потраплять у коміт);
  • --diff-filter=ACMR - додані, скопійовані, змінені й перейменовані; видалені файли форматувати нема чого;
  • git add після форматування - щоб виправлення потрапили в коміт.

Пастка частково доданих файлів: якщо у файлі частина змін додана в індекс (git add -p), а частина ні, то git add $files після Pint додасть у коміт і ті зміни, які ви навмисно не додавали. Варіанти:

  • лише перевіряти, а не виправляти: vendor/bin/pint --test $files - коміт блокується, розробник сам запускає Pint;
  • використати інструмент, що вміє працювати з частковим індексом (lint-staged ховає неіндексовані зміни на час перевірки).

--dirty у хуку - зручно, але він бере файли з будь-якими незакоміченими змінами, а не лише з індексу.

Що варто знати:

  • форматування в хуку - зручність; в CI має бути pint --test, щоб неформатований код не потрапив у main через --no-verify;
  • конфігурація в pint.json - однакова для хуку, CI й редактора;
  • великий одноразовий перехід на Pint краще зробити окремим комітом і додати його SHA у .git-blame-ignore-revs, щоб git blame не показував форматування як автора всіх рядків.

Докладніше в документації: Laravel Pint: запуск

GitHub Actions - вбудований у GitHub CI/CD. Робочі процеси (workflows) описуються в YAML у .github/workflows/ і запускаються на події: push, pull request, розклад, ручний запуск.

Основні поняття:

  • workflow - файл з описом процесу;
  • event (on) - що запускає процес;
  • job - набір кроків, що виконується на окремій віртуальній машині (runner); jobs ідуть паралельно, якщо не вказано залежність needs;
  • step - команда (run) чи готова дія (uses) з Marketplace.

Мінімальний workflow для Laravel:

# .github/workflows/tests.yml
name: tests

on:
  pull_request:
  push:
    branches: [main]

jobs:
  tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7

      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.5'
          coverage: none

      - run: composer install --no-interaction --prefer-dist

      - run: cp .env.example .env && php artisan key:generate

      - run: vendor/bin/pint --test

      - run: php artisan test --compact

Що відбувається: на кожен PR і push у main GitHub піднімає чисту машину з Ubuntu, завантажує код, встановлює PHP і залежності, перевіряє стиль і запускає тести. Результат видно в PR як перевірку - зелену чи червону.

Чому це важливо:

  • те, що «працює в мене», перевіряється в чистому середовищі;
  • хуки можна пропустити, а CI - ні, особливо з обов'язковими перевірками в захисті гілки;
  • рецензент не витрачає час на код, який не проходить тести.

Корисно знати:

  • для публічних репозиторіїв стандартні runner-и безкоштовні, для приватних є місячна квота хвилин;
  • секрети (ключі API) - у Settings → Secrets і доступні як ${{ secrets.NAME }}; у PR з форків секрети за замовчуванням не передаються;
  • shivammathur/setup-php - де-факто стандарт для PHP: версії, розширення, інструменти.

Докладніше в документації: GitHub Actions: основи

Dependabot - бот GitHub, що стежить за залежностями й сам відкриває pull request-и з оновленнями. Розробнику лишається переглянути зміни, дочекатися зеленого CI і злити.

Два режими:

  • security updates - PR лише для залежностей з відомими вразливостями (на основі GitHub Advisory Database);
  • version updates - регулярні PR з новими версіями за розкладом.

Конфігурація для Laravel-проєкту:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: composer
    directory: /
    schedule:
      interval: weekly
    groups:
      laravel:
        patterns: ["laravel/*"]
    open-pull-requests-limit: 5

  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly

  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: monthly

Що тут налаштовано:

  • екосистеми: Composer, npm і навіть версії дій у workflow GitHub Actions;
  • groups - кілька оновлень в одному PR (усі пакети laravel/* разом), щоб не тонути в десятках дрібних PR;
  • open-pull-requests-limit - скільки PR бот тримає відкритими одночасно;
  • schedule - як часто перевіряти.

Чому це варто робити:

  • маленькі регулярні оновлення простіші за одне велике раз на рік, коли накопичилися мажорні версії й breaking changes;
  • вразливості закриваються швидко, а не після інциденту;
  • оновлення проходять той самий процес, що й код: CI, рев'ю, історія.

Без доброго набору тестів Dependabot корисний наполовину: зелений CI має реально означати «оновлення нічого не зламало». Тому тести - передумова для сміливих оновлень.

Корисні опції: ignore - пропускати певні пакети чи мажорні версії, cooldown - не пропонувати версію одразу після публікації, а почекати кілька днів (захист від зламаних чи скомпрометованих релізів).

Докладніше в документації: GitHub: Dependabot version updates

Для повідомлень комітів є два хуки:

  • prepare-commit-msg - викликається до відкриття редактора; може підготувати чи доповнити текст;
  • commit-msg - викликається після введення повідомлення; може перевірити його і заборонити коміт.

Обидва отримують шлях до файла з повідомленням першим аргументом.

Перевірка Conventional Commits у commit-msg:

#!/bin/sh
pattern='^(feat|fix|docs|refactor|test|chore|perf|ci|build)(\([a-z0-9-]+\))?!?: .{1,72}$'

if ! head -1 "$1" | grep -qE "$pattern"; then
    echo "Повідомлення не відповідає Conventional Commits:"
    echo "  feat(vacancies): add CSV export"
    exit 1
fi

Номер задачі з назви гілки в prepare-commit-msg:

#!/bin/sh
# гілка feature/ABC-123-csv-export -> префікс [ABC-123]
case "$2" in merge|squash|commit) exit 0 ;; esac   # не чіпати merge, squash і --amend

ticket=$(git branch --show-current | grep -oE '[A-Z]+-[0-9]+')
[ -n "$ticket" ] && ! grep -q "$ticket" "$1" && sed -i.bak "1s/^/[$ticket] /" "$1" && rm -f "$1.bak"

Другий аргумент - джерело повідомлення (message при -m, merge, squash, commit при --amend чи -c), щоб не дописувати префікс у службові повідомлення.

Готові інструменти:

  • commitlint (Node) - правила для Conventional Commits, часто разом з Husky;
  • CaptainHook має вбудовані дії для перевірки повідомлень (довжина рядків, регулярні вирази, загальноприйняті правила оформлення повідомлень);
  • GrumPHP - задача git_commit_message.

Чому перевірка потрібна і в CI: хук локальний, і git commit --no-verify його пропускає. Якщо від формату залежать змінлог і версіонування, перевіряйте повідомлення ще й у CI - або заголовок PR, якщо зливаєте через squash: тоді саме він стає повідомленням коміту в main.

Докладніше в документації: githooks

Тести, що працюють з базою, найкраще ганяти на тій самій СУБД, що й на продакшені: SQLite пропускає помилки, специфічні для PostgreSQL чи MySQL (суворіший GROUP BY, типи, регістр у LIKE).

Service containers - Docker-контейнери, які GitHub Actions піднімає поруч із job:

jobs:
  tests:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:18
        env:
          POSTGRES_DB: testing
          POSTGRES_USER: app
          POSTGRES_PASSWORD: secret
        ports: ["5432:5432"]
        options: >-
          --health-cmd pg_isready
          --health-interval 5s
          --health-timeout 5s
          --health-retries 10
      redis:
        image: redis:8-alpine
        ports: ["6379:6379"]

    env:
      DB_CONNECTION: pgsql
      DB_HOST: 127.0.0.1
      DB_DATABASE: testing
      DB_USERNAME: app
      DB_PASSWORD: secret
      REDIS_HOST: 127.0.0.1

    steps:
      - uses: actions/checkout@v7
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.5'
          extensions: pdo_pgsql, redis

      - id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
      - uses: actions/cache@v6
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: composer-${{ hashFiles('composer.lock') }}
          restore-keys: composer-

      - run: composer install --no-interaction --prefer-dist
      - run: php artisan test --parallel

Ключові моменти:

  • healthcheck у options - GitHub чекає, доки база стане здоровою, перш ніж запускати кроки. Без нього тести можуть стартувати раніше, ніж PostgreSQL готовий;
  • адреса: job працює прямо на runner-і, тому сервіси доступні як 127.0.0.1 через опубліковані порти. Якщо job сам виконується в контейнері (container:), сервіси доступні за назвою (postgres), і порти публікувати не треба;
  • кеш Composer за хешем composer.lock - залежності не завантажуються щоразу; кешується каталог завантажень Composer, а не vendor/;
  • змінні env перекривають .env - конфігурація тестів без окремого файла.

Прискорення: --parallel для тестів, окремі jobs для стилю (Pint) і статичного аналізу (PHPStan), щоб вони йшли паралельно з тестами, і concurrency з cancel-in-progress: true - скасовувати застарілі запуски при новому push у ту саму гілку.

Докладніше в документації: GitHub Actions: service containers

actions/checkout за замовчуванням завантажує лише один коміт - той, що запустив workflow (fetch-depth: 1). Для збирання й тестів цього досить, і це швидко. Але все, що потребує історії, ламається.

Типові симптоми:

fatal: No names found, cannot describe anything.           # git describe без тегів
fatal: ambiguous argument 'origin/main...HEAD'             # немає гілки main
fatal: bad object ...                                       # немає потрібного коміту
  • git describe чи визначення версії з тегів - тегів немає;
  • git diff origin/main...HEAD, pint --diff=main, «запускати тести лише для змінених файлів» - немає main і спільного предка;
  • генерація змінлогу з повідомлень комітів - немає комітів;
  • git log для дати останньої зміни файла - показує один коміт;
  • інструменти на кшталт SonarQube скаржаться на неповну історію для blame.

Варіанти виправлення:

- uses: actions/checkout@v7
  with:
    fetch-depth: 0          # уся історія, усі гілки й теги

0 - найпростіше, але у великому репозиторії checkout стає помітно повільнішим.

Точніше - догрузити лише потрібне:

- uses: actions/checkout@v7
- run: git fetch --no-tags --depth=50 origin main
- run: vendor/bin/pint --test --diff=origin/main

Або fetch-tags: true з невеликою глибиною, якщо потрібні лише теги.

Нюанс pull request-ів: для події pull_request checkout за замовчуванням бере тимчасовий коміт злиття PR з базовою гілкою (refs/pull/N/merge), а не останній коміт гілки PR. Тести перевіряють результат злиття, що добре, але git log покаже коміт злиття, якого немає в гілці. Якщо потрібна саме голова гілки - ref: ${{ github.event.pull_request.head.sha }}.

Правило: глибина історії в CI - свідомий вибір: мінімум для швидкості, а для кроків, яким потрібна історія, - явно догрузити рівно стільки, скільки треба.

Докладніше в документації: actions/checkout

Видалити секрет з історії складно, а після публікації - вже запізно: ключ треба вважати скомпрометованим. Тому головне - не дати йому потрапити в коміт. Захист будують у кілька шарів.

1. Структура проєкту:

  • секрети лише в .env, який у .gitignore; у репозиторії - .env.example з порожніми значеннями;
  • у коді - лише config('services.stripe.secret'), ніяких ключів у тестах, сидерах і фікстурах;
  • для CI - секрети платформи (${{ secrets.NAME }}), а не файли в репозиторії.

2. Локальна перевірка до коміту - сканер секретів у pre-commit:

#!/bin/sh
gitleaks git --pre-commit --staged --redact --verbose

gitleaks шукає за сотнями шаблонів (ключі AWS, Stripe, GitHub-токени, приватні ключі) і за ентропією рядків. Хибні спрацювання - через .gitleaksignore чи конфігурацію.

3. Захист на сервері - push protection на GitHub:

  • для користувачів увімкнено за замовчуванням: GitHub блокує ваш push із розпізнаним секретом у публічні репозиторії;
  • для репозиторіїв (потрібна GitHub Secret Protection) - вмикається адміністратором і блокує push із секретами у конкретний репозиторій, зокрема з командного рядка, через вебінтерфейс і API.

При блокуванні push GitHub показує, у якому файлі й коміті знайдено секрет. Обхід можливий із зазначенням причини (хибне спрацювання, тестовий ключ), і для захисту на рівні репозиторію він створює сповіщення й запис у журналі аудиту.

4. Сканування в CI - той самий gitleaks на кожен PR: ловить те, що пройшло повз локальний хук через --no-verify чи відсутність хука.

5. Мінімізація шкоди:

  • ключі з мінімальними правами й обмеженим терміном дії;
  • окремі ключі для розробки, staging і продакшену;
  • план дій на випадок витоку: спочатку відкликати ключ, а вже потім чистити історію.

Чому шари: кожен окремо можна обійти - хук не встановлено, push protection не знає формату вашого внутрішнього токена. Разом вони ловлять переважну більшість випадків.

Докладніше в документації: GitHub: push protection

Коли Composer встановлює пакет з дистрибутива (--prefer-dist, поведінка за замовчуванням для стабільних версій), він завантажує ZIP-архів, який GitHub генерує через git archive. Атрибут export-ignore у .gitattributes виключає файли з цього архіву.

# .gitattributes пакета
/.github            export-ignore
/tests              export-ignore
/docs               export-ignore
/.gitattributes     export-ignore
/.gitignore         export-ignore
/phpunit.xml.dist   export-ignore
/phpstan.neon.dist  export-ignore
/pint.json          export-ignore
/CHANGELOG.md       export-ignore

Перевірити результат локально:

git archive HEAD | tar -t

Виключені файли в списку не з'являться.

Навіщо це:

  • менший vendor/ у кожному проєкті, що використовує пакет: тести, документація й конфіги CI пакета там нікому не потрібні;
  • швидше встановлення і менші образи Docker;
  • менше шуму для інструментів, що сканують vendor/ (автодоповнення IDE, статичний аналіз);
  • не публікувати зайве - внутрішні скрипти, фікстури з даними.

Що не можна виключати:

  • src/, composer.json, файли, на які посилається автозавантаження;
  • LICENSE - ліцензія має йти разом з кодом;
  • README.md зазвичай лишають;
  • ресурси, потрібні пакету під час роботи: шаблони, конфіги, міграції, файли перекладів.

Помилка тут ламає пакет лише при встановленні з дистрибутива: локально в репозиторії все працює, тому варто перевіряти через git archive чи тестове встановлення в чистий проєкт.

Пов'язане: --prefer-source завантажує повний клон репозиторію, і export-ignore там не діє. Інший корисний атрибут - export-subst: підставляє дані коміту (наприклад, $Format:%H$) у файли при створенні архіву.

Докладніше в документації: gitattributes: export-ignore

Ручний git bisect вимагає на кожному кроці перевірити код і сказати good чи bad. git bisect run робить це сам: на кожному кроці запускає скрипт і читає його код завершення.

git bisect start HEAD v2.3.0      # погана ревізія, потім добра
git bisect run php artisan test --compact --filter=VacancyExportTest
git bisect reset

На 1000 комітах це близько 10 запусків тесту замість ручного пошуку.

Як Git трактує код завершення:

Код Значення
0 коміт добрий
1-127, крім 125 коміт поганий
125 коміт неможливо перевірити - пропустити (skip)
інше (наприклад, 128 і більше) перервати bisect

Скрипт для реальних умов - стара версія коду може потребувати іншої підготовки:

#!/bin/sh
# bisect.sh
composer install --no-interaction --quiet || exit 125   # не збирається - пропустити
php artisan migrate:fresh --env=testing --force --quiet || exit 125

php artisan test --compact --filter=VacancyExportTest
  • exit 125 - коміт, на якому проєкт не збирається з не пов'язаних причин, не вважається «поганим» і не збиває пошук;
  • база - тестова (--env=testing), щоб не зачепити робочу.

Тест, якого ще не було в історії: часто баг виявили сьогодні, а тест написали теж сьогодні - у старих комітах його немає. Рішення - тримати тест поза робочим деревом і копіювати його на кожному кроці:

cp /tmp/RegressionTest.php tests/Feature/RegressionTest.php
php artisan test --compact tests/Feature/RegressionTest.php
status=$?
rm tests/Feature/RegressionTest.php
exit $status

Корисні деталі:

  • git bisect start --first-parent - іти лише по комітах злиття основної гілки: знайти, який PR зламав, без заходу в проміжні коміти гілок;
  • git bisect log і git bisect replay - зберегти й повторити сесію;
  • терміни good/bad можна замінити (--term-old=fast --term-new=slow) - bisect підходить і для пошуку регресії продуктивності, не лише помилки.

Докладніше в документації: git bisect run

Серверні хуки виконуються на сервері, що приймає push, і можуть відхилити його. На відміну від клієнтських, їх не можна пропустити через --no-verify - розробник їх не контролює.

Основні серверні хуки:

Хук Коли Що може
pre-receive один раз на весь push, до оновлення посилань відхилити весь push
update для кожного оновлюваного посилання окремо відхилити окрему гілку чи тег
post-receive після успішного оновлення сповіщення, запуск деплою; відхилити вже не може

pre-receive отримує на стандартний ввід рядки <старий SHA> <новий SHA> <посилання>:

#!/bin/sh
zero=0000000000000000000000000000000000000000
while read old new ref; do
    # заборонити force push у main: старий коміт має бути предком нового
    if [ "$ref" = "refs/heads/main" ] && [ "$old" != "$zero" ]; then
        if ! git merge-base --is-ancestor "$old" "$new"; then
            echo "Force push у main заборонено"
            exit 1
        fi
    fi
done

Вивід хука повертається клієнту з префіксом remote:.

Де це доступно:

  • власний сервер Git, GitLab self-managed, GitHub Enterprise Server - так;
  • github.com - власні серверні хуки встановити не можна. Їхню роль виконують rulesets (заборона force push, обов'язкові підписи, обмеження на шляхи й розмір файлів) і push protection для секретів.

Серверні хуки проти CI:

Серверний хук CI
коли синхронно, під час push асинхронно, після push
результат push відхилено, коду на сервері немає код уже на сервері, PR позначено червоним
час секунди - інакше push «зависає» хвилини, без обмежень
що перевіряти політики: формат, заборонені файли, права, підписи збирання, тести, аналіз

Правило: у хуки - швидкі й безумовні політики, порушення яких не має з'явитися на сервері навіть у гілці (секрети, величезні бінарні файли, заборонені операції). Усе, що довге чи потребує середовища, - в CI. Хук, що запускає тести, робить кожен push хвилинним і ламає роботу всієї команди при збоях.

Докладніше в документації: githooks: pre-receive

Якщо повідомлення комітів дотримуються Conventional Commits, з історії можна автоматично визначити, яким має бути наступний номер версії і що писати в змінлог.

Правила семантичного версіонування з комітів:

Коміти з останнього релізу Нова версія
лише fix: патч: 1.4.2 → 1.4.3
є feat: мінорна: 1.4.2 → 1.5.0
feat!: чи футер BREAKING CHANGE: мажорна: 1.4.2 → 2.0.0
chore:, docs:, test: релізу не потребують

release-please (від Google) працює через релізний pull request:

  1. після кожного злиття в main дія аналізує нові коміти;
  2. відкриває чи оновлює PR «chore(main): release 1.5.0» зі зміненим CHANGELOG.md і номером версії;
  3. команда зливає цей PR, коли хоче випустити реліз;
  4. дія створює тег v1.5.0 і GitHub Release з нотатками.
on:
  push:
    branches: [main]

permissions:
  contents: write
  pull-requests: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: googleapis/release-please-action@v5
        with:
          release-type: php

semantic-release - альтернатива без проміжного PR: реліз публікується одразу після злиття. Більше автоматизації, менше контролю над моментом релізу.

Що потрібно для роботи:

  • дисципліна повідомлень - перевірка в commit-msg і в CI; при злитті через squash - перевірка заголовка PR, бо він стає повідомленням коміту;
  • осмислені коміти: fix: typo в історії main потрапить у змінлог;
  • права боту на запис і створення PR; якщо релізний PR має запускати CI, потрібен токен GitHub App чи PAT - події від стандартного GITHUB_TOKEN не запускають інші workflow.

Обмеження: змінлог з комітів - для розробників. Для користувачів продукту часто потрібні людські нотатки до релізу, і автоматичний текст - лише чернетка для них.

Докладніше в документації: release-please

У великому проєкті з десятками залежностей бот, що відкриває окремий PR на кожне оновлення, швидко стає шумом, який команда ігнорує. Мета - щоб рутинні оновлення проходили без людей, а увагу отримували лише ризиковані.

Renovate - гнучкіша альтернатива Dependabot: підтримує більше екосистем, складні правила, групування, автозлиття й «панель залежностей» (Dependency Dashboard) - задачу зі списком усіх очікуваних оновлень.

{
    "extends": ["config:recommended"],
    "schedule": ["before 6am on monday"],
    "minimumReleaseAge": "3 days",
    "lockFileMaintenance": { "enabled": true },
    "packageRules": [
        {
            "matchUpdateTypes": ["patch", "minor"],
            "matchCurrentVersion": "!/^0/",
            "automerge": true
        },
        {
            "matchPackageNames": ["laravel/**", "livewire/**", "filament/**"],
            "groupName": "Laravel ecosystem"
        },
        {
            "matchDepTypes": ["require-dev"],
            "groupName": "dev dependencies",
            "automerge": true
        },
        {
            "matchUpdateTypes": ["major"],
            "dependencyDashboardApproval": true
        }
    ]
}

Що тут відбувається:

  • автозлиття патчів і мінорних версій стабільних пакетів - якщо CI зелений, PR вливається сам;
  • групи - пов'язані пакети оновлюються разом (екосистема Laravel), а не десятком окремих PR;
  • мажорні версії - лише після схвалення на панелі, бо вони потребують міграції коду;
  • minimumReleaseAge - не брати версію, опубліковану щойно: зламані й скомпрометовані релізи зазвичай відкликають за перші дні;
  • lockFileMaintenance - періодично оновлює транзитивні залежності в composer.lock, які інакше застарівають непомітно;
  • розклад - PR з'являються в певний час, а не протягом усього тижня.

Передумови для автозлиття:

  • тести, яким можна довіряти - інакше автозлиття постачає баги в main;
  • обов'язкові перевірки в захисті гілки - бот не повинен мати змоги злити червоний PR;
  • моніторинг після деплою - швидко помітити регресію й відкотити.

Dependabot чи Renovate: Dependabot вбудований у GitHub і простіший у налаштуванні (групи, cooldown, ігнорування є й у ньому); Renovate виграє на складних правилах, монорепозиторіях і поза GitHub (GitLab, Bitbucket).

Докладніше в документації: Renovate: параметри конфігурації