CI for Dotnet project with GitHub Action
Tags: dotnet, ukrainian, github, tutorial
Published: 2021-05-18 17:59:00

Я розкажу як можна автоматизувати роботу використовуючи можливості GitHub.

## Implement CI for dotnet
Для створення СI ми будемо використовувати функціональність [GitHub Actions](https://docs.github.com/en/actions/guides/building-and-testing-net). GitHub actions - надає можливість автоматизації процесів розробки. GitHub Actions оперує концепцією workflow, що є по-суті одиницею автоматизації. Workflow запускається подіями (push, pull_request, cron та інші). GitHub Actions, як і більшість CI систем використовує `.yml` синтаксис для опису одиниць автоматизації. Є два способи як можна створити workflow: 
* Вручну
* Безпосередньо з GitHub

Для створення CI необхідно вибрати свій репозиторій в GitHub перейти на вкладку **Actions** далі **New workflow** і обрати **NET**. Після цього залишилось тільки зробити **commit** обраного файлу.
```
name: .NET

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.x
    - name: Restore dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --no-restore
    - name: Test
      run: dotnet test --no-build --verbosity normal
```
Описана автоматизація запускається кожного разу коли ми робимо *pull_request* чи *push* в гілку *master* і збирає наш проект потім запускає тести. Щоб трохи покращити наш процес GitHub має функцію **Branch protection rule**, яка дозволяє налаштувати правила для наших pull_request. Для нас є цікавою опція **Require status checks to pass before merging**, за допомогою якої можна заборонити **merge** коли наш **build workflow** виконався неуспішно.

![check](https://drive.google.com/uc?id=1ZiB2g5YuSajrVpjzwOCrpdvUK0p170YK)


Якщо нам необхідно додати нову автоматизацію, то вона має знаходитися в папці `.github\workflows`. І нашою вишенькою буде `badge` який ми можемо додати до нашого репозиторія про результат виконання. Для цього необхідно  перейти на вкладку **Actions** вибрати наш Action - **Build** та в меню обрати **Create status badge**.
>В загальному досить зручний інструмент, зіткнувся тільки з однією проблемою, я створював тестовий `workflows`, який я потім видалив з репозиторія, але він залишився у вкладці *Actions*, щоб видалити його звідти необхідно видалити всі запуски цієї одиниці автоматизації.

## Add Sonarсloud
Sonarсloud - статичний аналізатор коду, що надає інформацію про покриття тестами, дублювання, підтримуваність та безпеку коду. Він має хорошу інтеграцію з GitHub. Для того щоб додати наш репозиторій до *Sonarсloud* необхідно зробити наступні кроки:
1. Логуємось в [https://sonarcloud.io/](https://sonarcloud.io/) використовуючи *GitHub* і обираємо *репозиторій* який ми хочемо проаналізувати.
![sonar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uozxkh9ebp6ija1r7vq1.PNG)
2. Вибираємо CI *GitHab Actions* і записуємо `SONAR_TOKEN` в *GitHub Secrets*
![secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j7d1ti8bnjcsy9254p0i.PNG)
3. Далі вибираємо `NET` і копіюємо згенерований `.yml` до нашого репозиторію. Необхідно тільки замінити `<your clean build command>` на наші команди для побудови `dotnet build`.

Аналізатор коду готовий до роботи і можна використовувати `badge` для метрик. Щоб додати покриття тестами необхідно згенерувати результат запуску тестів в зрозумілому для *sonarcloud* форматі це наприклад **opencover**. Для цього необхідно додати конфігурацію для opencover
```
run: |
  .\.sonar\scanner\dotnet-sonarscanner begin /k:"ohalay_TestSonarCloud" /o:"ohalay" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.language="cs" /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml"
  dotnet build
  dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
  .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
```

> Даний інструмент хороший тим, що дозволяє знайти проблеми в коді, що вже в нашому репозиторії та в коді, який ще не потрапив в наш репозиторій (*pull_request*). Також працюючи з sonarcloud наткнувся на проблему, коли тести виконуються не успішно, а сам запуск одиниці автоматизації залишається успішним, щоб це виправити необхідно перенести запуск тестів в окремий крок.

## Add Dependabot
[Dependabot](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically) - функціонал GitHub який автоматично моніторить і оновлює версії пакетів створюючи *pull_request* для цього. Щоб додати цей функціонал до реопзиторія необхідно перейти на вкладку **Settings** вибрати **Security & analysis** і ввімкнути **Dependabot security updates**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rq4l9tkq3172eookkeym.PNG)

Після цього залишається тільки додати файл конфігурації до нашого репозиторія `.github/dependabot.yml`
```
version: 2
updates:
  
  - package-ecosystem: "github-actions"
    directory: "/"
    target-branch: "master"
    schedule:
      interval: "daily"

  - package-ecosystem: "nuget"
    directory: "/"
    target-branch: "master"
    schedule:
      interval: "daily"
```
> Зараз в нашому репозиторію версії `nuget` та `github-actions` пакетів оновлюються автоматично. Важливо є те, що `dependabot.yml` обов'язково має знаходитись в *master* гілці. І щоб *dependabot* мав доступ завантажувати результати аналізу в *sonatcloud*, то необхідно додати йому `SONAR_TOKEN`. Також зіткнувся з проблемою, коли хотів налаштувати автоматичне оновлення пакетів лиши *minor* версії, поки цей [функціонал ще в розробці](https://github.com/dependabot/dependabot-core/issues/2219) єдиний варіант для кожного пакету вказати максимальну версію до якої можна оновлювати.

## Add CodeQL
[CodeQL](https://securitylab.github.com/tools/codeql/) - семантичний аналізатор коду, який дозволяє виявити потенційні вразливості в коді. Додати його можна перейшовши на вкладку **Security** вибрати **Code scanning alerts** і додати одиницю автоматизації для цього.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ktardzyeabcuupadvg3a.PNG)
Конфігурація виглядає наступним чином
```
name: CodeQL

on:
  push:
    branches: [master]
  pull_request:
    branches: [master]

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2

    - uses: github/codeql-action/init@v1
      with:
        languages: csharp
    - uses: github/codeql-action/autobuild@v1
    - uses: github/codeql-action/analyze@v1
```

## Summary
Перевагою такого підходу є те що всі налаштування знаходяться в одному місці, в нашому репозиторії і таким чином їх зручно підтримувати.
Трохи про ціни:
* GitHub Actions - 2000 безкоштовних хвилин в місяць для приватних репозиторіїв з *free GitHub plan* (публічні безкоштовно)
* SonarCloud -  безкоштовний для публічних проектів
* Dependabot - безкоштовний, зараз є частиною GitHub
* CodeQL - використовує хвилини з GitHub Actions

Приклад GitHub проекту, який містить описану вище конфігурацію 
{% github ohalay/TestSonarCloud %}