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

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

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

5 питань

Хуки - виконувані скрипти, які 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