Розробка

Застосування розширюваних політик Pull Request в VSTS для підтримки процесу розробки

Часто в рамках перевірки Pull Request, крім, власне, code review, виникає необхідність проробляти набір рутинних перевірок. Деякі перевірки можуть стосуватися оформлення PR. Інші — перевіряти суміжні умови, які складають основу процесу прийняття змін.
Якщо рутинні перевірки не автоматизовані, людина може почати їх забувати або обходити. Тому, що рутина — це нудно.

Visual Studio Team Services пропонує досить зручну інфраструктуру для обробки Pull Request. Сюди входять настроювані політики merge builds, призначення ревьюеров, правила злиття прийнятих змін. Все це доповненої зручною системою обговорення та коментування коду.

Найпотужнішим інструментом розширення процесу Pull Request є зовнішні підключені політики.

Про їх створення та використання і поговоримо (і подивимося код)

 

Розширювані статуси Pull Request

 

Статуси PR це прапори, визначені в рамках контексту (context). Контекст — унікальне поєднання імені (name) контексту і “жанру” (genre). Жанр, як правило, один на додаток, манипулирующий статусом.

 

Наприклад:

 

Статус 1: genre = ‘my-policy’, name = ‘check-1’
Статус 2: genre = ‘my-policy’, name = ‘check-2’

 

Статус приймає одне з попередньо встановлених значень. Для цілей підтримки процесу нас будуть цікавити два: succeeded і failed.

 

Статуси можна використовувати при налаштуванні політики бранчу: вибрані прапори повинні мати статус succeeded, для того щоб PR був прийнятий. Рівно так само реалізовані вбудовані політики, перевіряльники кількість аппруверов, наявність прикріпленого тікета, ітд.

 

Переходимо до редагування політики гілки. Додаємо зовнішню політику.

 

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

 

 

Додані статуси будуть видні на сторінці пулл-реквеста в блоці перевірок:

Якщо статус не використовується як політики — його значення виводиться на сторінці в розділі Status. Якщо статус вказаний як політики — він буде видний у верхній частині блоку.

 

Програмне управління статусами

 

Статусами PR можна маніпулювати програмно, використовуючи REST API. Таким чином, можна реалізувати додаткові перевірки PR і транслювати їх результат безпосередньо у процес прийняття змін.

 

Нове значення статусу додається методом Create. Крім результату і контексту, необхідно передати текст, який побачить користувач. Додатково можна передати URL: в цьому випадку мітка статусу на формі PR стане посиланням і користувач зможе перейти на сторінку з деталями статусу.

 

Виклик методу призводить до створення нового запису статусів PR. У межах одного контексту останній доданий значення статусу вважається активним. Більш ранні записи статусу не видно інтерфейсу UI, але їх можна отримати за допомогою методу List.

 

Для стану статусів на попередньому зображенні реальний список статусів для пулл-реквеста може бути таким:

 

{
 "value": [
{
 "id": 1,
 "state": "failed",
 "description": "PR title format",
 "контекст": "@{name=check-title; genre=my-pr-policy}",
 "creationDate": "2018-11-06T18:35:57.0324172 Z",
 "updatedDate": "2018-11-06T18:35:57.0324172 Z",
 "createdBy": "Masked value",
 "targetUrl": null
},
{
 "id": 2,
 "state": "failed",
 "description": "Build for last update",
 "контекст": "@{name=check-build; genre=my-pr-policy}",
 "creationDate": "2018-11-06T18:35:57.5167963 Z",
 "updatedDate": "2018-11-06T18:35:57.5167963 Z",
 "createdBy": "Masked value",
 "targetUrl": null
},
{
 "id": 3,
 "state": "succeeded",
 "description": "No offset from develop",
 "контекст": "@{name=check-offset; genre=my-pr-policy}",
 "creationDate": "2018-11-06T18:35:57.782379 Z",
 "updatedDate": "2018-11-06T18:35:57.782379 Z",
 "createdBy": "Masked value",
 "targetUrl": null
},
{
 "id": 4,
 "state": "succeeded",
 "description": "PR title format",
 "контекст": "@{name=check-title; genre=my-pr-policy}",
 "creationDate": "2018-11-06T18:46:37.2627154 Z",
 "updatedDate": "2018-11-06T18:46:37.2627154 Z",
 "createdBy": "Masked value",
 "targetUrl": null
},
{
 "id": 5,
 "state": "succeeded",
 "description": "Build for last update",
 "контекст": "@{name=check-build; genre=my-pr-policy}",
 "creationDate": "2018-11-06T18:51:33.7920543 Z",
 "updatedDate": "2018-11-06T18:51:33.7920543 Z",
 "createdBy": "Masked value",
 "targetUrl": null
},
{
 "id": 6,
 "state": "failed",
 "description": "PR title format",
 "контекст": "@{name=check-title; genre=my-pr-policy}",
 "creationDate": "2018-11-06T18:53:44.3075889 Z",
 "updatedDate": "2018-11-06T18:53:44.3075889 Z",
 "createdBy": "Masked value",
 "targetUrl": null
},
{
 "id": 7,
 "state": "failed",
 "description": "Title format is not correct",
 "контекст": "@{name=check-title; genre=my-pr-policy}",
 "creationDate": "2018-11-06T19:26:11.3019433 Z",
 "updatedDate": "2018-11-06T19:26:11.3019433 Z",
 "createdBy": "Masked value",
 "targetUrl": null
}
],
 "count": 7
}

 

Подивившись список поточних статусів, можна оновити обраний по індексу в списку. Для цього служить метод Update.

 

Нарешті, записи про статуси можна видалити методом Delete.

 

Історія зміни статусів PR може виявитися корисною — для подальшого аналізу. Тому у нас використовується наступний метод зміни статусів:

 

  • Знайти останню за часом запис статусу того ж контексту
  • Якщо у неї ті ж значення результату, опису і посилання, що ми хочемо додати — нічого не робимо
  • Інакше, додаємо нову запис статусу.
    Це дозволяє зберігати історію змін, не сильно роздуваючи її.

 

function Set-PullRequestStatus {
param(
 [Parameter (Mandatory = $true)]
 [string] $pullRequestId,

 [Parameter (Mandatory = $true)]
 [string] $state,

 [Parameter (Mandatory = $true)]
 [string] $description,

 [Parameter (Mandatory = $true)]
 [string] $contextName,

 [Parameter (Mandatory = $false)]
 [string] $contextGenre,

 [Parameter (Mandatory = $false)]
 [string] $targetUrl,

 [Parameter (Mandatory = $true)]
 [object] $context
) 

 $b = @{
 state = $state;
 description = $description;
 context = @{
 name = $contextName;
 genre = $contextGenre;
};
 targetUrl = $targetUrl;
}

 $body = ConvertTo-Json $b

#
 # Get current list of statuses
#

 $endpoint = (Get-ProjectBaseURL) + "/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/statuses?api-version=4.1-preview.1"
 $res = Get-AzureRequestReqults -URI $endpoint -context ($context + @{pullRequestId = $pullRequestId})

#
 # Try to find a status for a given context genre and name. Start looking from the last one. If found - check if it has same values.
#

 $i = $res.count
 $foundSameStatus = $false

 while ($i -GT 0) {
 $r = $res.value[$i-1]

 if (($r.context.name -EQ $contextName) -AND ($r.context.genre -EQ $contextGenre)) {
 $foundSameStatus = ($r.state -EQ $state) -AND ($r.description -EQ $description) -AND ($r.targetUrl -EQ $targetUrl)

break
}
$i--
}

 $res = $r

#
 # If same status / values was not found - add new record.
#

 if (-not $foundSameStatus) {
 $endpoint = (Get-ProjectBaseURL) + "/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/statuses?api-version=4.1-preview.1"

 $res = Get-AzureRequestReqults -URI $endpoint -context ($context + @{pullRequestId = $pullRequestId}) -method POST -query $body
}

 return @{status = $res; status_changed = $(-not $foundSameStatus)}
}

 

Практичне застосування

 

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

 

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

 

  1. Всі PRы повинні бути оформлені в єдиному стилі. Тому перший контроль який був створений — оформлення заголовка PR. Ми перевіряємо його на відповідність регулярному виразу.
  2. Гілка PR не повинен відставати від гілки куди її вливають (проведена інтеграція).
  3. Якщо в рамках PR змінюються файли проекту БД, то також повинні бути файли автотестів.
  4. Існує успішна збірка гілки останнього зміни.
  5. Така збірка успішно выкачена на тестовий стенд і автотесты пройшли.

Перераховані умови можна перевіряти вручну. Ми так раніше і робили. Але це рутина, і її замінили автоматизованим процесом. Тепер ми можемо доповнювати і змінювати набір перевірок — він буде завжди інтегрований у процес, завжди виконуватися.

Додатковий величезний плюс політик — вони прозорі для всіх, і завжди на виду — на сторінці PR. Про них більше не потрібно нагадувати.
Крім того, політик, які не пройшли перевірку, ми показуємо посилання на сторінки нашого Wiki з описом політики та очікуваних дій.

 

Технічна реалізація виконання політик

 

На даний момент логіка політик реалізована у вигляді скриптів powershell. За рахунок високорівневих командлетів і хорошою об’єктної моделі даних, код скрипта виходить дуже компактним. А можливість запускати скрипт по кроках інтерактивно і колупатися в змінних — суттєво спрощує процес розробки та налагодження.

До речі, після випуску powershell core — скрипти можна виконувати і на інших платформах (перевіряв на MacOS).

Запускаємо скрипти в спеціальному релізі VSTS за розкладом приблизно раз в пару годин. Може бути почнемо запускати через шедулер частіше.

Такий підхід, звичайно, дає істотно більш повільну реакцію, ніж якщо реалізувати те ж саме у вигляді Azure Function і пов’язати її з VSTS через web hook. Але для нас було важливіше реалізувати перевірки і подивитися як це буде працювати в процесі (MVP). Оперативність виконання перевірок поки не важлива. Це буде на наступному етапі.

Реалізація бібліотек роботи з VSTS REST API, які використані у перевірках, плюс невеликий приклад, що реалізує деякі з вказаних політик можна подивитися в репозиторії на GitHub. Сподіваюся, кому-небудь виявиться корисним.

Related Articles

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Close