Или как обзавестись красивыми бейджиками для своего проекта за один вечер ненапряжного кодинга Наверное, у каждого разработчика, имеющего хотя бы один пет-проект, в определённый момент возникает зуд на тему красивых бейджиков со статусами, покрытием кода, версиями пакетов в nuget… И меня этот зуд привёл к написанию этой статьи. В процессе подготовки к её написанию я обзавёлся вот такой красотой в одном из своих проектов:  В статье будет рассмотрена базовая настройка непрерывной интеграции и поставки для проекта библиотеки классов на .Net Core в GitLab, с публикацией документации в GitLab Pages и отправкой собранных пакетов в приватный фид в Azure DevOps.  В качестве среды разработки использовалась VS Code c расширением GitLab Workflow (для валидации файла настроек прямо из среды разработки). Что такое CI/CD и зачем нужно — можно легко нагуглить. Полноценную документацию по настройке пайплайнов в GitLab найти также несложно. Здесь я кратко и по возможности без огрехов опишу процесс работы системы с высоты птичьего полёта: Таким образом, имеем: Соответственно, задача при настройке CI/CD сводится к тому, чтобы создать набор задач, реализующих все необходимые действия для сборки, тестирования и публикации кода и артефактов. Потому, что когда появилась необходимость создать приватные репозитории под пет-проекты, на GitHub'e они были платными, а я — жадным. Репозитории стали бесплатными, но пока это не является для меня поводом достаточным переезжать на GitHub. Потому что там настройка элементарная — даже не требуются знания командной строки. Интеграция с внешними провайдерами git — в пару кликов, импорт SSH-ключей для отправки коммитов в репозиторий — тоже, пайплайн легко настраивается даже не из шаблона. Имеем: Хотим: Описанные требования органично ложатся на следующую модель пайплайна: Приступим! Создаём аккаунт в Microsoft Azure Переходим в Azure DevOps Создаём новый проект При нажатии на кнопку Create проект будет создан, и будет совершён переход на его страницу. На этой странице можно отключить ненужные возможности, перейдя в настройки проекты (нижняя ссылка в списке слева -> Overview -> блок Azure DevOps Services)
 Переходим в Atrifacts, жмём Create feed Жмём Connect to feed, выбираем Visual Studio, из блока Machine Setup копируем Source
 Идём в настройки аккаунта, выбираем Personal Access Token
 Создаём новый токен доступа Копируем созданный токен — после закрытия модального окна значение будет недоступно Заходим в настройки репозитория в GitLab, выбираем настройки CI/CD
 Раскрываем блок Variables, добавляем новую На этом предварительная настройка завершена. По умолчанию, для настройки CI/CD в GitLab используется файл .gitlab-ci.yml из корня репозитория. Можно настроить произвольный путь до этого файла в настройках репозитория, но в данном случае это не нужно. Как видно из расширения, файл содержит конфигурацию в формате YAML. В документации подробно описано, какие ключи могут содержаться на верхнем уровне конфигурации, и в каждом из вложенных уровней. Сначала добавим в файл конфигурации ссылку на docker-образ, в котором будет происходить выполнение задач. Для этого находим страницу образов .Net Core в Docker Hub. В GitHub есть подробное руководство, какой выбрать образ для разных задач. Нам для сборки подойдёт образ с .Net Core 3.1, поэтому смело добавляем первой строкой в конфигурацию Теперь при запуске пайплайна с хранилища образов Microsoft будет скачан указанный образ, в котором и будут исполняться все задачи из конфигурации. Следующий этап — добавить stage'ы. По умолчанию GitLab определяет 5 этапов: Ничего не мешает объявить их явно, впрочем. Порядок, в котором указаны этапы, влияет на порядок, в котором они выполняются. Для полноты изложения, добавим в конфигурацию: Для отладки имеет смысл получить информацию об окружении, в котором исполняются задачи. Добавим глобальный набор команд, который будет выполняться перед каждой задачей, с помощью before_script: Осталось добавить хотя бы одну задачу, чтобы при отправке коммитов пайплайн запустился. Пока что добавим пустую задачу для демонстрации: Запускаем валидацию, получаем сообщение, что всё хорошо, коммитим, пушим, смотрим на сайте на результаты… И получаем ошибку скрипта — bash: .PSVersion: command not found. WTF? Всё логично — по умолчанию runner'ы (отвечающие за исполнение скриптов задач, и предоставляемые GitLab'ом) используют bash для исполнения команд. Можно исправить это дело, явно указав в описании задачи, какие теги должны быть у исполняющего пайплайн раннера: Отлично! Теперь пайплайн выполняется. Внимательный читатель, повторив указанные шаги, заметит, что задача выполнилась в этапе test, хотя мы не указывали этап. Как можно догадаться, test является этапом по умолчанию. Продолжим создание скелета конфигурации, добавив все задачи, описанные выше: Получили не особенно функциональный, но тем не менее корректный пайплайн. Из-за того, что ни для одной из задач не указаны фильтры срабатывания, пайплайн будет полностью исполняться при каждой отправке коммитов в репозиторий. Так как это не является желаемым поведением в общем случае, мы настроим фильтры срабатывания для задач. Фильтры могут настраиваться в двух форматах: only/except и rules. Вкратце, only/except позволяет настраивать фильтры по триггерам (merge_request, например — настраивает задачу на выполнение при каждом создании запроса на слияние и при каждой отправке коммитов в ветку, являющуюся исходной в запросе на слияние) и именам веток (в т.ч. с использованием регулярных выражений); rules позволяет настраивать набор условий и, опционально, изменять условие выполнения задачи в зависимости от успеха предшествующих задач (when в GitLab CI/CD). Вспомним набор требований — сборка и тестирование только для merge request, упаковка и отправка в Azure DevOps — для merge request и пушей в мастер, генерация документации — для пушей в мастер. Для начала настроим задачу сборки кода, добавив правило срабатывания только при merge request: Теперь настроим задачу упаковки на срабатывания на merge request и добавление коммитов в мастер: Как видно, всё просто и прямолинейно. Также можно настроить задачу на срабатывание только если создан merge request с определённой целевой или исходной веткой: В условиях можно использовать перечисленные здесь переменные; правила rules не совместимы с правилами only/except. Во время выполнения задачи build job у нас будут созданы артефакты сборки, которые можно переиспользовать в последующих задачах. Для этого нужно в конфигурацию задачи добавить пути, файлы по которым нужно будет сохранить и переиспользовать в следующих задачах, в ключ artifacts: Пути поддерживают wildcards, что определённо упрощает их задание. Если задача создаёт артефакты, то каждая последующая задача сможет их получить к ним доступ — они будут располагаться по тем же путям относительно корня репозитория, по которым были собраны из исходной задачи. Так же артефакты доступны для скачивания на сайте. Теперь, когда у нас готов (и проверен) каркас конфигурации, можно переходить собственно к написанию скриптов для задач. Возможно, когда-то давно, в далёкой-далёкой галактике, собирать проекты (в том числе и на .net) из командной строки было болью. Сейчас же собрать, протестировать и опубликовать проект можно в 3 команды: Естественно, есть некоторые нюансы, из-за которых мы несколько усложним команды. Coverlet после запуска тестов выводит в консоль статистику по запуску: GitLab позволяет указать регулярное выражение для получения статистики, которую потом можно получить в виде бейджа. Регулярное выражение указывается в настройках задачи с ключом coverage; в выражении должна присутствовать capture-группа, значение которой и будет передано в бейдж: Здесь мы получаем статистику из строки с общим покрытием по линиям. Оба действия у нас назначены на последний этап пайплайна — раз уж сборка и тесты прошли, можно и поделиться с миром наработками. Для начала рассмотрим публикацию в источник пакетов: Если в проекте не присутствует файл конфигурации nuget (nuget.config), создадим новый: dotnet new nugetconfig Зачем: в образе может быть запрещён доступ на запись к глобальным (пользовательской и машинной) конфигурациям. Чтобы не ловить ошибки, просто создадим новую локальную конфигурацию и будем работать с ней. Теперь настроим создание документации: Раньше при настройке проекта я указывал источник кода для документации как файл решения. Основной минус — документация создаётся и для тестовых проектов. В случае, если это не нужно, можно задать такое значение узлу metadata.src: Вот такую простую конфигурацию можно составить буквально за полчаса и пару чашек кофе, которая позволит при каждом запросе слияния и отправке в мастер проверять, что код собирается и тесты проходят, собирать новый пакет, обновлять документацию и радовать глаз красивыми бейджиками в README проекта. Ради них ведь всё и затевалось! Бейджи со статусами пайплайна и покрытием кода доступны в GitLab в настройках CI/CD в блоке Gtntral pipelines:  Бейдж со ссылкой на документацию я создавал на платформе Shields.io — там всё достаточно прямолинейно, можно создать свой бейдж и получать его с помощью запроса.  Azure DevOps Artifacts также позволяет создавать бейджи для пакетов с указанием актуальной версии. Для этого в источнике на сайте Azure DevOps нужно нажать на Create badge у выбранного пакета и скопировать markdown-разметку:   Во время написания конфигурации и поисков по документации, я наткнулся на интересную возможность YAML — переиспользование фрагментов. Как видно из настроек задач, все они требуют наличия тега windows у раннера, и срабатывают при отправке в мастер/создании запроса на слияние (кроме документации). Добавим это во фрагмент, который будем переиспользовать: И теперь в описании задачи можем вставить объявленный ранее фрагмент: Названия фрагментов должны начинаться с точки, чтобы не быть интерпретированными как задача. При создании пакета компилятор проверяет ключи командной строки, и в их отсутствие — файлы проектов; найдя узел Version, он берёт его значение как версию собираемого пакета. Выходит, чтобы собрать пакет с новой версией, нужно либо обновить её в файле проекта, либо передать как аргумент командной строки. Добавим ещё одну хотелку — пусть младшие два номера в версии будут годом и датой сборки пакета, и добавим пререлизные версии. Добавлять эти данные в файл проекта и проверять перед каждой отправкой можно, конечно — но можно ведь это делать и в пайплайне, собирая версию пакета из контекста и передавая через аргумент командной строки. Условимся, что если в сообщении коммита есть строка вида release (v./ver./version) <version number> (rev./revision <revision>)?, то мы будем из этой строки брать версию пакета, дополнять её текущей датой и передавать как аргумент команде dotnet pack. В отсутствие строки — просто не будем собирать пакет. Данную задачу решает следующий скрипт:  Добавляем скрипт в задачу pack and deploy job и наблюдаем сборку пакетов строго при наличии заданной строки в сообщении коммита. Потратив примерно полчаса-час времени на написание конфигурации, отладку в локальном powershell и, возможно, пару неудачных запусков, мы получили несложную конфигурацию для автоматизации рутинных задач.  Конечно, GitLab CI/CD гораздо обширнее и многограннее, чем может показаться после прочтения этого руководства — это совершенно не так. Там даже Auto DevOps есть, позволяющий  Теперь в планах — сконфигурировать пайплайн для развёртывания приложений в Azure, с использованием Pulumi и автоматическим определением целевого окружения, что будет освещено в следующей статье. Разработчик C#