Вітаю, колеги! Мене звати Назарій Мошенський. Я Senior Software Engineer у компанії Grid Dynamics. За більш ніж 5 років у цій сфері я працював на проєктах різного розміру і складності, проте найбільше мене захоплювали задачі з автоматизації процесів. Ця стаття буде цікавою для розробників, які хочуть покращити швидкість написання коду, його якість, а також формалізувати процеси і стандарти.

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

Більшість з цих речей можна автоматизувати, і далі ми поговоримо, як це зробити. Сенс статті — показати не набір команд, які можуть автоматизувати будь-який проект, а набір інструментів, які ви можете налаштувати під свої потреби.

Як має виглядати pull request

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

  1. Невеликий розмір

    Погодьтесь, що перевіряти дуже великі пул-реквести досить важко. Особливо, коли ви не до кінця володієте контекстом задачі. Зі збільшенням кількості змін в пул-реквесті зберігати фокус і тримати все в голові стає все важче. Тобто розмір пул-реквеста треба якось обмежити.

  2. Статичний аналізатор коду не видає помилок

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

  3. Видно контекст задачі

    Ми хочемо бачити, над яким тікетом проводилась робота і що саме було зроблено. Для цього було б зручно, якби ми могли фейлити пайплайн, якщо PR не має опису, в заголовку додавати номер тікета і його тайтл. В ідеалі ще додати скріншоти, щоб було наочно видно зміни.

  4. “Зелені” тести

    Ми покриваємо код тестами і якщо зміни в коді “ламають” тести, то, очевидно, що такий PR поки не готовий до рев’ю, бо всі тести мають успішно проходити. Тому було б зручно унеможливити мердж такого коду.

  5. Зрозумілі меседжі комітів

    Коли ми знайомимось з пул-реквестом, то дуже легко відстежити послідовність дій автора, коли він створює атомарні коміти, додаючи до них зрозумілі меседжі. В ідеалі треба нав’язати стиль написання таких меседжів.

  6. Автоматичне визначення рев’юерів

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

  7. Pull request template (checklist)

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

Для всього цього є багато інструментів, які спростять нам життя і автоматизують це все.

Danger

Для автоматизації рутинних задач в процесі СІ ми використовуємо Danger. Він досить легко налаштовується і має багато плагінів, які вміють працювати з популярними інструментами. Також ви можете писати власні за потреби. Ось, наприклад, набір готових плагінів для ознайомлення — https://danger.systems/ruby/


Danger можна інтегрувати, наприклад, в GitHub Actions і видавати результати його роботи в pull request за допомогою бота.

Взагалі Danger — це інструмент, який заслуговує окремої серії статей. Ми розберемо базові можливості для розуміння, що можна з ним робити. Для детальнішого розбору можливостей раджу зазирнути на офіційний сайт — https://danger.systems/reference.html або безпосередньо в документацію до плагінів, які ви для нього обрали. 

Наприклад, вирішимо декілька проблем, які ми описували вище:

  1. Порожній description пул-реквеста

  2. Занадто великий пул-реквест

  3. Тести “падають”

  4. 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, то хороша новина в тому, що є готовий плагін, який буде підсвічувати вам помилки в структурі та форматуванні коміт меседжів.

Conventional Commits Plugin
Приклад роботи плагіна

Також у нього є зручний Wizard, в якому ви просто вводите значення в поля вводу і він форматує код за вас.

Conventional Commits Wizards

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. Надіюсь, вам було корисно. Буду радий прочитати в коментарях, що ви використовуєте на своїх проєктах, що працює для вас, а що ні.

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Назар Мошенський
Назар Мошенський@moshenskyi_n

55Прочитань
0Автори
5Читачі
На Друкарні з 15 квітня

Вам також сподобається

  • Навчити — найкраще що я можу зробити як керівник

    Як керівник компанії я проходив декілька етапів розвитку щодо відношення до навчання співробітників: від "ви повинні самі" до розробки власних курсів та регулярних семінарів для співробітників.

    Теми цього довгочиту:

    Навчання
  • Android застосунок прямо з бліндажа. Пролог

    До повномасштабного вторгнення я спокійно працював Web-розробником. Створював сайти на WordPress, Vercel, інтернет-магазини на Magento 2 і постійно освоював якісь нові web-сервіси з метою покращення своєї роботи.

    Теми цього довгочиту:

    Android Development
  • Шпаргалка по Kotlin ч. 1 Колекції

    Колеції та їх функції, які мало хто використовує, але можуть бути дуже корисними в написанні коду.

    Теми цього довгочиту:

    Kotlin

Коментарі (4)

Pull request template (checklist)

уявляю як ця штука дістане розробників за тиждень-два, особливо коли спрінт складається з маленьких багів

Вам також сподобається

  • Навчити — найкраще що я можу зробити як керівник

    Як керівник компанії я проходив декілька етапів розвитку щодо відношення до навчання співробітників: від "ви повинні самі" до розробки власних курсів та регулярних семінарів для співробітників.

    Теми цього довгочиту:

    Навчання
  • Android застосунок прямо з бліндажа. Пролог

    До повномасштабного вторгнення я спокійно працював Web-розробником. Створював сайти на WordPress, Vercel, інтернет-магазини на Magento 2 і постійно освоював якісь нові web-сервіси з метою покращення своєї роботи.

    Теми цього довгочиту:

    Android Development
  • Шпаргалка по Kotlin ч. 1 Колекції

    Колеції та їх функції, які мало хто використовує, але можуть бути дуже корисними в написанні коду.

    Теми цього довгочиту:

    Kotlin