Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
По натуре своей многие разработчики слишком ленивые не любят делать одно и то же действие много раз. Нам проще научить компьютер, чтобы он делал монотонные действия за нас.
Как только кто-либо из нашей команды вносит изменения в код (читай «мерджит feature-ветку в develop»), наш билд-сервер:
- Собирает исходный код и установщик приложения
- проставляет номер сборки, каждый раз увеличивая последнюю цифру. Например, текущая версия нашего ПО 3.3.0.202 – часть 3.3.0 когда-то ввёл разработчик (привет, SemVer), а «202» проставляется в процессе сборки.
- В процессе анализирует качество кода (с использованием SonarQube) – и отправляет отчёт во внутренний SonarQube,
- Сразу после сборки запускает автотесты (xUnit) и анализирует покрытие тестами (OpenCover),
Также, в зависимости от ветки, в которую были внесены изменения, могут быть выполнены:
- отправка сборки (вместе с changelog-ом) в один или несколько телеграм-каналов (иногда удобнее брать сборки оттуда).
- публикация файлов в систему автообновления ПО.
Под катом о том, как мы научили Gitlab CI делать за нас бОльшую часть этой муторной работы.
Оглавление
- Устанавливаем и регистрируем Gitlab Runner.
- Что нужно знать про .gitlab-ci.yml и переменные сборки.
- Активируем режим Developer PowerShell for VS.
- Используем CI для проставления версии во все сборки решения.
- Добавляем отправку данных в SonarQube.
- «Причёсываем» автотесты xUnit + добавляем вычисление покрытия тестами через OpenCover.
- Послесловие.
Перед началом
Чтобы быть уверенными, что написанное ниже работает, мы взяли на github небольшой проект, написанный на WPF и имеющий unit-тесты, и воспроизвели на нём описанные в статье шаги. Самые нетерпеливые могут сразу зайти в созданный на сайте gitlab.com репозиторий и посмотреть, как это выглядит.
Устанавливаем и регистрируем Gitlab Runner
Для того чтобы Gitlab CI мог что-либо собрать, сначала установите и настройте Gitlab Runner на машине, на которой будет осуществляться сборка. В случае проекта на .Net Framework это будет машина с ОС Windows.
Чтобы настроить Gitlab Runner, выполните следующие шаги:
- Установите Git для Windows с сайта git.
- Установите Visual Studio с сайта Microsoft. Мы поставили себе Build Tools для Visual Studio 2019. Чтобы скачать именно его, разверните список Инструменты для Visual Studio 2019.
- Создайте папку C:\GitLab-Runner и сохраните в неё программу gitlab runner. Скачать её можно со страницы [документации Gitlab] (https://docs.gitlab.com/runner/install/windows.html) — ссылки скрыты прямо в тексте: «Download the binary for x86 or amd64».
- Запустите cmd или powershell в режиме администратора, перейдите в папку C:\GitLab-Runner и запустите скачанный файл с параметром install (Gitlab runner установится как системная служба).
.\gitlab-runner.exe install
Посмотрите токен для регистрации Runner-а. В зависимости от того, где будет доступен ваш Runner:
- только в одном проекте — смотрите токен в меню проекта Settings > CI/CD в разделе Runners,
- в группе проектов — смотрите токен в меню группы Settings > CI/CD в разделе Runners,
- для всех проектов Gitlab-а — смотрите токен в секции администрирования, меню Overview > Runners.
Выполните регистрацию Runner-а, с помощью команды
.\gitlab-runner.exe register
Далее надо ввести ответы на вопросы мастера регистрации Runner-а:
- coordinator URL — http или https адрес вашего сервера gitlab;
- gitlab-ci token — введите токен, полученный на предыдущем шаге;
- gitlab-ci description — описание Runner-а, которое будет показываться в интерфейсе Gitlab-а;
- gitlab-ci tags — через запятую введите тэги для Runner-а. Если вы не знакомы с этим механизмом, оставьте поле пустым — отредактировать его можно позднее через интерфейс самого gitlab-а. Тэги можно использовать для того, чтобы определённые задачи выполнялись на определённых Runner-ах (например, чтобы настроить сборку ПО на Runner-е, развёрнутом на копьютере ОС Windows, а подготовку документации на Runner-е с ОС Linux);
- enter the executor — ответьте
shell
. На этом шаге указывается оболочка, в которой будут выполняться команды; при указании значения shell под windows выбирается оболочка powershell, а последующие скрипты написаны именно для неё.
Что нужно знать про .gitlab-ci.yml и переменные сборки
В процессе соей работы Gitlab CI берёт инструкции о том, что делать в процессе сборки того или иного репозитория из файла .gitlab-ci.yml
, который следует создать в корне репозитория.
Вбив в поиске содержимое .gitlab-ci.yml
для сборки приложения .NET Framework можно найти несколько шаблонов: 1, 2. Выглядят они примерно так:
variables:
# Максимальное количество параллельно собираемых проектов при сборке решения; зависит от количества ядер ПК, выбранного для сборки
MSBUILD_CONCURRENCY: 4
# Тут куча путей до утилит, которые просто оябзаны лежать там, где ожидается
NUGET_PATH: 'C:\Tools\Nuget\nuget.exe'
MSBUILD_PATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\msbuild.exe'
XUNIT_PATH: 'C:\Tools\xunit.runner.console.2.3.1\xunit.console.exe'
TESTS_OUTPUT_FOLDER_PATH: '.\tests\CiCdExample.Tests\bin\Release\'
# Тут указываются стадии сборки. Указывайте любые названия которые вам нравятся, но по умолчанию используют три стадии: build, test и deploy.
# Стадии выполняются именно в такой последовательности.
stages:
- build
- test
# Далее описываются задачи (job-ы)
build_job:
stage: build # указание, что задача принадлежит этапу build
# tags: windows # если тут указать тэг, задача будет выполняться только на Runner-е с указанным тэгом
only: # для каких сущностей требуется выполнять задачу
- branches
script: # код шага
- '& "$env:NUGET_PATH" restore'
- '& "$env:MSBUILD_PATH" /p:Configuration=Release /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' # сборка; ключ clp:ErrorsOnlyоставляет только вывод ошибок; ключ nr:false завершает инстансы msbuild
artifacts: # где по завершении задачи будут результаты, которые надо сохранить в gitlab (т.н. артефакты) и которые можно будет передать другим задачам по цепочке
expire_in: 2 days # сколько хранить артефакты
paths: # список путей, по которым находятся файлы для сохранения
- '$env:TESTS_OUTPUT_FOLDER_PATH'
test_job:
stage: test
only:
- branches
script:
- '& "$env:XUNIT_PATH" "$env:TESTS_OUTPUT_FOLDER_PATH\CiCdExample.Tests.dll"'
dependencies: # указание, что для запуска этой задачи требуется успешно завершенная задача build_job
- build_job
И последнее: если нам требуется передавать в скрипт значение параметра, который мы не хотим хранить в самом скрипте (например, пароль для подключения куда-либо), мы можем использовать для этого объявление параметров в gitlab. Для этого зайдите в проекте (или в группе проекта) в Settings > CI/CD и найдите раздел Variables. Прописав в нём параметр с именем (key) SAMPLE_PARAMETER, вы сможете получить его значение в в скрипте .gitlab-ci.yml через обращение $env:SAMPLE_PARAMETER.
Также в этом разделе можно настроить передачу введенных параметров только при сборке защищённых веток (галочка Protected) и/или скрытие значения параметра из логов (галочка Masked).
Подробнее о параметрах окружения сборки смотрите в документации к Gitlab CI.
Активируем режим Developer PowerShell for VS
Скрипт, приведённый выше, уже можно использовать для сборки и вызова тестов. Правда, присутствует НО: крайне неудобно прописывать абсолютные пути к разным установкам Visual Studio. К примеру, если на одной билд-машине стоит Visual Studio 2017 BuildTools, а на другой Visual Studio Professional 2019, то такой скрипт будет работать только для одной из двух машин.
К счастью, с версии Visual Studio 2017 появился способ поиска всех инсталляций Visual Studio на компьютере. Для этого существует утилита vswhere, путь к которой не привязан ни к версии Visual Studio, ни к её редакции. А в Visual Studio 2019 (в версии 16.1 или более новой) есть библиотека, которая умеет «трансформировать» консоль Powershell в режим Developer Powershell, в котором уже прописаны пути к утилитам из поставки VS.
Как применить
Дописываем переменную к секции Variables:
variables:
VSWHERE_PATH: '%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe'
Затем создаём новую секцию before_msbuild якорем enter_vsdevshell и следующим текстом:
.before_msbuild: &enter_vsdevshell
before_script:
- '$vsWherePath = [System.Environment]::ExpandEnvironmentVariables($env:VSWHERE_PATH)'
- '& $vsWherePath -latest -format value -property installationPath -products Microsoft.VisualStudio.Product.BuildTools | Tee-Object -Variable visualStudioPath'
- 'Join-Path "$visualStudioPath" "\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" | Import-Module'
- 'Enter-VsDevShell -VsInstallPath:"$visualStudioPath" -SkipAutomaticLocation'
И всюду, где нам надо использовать утилиты Visual Studio, добавляем этот якорь. После этого задача сборки начинает выглядеть намного более опрятно:
build_job:
<<: *enter_vsdevshell
stage: build
only:
- branches
script:
- 'msbuild /t:restore /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly'
- 'msbuild /p:Configuration=Release /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly'
artifacts:
expire_in: 2 days
paths:
- '$env:TESTS_OUTPUT_FOLDER_PATH'
- Утилита vswhere.exe умеет находить и выдавать список найденных инсталляций Visual Studio. Расположен она всегда по одному и тому же пути (этот путь записан в переменной VSWHERE_PATH). Поскольку в переменной фигурирует подстановка
%programfiles%
, её требуется раскрыть до пути к этой папке. Такое раскрытие проще всего сделать через статический метод .NET System.Environment.ExpandEnvironmentVariables.
Результат: мы имеем путь к vswhere.
- Вызовом vswhere получим путь к папке с установленной Visual Studio.
Все параметры утилиты можно посмотреть, если запуститьvswhere.exe
с параметром-help
, в статье же только перечислю использованные:
- -latest (искать самые свежие установки),
- -property installationPath (вывести параметр пути установки),
- -format value (при печати параметра вывести только значение параметра, без его имени),
- -products <список искомых инсталляций Visual Studio, пробел считается разделителем элементов списка> (указание искомых редакций Visual Studio). Например, при запуске с параметром
-products Microsoft.VisualStudio.Product.Community Microsoft.VisualStudio.Product.BuildTools
утилита попробует найти Visual Studio редакций Community или BuildTools. Подробнее об идентификаторах продуктов смотрите по ссылке https://aka.ms/vs/workloads.
Результат: в переменную $visualStudioPath записан путь к Visual Studio или пустая строка, если инсталляций Visual Studio не найдено (обработку этой ситуации мы ещё не добавили).
- Команда Import-Module загружает библиотеку Microsoft.VisualStudio.DevShell.dll, в которой прописаны командлеты трансформации консоли Powershell в Developer-консоль. А командлет Join-Path формирует путь к этой библиотеке относительно пути установки Visual Studio.
На этом шаге нам прилетит ошибка, если библиотека Microsoft.VisualStudio.DevShell.dll отсутствует или путь к установке Visual Studio нужной редакции не был найден — Import-Module сообщит, что не может загрузить библиотеку.
Результат: загружен модуль Powershell с командлетом трансформации.
- Запускаем «передёлку» консоли в Developer Powershell. Чтобы корректно прописать пути к утилитам, командлету требуется путь к установленной Visual Studio (параметр
-VsInstallPath
). А указаниеSkipAutomaticLocation
требует от командлета не менять текущее расположение (без этого параметра путь меняется на<домашнаяя папка пользователя>\source\repos
.
Результат: мы получили полноценную консоль Developer Powershell с прописанными путями к msbuild и многим другим утилитам, которые можноиспользовать при сборке.
Используем CI для проставления версии во все сборки решения
Раньше мы использовали t4 шаблоны для проставления версий: номер версии собиралась из содержимого файла в формате <major>.<minor>.<revision>
, далее к ней добавлялся номер сборки из Gitlab CI и он передавался в tt-шаблон, добавляющий в решение везде, где требуется, номер версии. Однако некоторое время назад был найден более оптимальный способ — использование команд git tag
и git describe
.
Команда git tag
устанавливает коммиту метку (тэг). Отметить таким образом можно любой коммит в любой момент времени. В отличие от веток, метка не меняется. То есть если после помеченного коммита вы добавите ещё один, метка останется на помеченном коммите. Если попробуете переписать отмеченный коммит командами git rebase или git commit --amend, метка также продолжит указывать на исходный коммит, а не на изменённый. Подробнее о метках смотрите в git book.
Команда git describe
, к сожалению, в русскоязычном gitbook не описана. Но работает она примерно так: ищет ближайшего помеченного родителя текущего коммита. Если такого коммита нет — команда возвращает ошибку fatal: No tags can describe '<тут хэш коммита>'
. А вот если помеченный коммит нашёлся — тогда команда возвращает строку, в которой участвует найденная метка, а также количество коммитов между помеченным и текущим.
На заметку: чтобы данная команда работала корректно во всех случаях, автор gitflow даже чуть-чуть поменял скрипты finish hotfix и finish release. Если кому интересно посмотреть обсуждение с автором gitflow, а также увидеть что изменилось (картинка с актуальной схемой в последнем сообщении в треде).
Кстати, по этой же причине если вы используете gitflow, требуется после вливания feature-ветки в develop требуется удалить влитую локальную ветку, после чего пересоздать её от свежего develop:
(обратите внимание на историю git в левой части картинки: из-за отсутствия пути из текущего коммита до коммита с меткой 1.0.5, команда git describe
выдаст неверный ответ)
Но вернёмся к автопроставлению версии. В нашем репозитории царит gitflow (точнее его rebase-версия), метки расставляются в ветке master и мы не занываем пересоздавать feature-ветки от develop, а также merge-ить master в develop после каждого релиза или хотфикса.
Тогда получить версию для любого коммита и сразу передать её в msbuild можно добавив всего пару строк к задаче сборки:
build_job:
<<: *enter_vsdevshell
stage: build
only:
- branches
script:
- 'msbuild /t:restore /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly'
- '$versionGroup = git describe --long | Select-String -Pattern "(?<major>[0-9]+)\.(?<minor>[0-9]*)\.(?<patch>[0-9]*)\-(?<commit>[0-9]+)\-g[0-9a-f]+" | Select-Object -First 1'
- '[int]$major, [int]$minor, [int]$patch, [int]$commit = $versionGroup.Matches[0].Groups["major", "minor", "patch", "commit"].Value'
- '[string]$version = "$major.$minor.$patch.$commit"'
- 'msbuild /p:Configuration=Release /p:AssemblyVersionNumber=$version /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly'
artifacts:
expire_in: 2 days
paths:
- '$env:TESTS_OUTPUT_FOLDER_PATH'
Как это работает:
- Мы проставляем метки в формате
<major>.<minor>.<revision>
. - Тогда
git describe --long
возвращает нам строку, описывающую версию в формате<major>.<minor>.<revision>-<количество новых коммитов>-g<хэш текущего коммита>
. - Парсим полученную строку через регулярные выражения, выделяя нужные нам части — и записывем части в
$versionGroup
. - Преобразовываем четыре найденные подстроки в 4 числа и пишем их в переменные
$major
,$minor
,$patch
,$commit
, после чего собираем из них строку уже в нужном нам формате. - Передаём указанную строку в msbuild чтобы он сам проставил версию файлов при сборке.
Обратите внимание: если вы, согласно gitflow, будете отмечать (тэгировать) ветку master после вливания в неё release или hofix, будьте внимательны: до простановки метки автосборка будет вестись относительно последней существующей ветки. Например, сейчас опубликована версия 3.4, а вы создаёте release-ветку для выпуска версии 3.5. Так вот: всё время существования этой ветки, а также после её вливания в master, но до простановки тэга, автосборка будет проставлять версию 3.4.
Добавляем отправку данных в SonarQube
SonarQube — это мощный инструмент контроля качества кода.
SonarQube имеет бесплатную Community-версию, которая способна проводить полный анализ. Правда, только одной ветки. Чтобы настроить её на контроль качества ветки разработки (develop), требуется выполнить следующие шаги (разумеется, помимо установки и развёртывания сервера SonarQube):
Создайте в SonarQube новый проект, после чего запомнить его ключ.
Скачайте SonarScanner for MSBuild (с сайта sonarqube.org)[https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-msbuild/] — мы используем версию .NET Framework 4.6+.
Распакуйте содержимое архива в папку. Например, в
C:\Tools\SonarScanner
.
На заметку: эту утилиту также можно скачать на сборочную машину через NuGet, но тогда надо будет чуть по-иному указывать её путь.
- Зайдите в параметры CI/CD в свойствах проекта в Gitlab следующие параметры:
- SONARQUBE_PROJECT_KEY — ключ проекта,
- SONARQUBE_AUTH_TOKEN — токен авторизации.
Прописывать параметр ключа проекта необходимо для каждого проекта отдельно (ведь у каждого проекта свой уникальный ключ). А параметр токена авторизации желательно скрыть (отметить как Masked) чтобы он не был доступен всем, кто имете доступ к репозиторию или логам сборки.
Допишите переменные к секции Variables:
variables: SONARSCANNER_MSBUILD_PATH: 'C:\Tools\SonarScanner\SonarScanner.MSBuild.exe' SONARQUBE_HOST_URL: 'url вашего сервера SonarQube'
Допишите в задачу тестирования ветки разработки (test_job) команды для запуска анализа кода и уберите зависимость от задачи build_job:
test_job: stage: test only: - /^develop$/ <<: *enter_vsdevshell script: - '$versionGroup = git describe --long | Select-String -Pattern "(?<major>[0-9]+)\.(?<minor>[0-9]*)\.(?<patch>[0-9]*)\-(?<commit>[0-9]+)\-g[0-9a-f]+" | Select-Object -First 1' - '[int]$major, [int]$minor, [int]$patch, [int]$commit = $versionGroup.Matches[0].Groups["major", "minor", "patch", "commit"].Value' - '[string]$version = "$major.$minor.$patch.$commit"' - '& "$env:SONARSCANNER_MSBUILD_PATH" begin /key:$env:SONARQUBE_PROJECT_KEY /d:sonar.host.url=$env:SONARQUBE_HOST_URL /d:sonar.login=$env:SONARQUBE_AUTH_TOKEN /d:sonar.gitlab.project_id=$CI_PROJECT_PATH /d:sonar.gitlab.ref_name=develop /v:$version /d:sonar.dotnet.excludeGeneratedCode=true' - 'msbuild /t:rebuild /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly' - '& "$env:SONARSCANNER_MSBUILD_PATH" end /d:sonar.login=$env:SONARQUBE_AUTH_TOKEN' - '& "$env:XUNIT_PATH" "$env:TESTS_OUTPUT_FOLDER_PATH\CiCdExample.Tests.dll"'
Теперь при каждой сборке ветки develop в SonarQube будет отправляться подробный анализ нашего кода.
На заметку: вообще команда msbuild /t:rebuild
полностью пересобирает решение. Вероятно, в большинстве проектов анализ можно было бы встроить прямо в стадию сборки. Но сейчас у нас анализ в отдельной задаче.
Пара слов об использованных параметрах:
- key — ключ проекта на сервере SonarQube,
- v — собираемая версия. ИМХО отлично комбинируется с предыдущим шагом автопроставления версии,
- sonar.gitlab.project_id — ID проекта на сервере Gitlab,
- sonar.gitlab.ref_name — название ветки, которое получает сервер SonarQube при передаче результатов анализа,
- sonar.dotnet.excludeGeneratedCode — не включать в анализ объекты, отмеченные атрибутом System.CodeDom.Compiler.GeneratedCode (чтобы не оценивать качество автосгенерированного кода).
«Причёсываем» автотесты xUnit + добавляем вычисление покрытия тестами через OpenCover
Со сборкой более-менее разобрались — теперь приступаем к тестам. Доработаем код прогона тестов, чтобы он:
- сам находил библиотеки с тестами,
- прогонял их пачкой через xUnit,
- вычислял тестовое покрытие через OpenConver,
- отправлял результаты покрытия тестами в SonarQube.
На заметку: обычно в паре с OpenCover используют ReportGenerator, но при наличии SonarQube мы с тем же успехом можем смотреть результаты через его интерфейс.
Для настройки выполним следующие шаги:
Скачайте OpenCover в виде zip-файла с сайта github.
Распакуйте содержимое архива в папку. Например, в
C:\Tools\OpenCover
.
На заметку: эту утилиту также можно скачать на сборочную машину через NuGet, но тогда надо будет чуть по-иному указывать её путь.
Допишите переменные к секции Variables:
variables: OBJECTS_TO_TEST_REGEX: '^Rt[^\n]*\.(dll|exe)$' OPENCOVER_PATH: 'C:\Tools\opencover-4.7.922\xunit.console.exe' OPENCOVER_FILTER: '+[Rt.*]* -[*UnitTests]* -[*AssemblyInfo]*' OPENCOVER_REPORT_FILE_PATH: '.\cover.xml'
Модифицируйте задачу тестирования ветки разработки (test_job), чтобы она включала и команды вызова OpenCover:
test_job:
stage: test
only:
- /^develop$/
<<: *enter_vsdevshell
script:
- '$versionGroup = git describe --long | Select-String -Pattern "(?<major>[0-9]+)\.(?<minor>[0-9]*)\.(?<patch>[0-9]*)\-(?<commit>[0-9]+)\-g[0-9a-f]+" | Select-Object -First 1'
- '[int]$major, [int]$minor, [int]$patch, [int]$commit = $versionGroup.Matches[0].Groups["major", "minor", "patch", "commit"].Value'
- '[string]$version = "$major.$minor.$patch.$commit"'
- '& "$env:SONARSCANNER_MSBUILD_PATH" begin /key:$env:SONARQUBE_PROJECT_KEY /d:sonar.host.url=$env:SONARQUBE_HOST_URL /d:sonar.login=$env:SONARQUBE_AUTH_TOKEN /d:sonar.gitlab.project_id=$CI_PROJECT_PATH /d:sonar.gitlab.ref_name=develop /v:$version /d:sonar.cs.opencover.reportsPaths="$env:OPENCOVER_REPORT_FILE_PATH" /d:sonar.dotnet.excludeGeneratedCode=true'
- 'msbuild /t:rebuild /m:$env:MSBUILD_CONCURRENCY /nr:false /clp:ErrorsOnly'
- '$dllsToRunUnitTesting = @(Get-ChildItem "$env:TESTS_OUTPUT_FOLDER_PATH" -Recurse) | Where-Object {$_.Name -match $env:OBJECTS_TO_TEST_REGEX} | ForEach-Object { """""$_""""" } | Join-String -Separator " "'
- '& "$env:OPENCOVER_PATH" -register -target:"$env:XUNIT_PATH" -targetargs:"$dllsToRunUnitTesting -noshadow" -filter:"$env:OPENCOVER_FILTER" -output:"$env:OPENCOVER_REPORT_FILE_PATH" | Write-Host'
- 'if ($?) {'
- '[xml]$coverXml = Get-Content "$env:OPENCOVER_REPORT_FILE_PATH"'
- '$sequenceCoverage = $coverXml.CoverageSession.Summary.sequenceCoverage'
- '$branchCoverage = $coverXml.CoverageSession.Summary.branchCoverage'
- 'Write-Host "Total Sequence Coverage <!<$sequenceCoverage>!>"'
- 'Write-Host "Total Branch Coverage [![$branchCoverage]!]"'
- '} else {'
- 'Write-Host "One or more tests failed!"'
- 'Throw'
- '}'
- '& "$env:SONARSCANNER_MSBUILD_PATH" end /d:sonar.login=$env:SONARQUBE_AUTH_TOKEN'
Обратите внимание: в begin-команде запуска sonar scanner-а появился дополнительный параметр — /d:sonar.cs.opencover.reportsPaths
.
- (необязательный пункт) Ненадолго возврващаемся в Gitlab, заходим в меню проекта Settings > CI/CD и находим на странице настроек параметр
Test coverage parsing
. Указываем в нём регулярное выражение, которое позволит Gitlab-у также получать информацию о покрытии тестами приложения:
- если хочется видеть значение покрытия тестов по строкам кода (его ешё называют Sequence Coverage или Statement Coverage), указываем выражение
<!<([^>]+)>!>
, - если хочется видеть значение покрытия тестов по веткам условных операторов (его называют Decision Coverage или Branch Coverage), указываем выражение
\[!\[([^>]+)\]!\]
.
- если хочется видеть значение покрытия тестов по строкам кода (его ешё называют Sequence Coverage или Statement Coverage), указываем выражение
А теперь комментарии по изменениям в скрипте.
Длинная строка, начинающаяся с объявления переменной $dllsToRunUnitTesting
, нужна для того, чтобы найти все библиотеки нашего приложения, которые потом будут участвовать в расчёте тестового покрытия. При этом выбираются они по регулярному выражению, заданному в параметре $env:OBJECTS_TO_TEST_REGEX
(мы же не хотим, например, учитывать в покрытии библиотеки .net или сторонних nuget-пакетов). Пути ко всем найденным библиотекам склеиваются в строку с разделителем пробелом и двумя двойными кавычками для каждого параметра. Две двойные кавычки были добавлены потому, что OpenCover при вызове приложения xunit «съедает» одни из кавычек, а вторые кавычки нужны на случай наличия пробелов в абсолютных путях.
Следующая строка — запуск утилиты OpenConver с передачей ей списка библиотек нашего приложения, фильтра, по которому OpenCover исключает из покрытия библиотеки с unit-тестами, а также часть классов, не требующих расчёта покрытия (например, AssemblyInfo). Конвейер и Write-Host
были добавлены в порыве вдохновения, так как без него у нас не работал вывод OpenConver-а.
И следующий if
проверяет, успешно ли завершился запуск OpenConver. Если не успешно — «кладём» скрипт; если же успешно — парсим получившийся xml-файлик с отчётом и печатаем значения покрытия тестами, чтобы затем его легко распарсил gitlab через указанное в настройках регулярное выражение.
Послесловие
Как известно, нет предела совершенству. Мы продолжим добавлять новые функции в используемые нами процессы сборки, тестирования и публикации. На момент написания тестируется переезд нашего ПО на .NET 5 (с ним OpenCover уже не работает, сборка выполняется через команду dotnet), а также мы переходим на другой способ публикации (к сожалению, пока не могу дать более подробный комментарий).
Если у вас появятся вопросы или предложения — пишите в комментариях.
И спасибо за прочтение!
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Какие примеры использования автосборки/автопубликации вам интересны?
-
0,0%Публикация артефактов сборки: на сайт, в телеграм-канал0
-
100,0%Создание и сборка инсталлера1
-
0,0%Оповещение об этапах сборки и ошибках: в телеграм, в слак, по почте0
-
100,0%Автосборка и тестирование в .NET 5/.NET Core1
-
0,0%Другое, напишу в комментариях0