Итак, Cake. Многие слышали, многие хотели попробовать, но откладывали. Конечно, если ты все время работал на TeamCity или на Jenkins и продолжаешь, то зачем переизобретать то, что уже отлично работает? Люби свою жизнь и радуйся. Но вот, допустим, в твоей любимой жизни появился новый проект, новый дедлайн, минимум сторипойнтов до релиза, а опыта с новым сборщиком нет? Мне в этом случае и пригодился Cake.
Я сразу оговорюсь, что эта статья не подтолкнет сразу на использование Cake, как меня, и многих моих коллег не подтолкнули статьи, которые выходили ранее. По большей части потому что на него нет смысла переходить в проекте, который не приносит боль и который работает стабильно. Собираете в своем любимом Jenkins и все идет нормально. Но пусть после этой статьи в голове отложится, что Cake существует. Он в очередной раз никуда не делся, он умеет уже многое и работать с ним все проще. Гораздо проще, чем было раньше.
На что похож Cake? Наверное, любой разработчик, не погрязший в
мире .Net, найдет свою аналогию: gradle,
gulp, golang make. Make-системы не откровение в 2020 году.
Это всегда было удобно, а значит нужно и правильно. Мир
.Net долгое время был обделен такими средствами.
Фактически был и есть до сих пор MSBuild, но у
него есть очень-очень много недостатков. Основной - кто вообще
умеет им пользоваться из рядовых разработчиков? И какова
целесообразность его освоения? Какие-то базовые и нужные всем вещи
явно проще делать на билд-сервере. Наверное, кому-то он и удобен,
но я уверен, что значимая часть коммьюнити предпочтет
MSBuild'у освоить новый билд-сервер. Один раз
написать конфиг и забыть как страшный сон.
А что если бы существовала make-система с DSL на C#,
автокомплитом и прочими фишками типизированного языка? Да, я про
Cake. В частности сейчас пойдет разговор про
библиотеку Cake.Frosting, являющуюся одним из
раннеров make-системы.
Подробней про доступные раннеры можно прочитать тут: Cake Runners
С Frosting все привычно самодокументирующийся
Api с которым почти сразу находишь общий язык. Методы расширения,
загружаемые из Nuget на любой случай жизни,
структура проекта, похожая на смесь тестов или бенчмарков и хоста
Asp. Все решения угадываются сразу, все как
дома.
Frosting от остальных раннеров Cake отличается тем, что существует не в виде тулза, а в виде отдельного проекта, который можно докинуть в solution и хранить вместе с ним в репозитории. Это невероятно упрощает работу с системой. Фактически стоит просто создать новый проект, подключить к нему зависимость Cake.Frosting, сконфигурировать Build-хост и можно запускать этот проект командой.
dotnet run
Чтобы нам стало еще проще, существует темплейт проекта. К нему в
комплект даже идут шелл-скрипты для Mac
OS, Linux и
Windows, подгружающие SDK, если его нет в
окружении. Через них стоит вызывать сборку вместо dotnet
CLI, если в этом есть необходимость.
Тут можно почитать подробнее об этом: Frosting Bootstraping
После того, как проект создан, можно начинать конфигурировать
процесс сборки. Из основных мест для нашего кода мы сразу обращаем
внимание на прогрев и очистку системы перед сборкой это
соответствующие методы класса Lifetime:
Setup и TearDown. В них привычно
делать уборку артефактов до и после сборки.

Вторая интересующая нас часть проекта папка Tasks. Тут хранятся все шаги сборки в виде классов -наследников от FrostingTask<Context>.

Задачи автоматически регистрируются в IoC контейнере, как мы привыкли в Asp. Более того, Frosting реализует точно такой же паттерн с DI через IServiceCollection, к которому мы все привыкли.

Порядок выполнения билд-шагов определяется их зависимостями. Анализ зависимостей начинается с корня графа, по умолчанию это задача Default. Для того, чтобы уведомить систему, что эта задача зависит от другой, ей можно установить атрибут
[Dependency(typeof(MyPreviousTask))]
Где MyPreviousTask это задача, которая должна завершиться ранее помеченной.
Список задач может быть любым, в том числе привычный нам:
-
Восстановление пакетов.
-
Билд.
-
Прогон unit-тестов.
-
Publish.
-
Поставка артефактов.
В качестве поставки артефактов мы можем делать как привычную нам
архивацию и отправку в среду исполнения приложения, так и упаковку
приложения в образ docker, словом все, что мы
можем написать на C#.
Как уже было сказано выше, на данный момент существует масса
пакетов с расширениями контекста выполнения, понимающего, благодаря
им, множество команд сборки. Копирование файлов, удаление,
логирование и т.д.
Единственный минус такого похода захламление
IntelliSense окна чудовищным количеством методов,
но когда это нас останавливало?
По случаю хотелось бы напомнить про относительно свежую фичу
.Net core self-contained
приложения. В этом способе публикации надо явно задать версию
рантайма, в результате чего формируется не библиотека, исполняемая
в контексте dotnet, а запускаемое приложение,
содержащее рантайм, так сказать, в себе. Она может пригодиться при
упаковке в образ без установленного рантайма, если по каким-то
причинам установить последний нельзя. Нет никаких причин не делать
этого в Cake.
Когда все готово, настроено и залито в репозиторий, мы делаем в TS или Jenkins всего одну команду
dotnet run ./Build/Build.csproj
Путь до проекта у вас будет свой (Ваш Кэп) и смотрим, как
происходит медитативный процесс сборки. Frosting
пишет события сборки в стандартный вывод, который читает
билд-сервер, так что никакие данные не пропадут.
Конечно, это минимальный и самый простой сценарий использования системы. Билд-сервер может передать в dotnet аргументы при вызове билда Frosting. Например, чтобы в дальнейшем установить версию сборки или образа или тип рантайма или папки назначения для публикации.
В общем полный простор фантазии.
Мотивация
-
Это удобно. Вы пишете на своем основном языке и не зависите от выразительности скриптов и настроек/плагинов билд-сервера;
-
Это мобильно. Вы заливаете код в репозиторий и он универсально запускается на любом билд-сервере. И никакого вендор-лока.
-
Это версионно. Код сборки хранится в репозитории. Вместе с самим релизом.
-
Это позволяет экономить. Если тарифный план билд-сервера не позволяет делать много билд-конфигов, вам достаточно одного, запускающего разные проекты. Тот же результат без лишних трат.
-
Это легко. IntelliSense, автокомплит, разберется даже обленившийся senior.
Проблемой же может стать отсутствие в Cake необходимых интеграций с линтерами, сонарами и т.д. Этот вопрос следует уточнить перед использованием системы. Или дописать свои расширения, залить в Nuget и быть очень-очень хорошим человеком.
Бонусом пример использования Cake.Frosting на github. Для затравки так сказать: Link
Ссылка на сайт проекта Cake