Вітаю, колеги! Мене звати Назарій Мошенський. Я Senior Software Engineer у компанії Grid Dynamics. За більш ніж 5 років у цій сфері я працював на проєктах різного розміру і складності, проте найбільше мене захоплювали задачі з автоматизації процесів. Ця стаття буде цікавою для розробників, які хочуть покращити швидкість написання коду, його якість, а також формалізувати процеси і стандарти.
Перевірка пул-реквестів — це одна з найбільш марудних робіт у розробці програмного забезпечення. Під час створення нового функціоналу може виникати багато побічних нюансів: описки, новий код міг щось зламати в решті кодової бази, з’явились невикористані ресурси після рефакторингу тощо.
Більшість з цих речей можна автоматизувати, і далі ми поговоримо, як це зробити. Сенс статті — показати не набір команд, які можуть автоматизувати будь-який проект, а набір інструментів, які ви можете налаштувати під свої потреби.
Як має виглядати pull request
На мою думку, ідеальний пул-реквест має невеликий набір характеристик:
Невеликий розмір
Погодьтесь, що перевіряти дуже великі пул-реквести досить важко. Особливо, коли ви не до кінця володієте контекстом задачі. Зі збільшенням кількості змін в пул-реквесті зберігати фокус і тримати все в голові стає все важче. Тобто розмір пул-реквеста треба якось обмежити.Статичний аналізатор коду не видає помилок
Ніхто не хоче перевіряти мінорні помилки чи помилки, які легко можна проаналізувати готовими інструментами. В Android ми можемо писати власні правила перевірки коду і використовувати вже готові, налаштовувати стиль написання коду і перевіряти, як він підтримується тощо. Тому треба якось вивести ці помилки в пул-реквесті. Якщо ми бачимо нові помилки, то перевіряти його поки немає сенсу.Видно контекст задачі
Ми хочемо бачити, над яким тікетом проводилась робота і що саме було зроблено. Для цього було б зручно, якби ми могли фейлити пайплайн, якщо PR не має опису, в заголовку додавати номер тікета і його тайтл. В ідеалі ще додати скріншоти, щоб було наочно видно зміни.“Зелені” тести
Ми покриваємо код тестами і якщо зміни в коді “ламають” тести, то, очевидно, що такий PR поки не готовий до рев’ю, бо всі тести мають успішно проходити. Тому було б зручно унеможливити мердж такого коду.Зрозумілі меседжі комітів
Коли ми знайомимось з пул-реквестом, то дуже легко відстежити послідовність дій автора, коли він створює атомарні коміти, додаючи до них зрозумілі меседжі. В ідеалі треба нав’язати стиль написання таких меседжів.Автоматичне визначення рев’юерів
Якщо у вас великий проєкт, на якому працює декілька команд, то було б зручно ділити код на зони відповідальності. Якщо ви робите зміни в репозиторії сусідньої команди, то було б зручно, якби неможливо було змерджити їх без апрува цієї команди.Pull request template (checklist)
Перед тим, як передавати пул-реквест на перевірку, автор повинен переконатись, що він нічого не забув і виконав всі необхідні підготовчі дії. Для цього зручно мати список з чекбоксами, де автор повинен відмітити дії, які він виконав. Тоді рев’юер буде бачити, що пул-реквест дійсно готовий до перевірки.
Для всього цього є багато інструментів, які спростять нам життя і автоматизують це все.
Danger
Для автоматизації рутинних задач в процесі СІ ми використовуємо Danger. Він досить легко налаштовується і має багато плагінів, які вміють працювати з популярними інструментами. Також ви можете писати власні за потреби. Ось, наприклад, набір готових плагінів для ознайомлення — https://danger.systems/ruby/.
Danger можна інтегрувати, наприклад, в GitHub Actions і видавати результати його роботи в pull request за допомогою бота.
Взагалі Danger — це інструмент, який заслуговує окремої серії статей. Ми розберемо базові можливості для розуміння, що можна з ним робити. Для детальнішого розбору можливостей раджу зазирнути на офіційний сайт — https://danger.systems/reference.html або безпосередньо в документацію до плагінів, які ви для нього обрали.
Наприклад, вирішимо декілька проблем, які ми описували вище:
Порожній 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 ми можемо перевірити чи пул реквест не занадто великий і чи є в нього опис.
warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]"
# Warn when there is a big PR
warn("Big PR") if git.lines_of_code > 500
if github.pr_body.length < 5
fail "Please provide a summary in the Pull Request description"
end
Щоб перевірити результати тестів, я встановлюю Danger-плагін JUnit. Він парсить звіти виконання юніт-тестів. Ви можете їх відформатувати, як вам зручно і вивести в ваш pull request.
У моєму проєкті декілька модулів, тому я створюю патерн обходу директорій і використовую методи parse & report:
###########################################
# JUnit #
###########################################
junit_tests_dir = "**/build/test-results/**/*.xml"
Dir[junit_tests_dir].each do |file_name|
junit.parse file_name
junit.report
end
Насправді це найбазовіший конфіг. Власне, сам плагін здатний на значно більше.
Також ми можемо налаштувати Android Lint, який проаналізує код і виведе результати.
Так буде виглядати результат:
Це знову ж таки зроблено з мінімальними конфігураціями і без нічого особливого:
###########################################
# LINT #
###########################################
lint_dir = "**/build/reports/*.xml"
Dir[lint_dir].each do |file_name|
android_lint.skip_gradle_task = true
android_lint.filtering = false
android_lint.report_file = file_name
android_lint.lint
end
Ідея така ж — запускаємо 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/checkout@v2
- name: Set up Java SDK
uses: actions/setup-java@v1
with: {java-version: 1.8}
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.0'
- uses: actions/cache@v1
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.
Це ідеологія, яка формалізує структуру коміт меседжів. Детальніше про неї можна прочитати тут — https://www.conventionalcommits.org/en/v1.0.0/#summary
Її сенс полягає в тому, щоб у кожному коміті було вказано тип коміта, скоуп, до якого він відноситься, короткий опис. Також для читабельності визначені і вимоги до форматування таких меседжів.
Якщо ви, як і я, користуєтесь 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. Надіюсь, вам було корисно. Буду радий прочитати в коментарях, що ви використовуєте на своїх проєктах, що працює для вас, а що ні.