Автоматизація перевірки pull requests
Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.
Вітаю, колеги! Мене звати Назарій Мошенський. Я Android Software Engineer у компанії Grid Dynamics. За більш ніж 5 років у цій сфері я працював на проєктах різного розміру і складності, проте найбільше мене захоплювали задачі з автоматизації процесів. Ця стаття буде цікавою для розробників, які хочуть покращити швидкість написання коду, його якість, а також формалізувати процеси і стандарти.
Перевірка пул-реквестів — це одна з наймарудніших робіт у розробці програмного забезпечення. Під час створення нового функціоналу може виникати багато побічних нюансів: описки, новий код міг щось зламати в решті кодової бази, з’явились невикористані ресурси після рефакторингу тощо.
Більшість з цих речей можна автоматизувати, і далі ми поговоримо, як це зробити. Сенс статті — показати не набір команд, які можуть автоматизувати будь-який проєкт, а набір інструментів, які ви можете налаштувати під свої потреби.
Як має виглядати pull request
На мою думку, ідеальний пул-реквест має невеликий набір характеристик:
- Невеликий розмір. Погодьтесь, що перевіряти дуже великі пул-реквести досить важко. Особливо, коли ви не до кінця володієте контекстом задачі. Зі збільшенням кількості змін в пул-реквесті зберігати фокус і тримати все в голові стає все важче. Тобто розмір пул-реквеста треба якось обмежити.
- Статичний аналізатор коду не видає помилок. Ніхто не хоче перевіряти мінорні помилки чи помилки, які легко можна проаналізувати готовими інструментами. В Android ми можемо писати власні правила перевірки коду і використовувати вже готові, налаштовувати стиль написання коду і перевіряти, як він підтримується тощо. Тому треба якось вивести ці помилки в пул-реквесті. Якщо ми бачимо нові помилки, то перевіряти його поки немає сенсу.
- Видно контекст задачі. Ми хочемо бачити, над яким тікетом проводилась робота і що саме було зроблено. Для цього було б зручно, якби ми могли фейлити пайплайн, якщо PR не має опису, в заголовку додавати номер тікета і його тайтл. В ідеалі ще додати скріншоти, щоб було наочно видно зміни.
- «Зелені» тести. Ми покриваємо код тестами і якщо зміни в коді «ламають» тести, то очевидно, що такий PR поки не готовий до рев’ю, бо всі тести мають успішно проходити. Тому було б зручно унеможливити мердж такого коду.
- Зрозумілі меседжі комітів. Коли ми знайомимось з пул-реквестом, то дуже легко відстежити послідовність дій автора, коли він створює атомарні коміти, додаючи до них зрозумілі меседжі. В ідеалі треба нав’язати стиль написання таких меседжів.
- Автоматичне визначення рев’юерів. Якщо у вас великий проєкт, на якому працює декілька команд, то було б зручно ділити код на зони відповідальності. Якщо ви робите зміни в репозиторії сусідньої команди, то було б зручно, якби неможливо було змерджити їх без апрува цієї команди.
- Pull request template (checklist). Перед тим, як передавати пул-реквест на перевірку, автор повинен переконатись, що він нічого не забув і виконав всі необхідні підготовчі дії. Для цього зручно мати список з чекбоксами, де автор повинен відмітити дії, які він виконав. Тоді рев’юер буде бачити, що пул-реквест дійсно готовий до перевірки.
Для всього цього є багато інструментів, які спростять нам життя і автоматизують це все.
Danger
Для автоматизації рутинних задач в процесі СІ ми використовуємо Danger. Він досить легко налаштовується і має багато плагінів, які вміють працювати з популярними інструментами. Також ви можете писати власні за потреби. Ось, наприклад, набір готових плагінів для ознайомлення — danger.systems/ruby.
Danger можна інтегрувати, наприклад, в GitHub Actions і видавати результати його роботи в pull request за допомогою бота.
Взагалі Danger — це інструмент, який заслуговує окремої серії статей. Ми розберемо базові можливості для розуміння, що можна з ним робити. Для детальнішого розбору можливостей раджу зазирнути на офіційний сайт або безпосередньо в документацію до плагінів, які ви для нього обрали.
Наприклад, вирішимо декілька проблем, які ми описували вище:
- Порожній description пул-реквеста.
- Занадто великий пул-реквест.
- Тести «падають».
- Lint warnings не видно.
Ми використовуємо danger/ruby. Два основних файли, які нам потрібні,— це Gemfile і Dangerfile.
В Gemfile ви додаєте danger-плагіни, які ви плануєте використовувати, наприклад, danger-junit, danger-android_lint тощо.
Виглядати він буде приблизно таким чином:
source 'https://rubygems.org'
gem 'danger'
gem 'danger-junit'
gem 'danger-android_lint'
gem 'danger-checkstyle_format'
В Dangerfile ви пишете скрипт за допомогою Ruby DSL, використовуючи Danger-плагіни, які ви вказали в Gemfile.
Ось так в декілька рядків в Dangerfile ми можемо перевірити, чи пул реквест не занадто великий і чи є в нього опис.
Щоб перевірити результати тестів, я встановлюю Danger-плагін JUnit. Він парсить звіти виконання юніт-тестів. Ви можете їх відформатувати, як вам зручно, і вивести у ваш pull request.
У моєму проєкті декілька модулів, тому я створюю патерн обходу директорій і використовую методи parse & report:
Насправді це найбазовіший конфіг. Власне, сам плагін здатний на значно більше.
Також ми можемо налаштувати Android Lint, який проаналізує код і виведе результати.
Так буде виглядати результат:
Це знову ж таки зроблено з мінімальними конфігураціями і без нічого особливого:
Ідея така ж: запускаємо Lint, він кладе результати своїх спостережень в папку, яку ми потім парсимо та відображаємо результати за допомогою Danger.
Ми можемо легко інтегрувати Danger з GitHub Actions, створивши подібний workflow:
name: Test,Verify,Report
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
pull_request:
types: [synchronize, opened, reopened, labeled, unlabeled, edited]
jobs:
danger:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/[email protected]
- name: Set up Java SDK
uses: actions/[email protected]
with: {java-version: 1.8}
- uses: ruby/[email protected]
with:
ruby-version: '3.0'
- uses: actions/[email protected]
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('Gemfile.lock') }} # change your gemfile path
restore-keys: |
${{ runner.os }}-gems-
- name: Run unit tests
run: ./gradlew test
- name: Run linter
run: ./gradlew runChecksForDanger
- name: danger
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bundle install
bundle exec danger
Перед запуском Danger я запускаю команди ./gradlew test та ./gradlew runChecksForDanger. Перша генерує звіти юніт-тестів, а друга звіти лінтера. Вони будуть потрібні для того, щоб Danger їх розпарсив і вивів результати на екран.
Також потрібно додати токен, щоб бот міг виводити результати роботи Danger в ваш PR.
Commit messages
Щоб коміт-меседжі легко читались і було зрозуміло, що там було зроблено, пропоную розглянути ідею Conventional Commits.
Це ідеологія, яка формалізує структуру коміт меседжів. Детальніше про неї можна прочитати тут. Її сенс полягає в тому, щоб у кожному коміті було вказано тип коміта, скоуп, до якого він відноситься, короткий опис. Також для читабельності визначені і вимоги до форматування таких меседжів.
Якщо ви, як і я, користуєтесь IntelliJ IDEA чи Android Studio, то хороша новина в тому, що є готовий плагін, який буде підсвічувати вам помилки в структурі та форматуванні коміт меседжів.
![]() |
Також у нього є зручний Wizard, в якому ви просто вводите значення в поля вводу і він форматує код за вас.
Branch protection rules
Це дуже важлива частина PR, де ви можете налаштувати правила для різних гілок у вашому git-репозиторії. А особливо нас будуть цікавити налаштування, які стосуються пул-реквестів.
Code Owners
Якщо у вас досить великий проєкт і над ним одночасно працюють декілька команд, то скоріш за все, кожна з них має свою зону відповідальності. Щоб рев’ю обов’язково робили люди, які відповідають за компонент, в який ви вносите зміни, пропоную налаштувати Code Owners.
Це файл, в якому прописані користувачі чи групи користувачів, які відповідають за окремі частини системи. Також в branch protection rules ви можете встановити обов’язкову перевірку коду Code Owner’ами:
Ми можемо встановити, наприклад, 2 обов’язкових апруви і поставити галочку навпроти Require review from Code Owners.
Pull request template (checklist)
Перед тим, як дивитись PR, було б зручно, щоб його автор спочатку перевірив сам себе. Для цього потрібно скласти список підготовчих дій, які повинен виконати кожен, хто створює пул-реквест. В гуглі можна знайти безліч прикладів таких чеклістів. Вам треба обрати, що саме важливо для вас і вашого проєкту і внести це в список. Ось, наприклад, список з мого демо-проєкту:
Шаблон такого чекліста пишеться за допомогою Markdown. Ось приклад мого:
## Checklist:
### Code quality
- [ ] My code follows the style guidelines of this project
- [ ] My changes generate no new warnings
- [ ] I have commented my code, particularly in hard-to-understand areas
### Testing
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] I have performed a self-review of my own code
### Merge conflicts
- [ ] Any dependent changes have been merged and published in downstream modules
Ви можете встановити застосунок з маркетплейсу, який буде перевіряти, чи всі галочки проставлені, і відображати статус заповнення чек-листа. У разі невиконання всіх умов, змінить її колір з зеленого на сірий:
Як тільки всі умови з чек-листа будуть виконані, кнопка стане зеленою:
Насправді це не обов’язково повинен бути чек-лист. Часто використовують шаблони формату «питання-відповідь» або змішаного:
Ось такі невеличкі поради щодо автоматизації перевірки pull requests. Сподіваюсь, вам було корисно. Буду радий прочитати в коментарях, що ви використовуєте на своїх проєктах, що працює для вас, а що ні.
6 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів