Поэтапная настройка Continuous Integration (build, test, deploy) для .NET Core WebApp + GitHub

  • Tutorial

Поэтапная настройка Continuous Integration (build, test, deploy) для .NET Core WebApp + GitHub




Всем привет. Continuous Integration (CI) давным-давно проник в мир разработки программного обеспечения и для многих является его неотъемлемой частью, которое позволяет создавать более качественный код сохраняя при этом удобство разработки. И, если поначалу, настройка CI требовала значительных усилий и денег, то сейчас это стало намного доступнее, проще и даже бесплатно. Если вам интересно, как настроить CI для своего .NET Core open-source проекта, прошу под кат.


Disclaimer


В статье будет много ссылок, тривиальных моментов и отступлений, не вините автора за это, никогда не знаешь на чем читающий споткнется.


Несмотря на то, что основным языком демонстрационного проекта выбран F#, руководство полностью совместимо с проектами написанными на C#. F# выбран т.к. эта статья и связанный с ней репозиторий является основой для второй публикации — "Строим полноценное веб приложение на F# + Giraffe с нуля.", которая запланирована на июнь.


Оглавление


  • Вступление и необходимые инструменты
  • Создаем репозиторий
  • Новый проект
  • CI Build
  • CI Test
  • CI Deployment
  • Итоги
  • Теги
  • Ссылки

Вступление и необходимые инструменты


В статье используется демонстрационный репозиторий. Для каждой главы в репозитории созданы теги, полный список которых можно найти в конце статьи.


Если вы хотите повторить то, что написано в статье вам понадобятся:



Создаем свой репозиторий


Tag: InitialMaster


Давайте начинать. Прежде всего нам нужна какая то система контроля версий. Я буду использовать Git и GitHub


Создайте новый репозиторий на GitHub, выберите шаблон для .gitignore файла и лицензию проекта. .gitignore файл нужен для того, что бы в ваш репозиторий не попадали лишние файлы (например результаты build процесса). Лицензия нужна что бы можно было понять объем разрешенных действий с вашим кодом. Для этого проекта я выбрал шаблон VisualStudio и MIT лицензию.


После создания репозитория нам нужно его клонировать — снять его полную копию и поместить на локальную машину. Для этого нужно установить Git локально и выполнить команду:


git clone PATH_TO_YOUR_REPOSITORY

Теперь у вас есть локальный репозиторий, связанный с удаленным репозиторием на GitHub. Имейте ввиду, у Git есть неплохая документация, так что, если вы на чем-то споткнетесь — вам сюда.


Система контроля версий не самое новое изобретение. Первая VCS появилась уже в 1982 году (хотя некоторые датируют ее появление даже 1972 годом). Подробнее об истории развития инструмента без которого современная разработка практически невозможна, можно найти здесь. А вот Git появился сравнительно недавно — в 2005 году благодаря Linus Torvald ключевому создателю Linux.

Новый проект


Tag: NewFSharpProject


Теперь, когда у нас есть репозиторий, мы готовы создать наш проект. Перейдите в папку с репозиторием и выполните команду создания нового проекта.


dotnet new web -lang F#

Важно, для того, что бы эта команда сработала вам нужно установить .NET Core SDK. Если вы работаете под Windows, скорее всего вам нужно будет перезапустить ОС для того, что бы система подхватила новые пути для dotnet.exe. И если к F# вы пока еще не готовы (а это очень веселый язык, рекомендую), вы можете не передавать параметр lang и тогда dotnet создаст для вас веб приложение на С#.


Выполнив эту команду, вы получите три новых файла:


  • Startup.fs — здесь находится конфигурация нашего сервера
  • Program.fs — здесь находится точка старта сервера
  • FSharpWebAppWithCIDemo.fsproj — а это описание структуры проекта

Несмотря на минимализм этого вполне достаточно что бы запустить свой hello world.


Теперь давайте договоримся о структуре проекта. Каждый проект должен находится в своей отдельной папке, а коревая папка будет содержать только файлы настройки безотносительно проектов.


В итоге у нас должно получиться что-то вроде такой структуры (дальше, все манипуляции с решением я буду делать через Visual Studio):


— FSharpWebAppWithCIDemo // основная папка проекта


— — BLL // проект с основной логикой


— — WebServer // веб сервер и его настройки.


Важно! В F# порядок файлов и проектов имеет значение. Файлы, которые находятся выше по дереву проекта не имеют доступа к файлам, которые находятся ниже по дереву. Это значит, что наш веб сервер сможет получить доступ к любому из проектов (он в самом низу), а вот проект с бизнес логикой должен будет довольствоваться только своим собственным кодом. С одной стороны, это несколько непривычно, с другой стороны, сразу структурирует код. Зачем, например, базе данных знать о каком-то веб сервере, правильно? Вообще, чем меньше один модуль знает о других модулях, тем меньше шансов сломать его, работая над каким-то другим модулем. Другими словами — low coupling high cohision


И еще один, пока все еще важный момент, касающийся Linux систем. Как вы знаете проекты, написанные на .NET Core кроссплатформенны, и могут быть выполнены на Linux системах. Но файл описывающий весь наш проект целиком (.sln файл) не совсем кроссплатформенен. Поэтому если вы работаете под ОС Windows, пути к проектам в этом файле будут написаны не совсем корректно — с использованием бекслеш символа: "\". К сожалению, сейчас это проблема. Она будет исправлена в ближайшее время. Но если, во время сборки, на сервере или у себя на локальной машине вы увидите ошибку вида:


 error MSB4025: The project file could not be loaded. Could not find file ... MySuperProject.fsproj.metaproj.

Скорее всего это значит, что вам нужно зайти в .sln файл и руками изменить "\" на "/".


Теперь наш проект готов. Соберите его, проверьте что он успешно собирается и давайте идти дальше.


Корни традиции писать hello world восходят к 1974 году, когда появилось руководство Programming in C: A Tutorial. Показательно, что примеры кода, описанные в руководстве, уже не собираются последними компиляторами.

CI — Build


Tag: TravisSupport


У нас уже есть работающее веб приложение на F#. Можно было бы начать непосредственно разработку, но тогда статья была бы не о том. Так что мы начнем настройку нашего — CI процесса и начнем ее с самого простого — настройки билда.


Вообще то, CI это не сервис и не инструмент. Это подход в программировании, основная и упрощенная идея которого заключается в частых коммитах, их автоматической проверке и доставке результата конечному пользователю. Поэтому, когда я говорю о том, что мы будем настраивать CI это значит, что мы настроим автоматический билд процесс при каждом пулл реквесте, запуск тестов и анализ их результатов. А результаты всего этого будут отображаться сразу под пулл реквестом, так что мы всегда будем в курсе того, кто и как именно сломал наш проект.


Давайте создадим новую ветку и назовем ее TravisSupport. Кстати, если вы делаете какую-то новую фичу ее неплохо делать именно в отдельной ветке, а не в master. Во-первых, вы будете независимы от ваших коллег и их коммитов. Во-вторых, в своей локальной ветке вы можете делать любые коммиты не боясь, что они попадут в основную ветвь и что-то сломают. И в-третьих, мое любимое преимущество, если сделать merge с флагом --squash то все ваши коммиты сольются в один и будут выглядеть так, как будто и не было этих 100500 попыток, а все получилось с первого раза. Главное, не забудьте потом удалить ветку источник иначе, если продолжите в ней работу и попробуете закоммититься еще раз, можете получить конфликт.


Но, ближе к делу. Для CI нам нужен инструмент, который будет собирать наш проект, тестировать его и делать то, что мы попросим. В качестве такого инструмента я выбрал Travis-CI. Он бесплатен для open-source, относительно прост и у меня есть с ним кое-какой опыт.


Что бы интегрироваться с Travis нам нужно:


  • Дать Travis доступ к нашему репозиторию.
  • Объяснить ему что от него требуется.

Первый пункт достаточно прост. Откройте https://travis-ci.org/ и залогиньтесь туда с помощью своего GitHub аккаунта. Затем добавьте свой репозиторий в список доступных. Если вы не увидели свой репозиторий, воспользуйтесь кнопкой SyncAccount.


Второй пункт гораздо интереснее. Управлением CI процессом travis-a осуществляется с помощью файла .travis.yml. Вот документация. Скопируйте этот файл в корень проекта и давайте разберем что в нем написано.


language: csharp

dotnet: 2.1.4
sudo: false

env:
    global:
        - DOTNET_CLI_TELEMETRY_OPTOUT: 1

script:
    - dotnet build FSharpWebAppWithCIDemo.sln -c Release

  • language — язык на котором написано приложение. Travis не делает различий между C# и F#, и директивы fsharp нет. Зато этот файл прекрасно подходит и для проектов на C#.
  • dotnet — мы можем сразу указать версию dotnet что бы быть уверенными с чем нас будут собирать.
  • env — эта секция отвечает за переменные окружения. Как видите мы добавили туда DOTNET_CLI_TELEMETRY_OPTOUT что бы выключить передачу телеметрии при билд процессе. Сюда можно записать любую переменную, и она будет передана аргументом в наш билд.
  • script — а эта секция отвечает за то, чего же мы хотим от нашего travis-a. В данном случае мы всего лишь хотим запустить билд в режиме релиза для нашего приложения.

Это самый простой и базовый сценарий CI. Простыми словами, при каждом пулл-реквесте, мы пробуем собрать наш билд. Собирается — все хорошо. Не собирается — мы увидим предупреждение прямо в GitHub и на почте. Условия запуска CI процесса легко настраиваются как в самом .travis.yml так и в настройках проекта на сайте. А если вам интересны дополнительные возможности Travis (например, выбор OS) то вам сюда


Еще раз напомню, если во время билда вы столкнетесь с ошибкой: "MSB4025, could not find file… .fsproj.metaproj" попробуйте заменить бекслеши на слеши в .sln файле.


Кстати, если посмотрите на процесс билда, вы увидите, что он проходит под Linux, что не может не радовать.


И, конечно, не забудьте добавить заслуженный бейджик в readme. Его можно найти в вашем Travis CI проекте.


Если вам нравится наглядность бейджиков, рекомендую заглянуть сюда. Если вам будет мало — там же можно создать свой собственный бейджик.

CI Test


Tag: CodeCov


Вот и пришло время тестирования. Вообще, принципы TDD мы уже нарушили, не написав тесты до написания кода. Так что пора все исправить.


Под проектом Bll создайте новый проект BllTest и добавьте туда BllTest.fs. В этот же проект установите xUnit
Напомню, что структура нашего проекта теперь должна выглядеть так:


— FSharpWebAppWithCIDemo


— — Bll


— — BllTest


— — WebServer


В проекте Bll, создайте новый bll.fs файл c модулем Say и функцией hello (мы опять нарушили TDD написав код до тестов)


namespace Bll

module Say =
    open System

    let hello() =
        "Hello user #" + Guid.NewGuid().ToString()

И протестируйте ее, а именно то, что результат вызова этой функции будет всегда отличается от предыдущего. Что, кстати, не гарантировано, т.к. существует не нулевая вероятность того, что при генерации двух GUID нам могут выпасть два одинаковых. Правда, скорее всего, пока вы дождетесь этого события потухнет солнце, но все же шанс есть.


На самом деле ни сам код, ни сам тест нам не важны (ведь вы это уже когда-то слышали, не так ли?). Важно только их наличие что бы правильно интегрироваться с Travis и CodeCov. Запустите тест вручную, убедитесь, что он проходит и давайте продолжать. После того как у нас появился первый тест, нам нужно, что бы Travis этот тест запустил. Для этого, в .travis.yml нужно добавить еще одну строку в секцию script


- dotnet test -c Release --no-build BllTest/BllTest.fsproj

Здесь все очевидно — мы запускаем тесты без билда (т.к. билд артефакты уже получены на предыдущем шаге) чем экономим себе время.


Но этого мало. Нам нужна статистика покрытия кода. Для этого нам пригодится coverlet.msbuild прекрасный инструмент который встраивается в msbuild и создает отчеты в разных форматах, в том числе и в нужном нам opencover. Откройте вкладку Nuget (или запустите команду из консоли) и установите этот пакет для BllTest проекта. В других проектах он не нужен. После этого поменяйте предыдущую строку в .travis.yml на эту:


 - dotnet test -c Release --no-build BllTest/BllTest.fsproj /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

Напомню, что полную версию .travis.yml можно забрать тут


Можно запустить эту же команду и из-под консоли что бы убедиться что все работает. Заодно вы увидите текущий code coverage своего проекта.


Итак, тесты у нас есть, они запускаются и даже видно какой-то отчет о покрытии кода. Но и этого тоже недостаточно. Теперь нам нужен кто-то еще, кто сможет обработать этот отчет и сообщить о его результатах. Здесь нам пригодится Codecov который, тоже бесплатен для OpenSource проектов. Логинимся в Codecov с помощью аккаунта GitHub и добавляем нужный проект. Наконец, в последний раз обновляем .travis.yml — добавляем новую секцию after_script.


    after_script:
        - bash <(curl -s https://codecov.io/bash)

Когда я увидел этот пример я зарыдал. До этого я убил часа три на то что бы запустить отправку отчета в coverall, а здесь все оказалось настолько просто… Спасибо ребята, что сделали меня счастливым.


Теперь, когда все приготовления сделаны, можем делать комит и идти получать свой заслуженный бейдж со 100% покрытием.


Автором TDD некоторые называют Kent Beck, однако сам он говорит, что всего лишь открыл заново методику, о которой читал давным-давно в какой-то древней книге по программированию.

CI Deployment


Tag: DeployToAzure


Вздохните, выпейте кофе, конец инфраструктурных работ уже близок. Мы начинаем приготовления к deployment процессу.


Если кратко, deployment это процесс доставки результатов нашей работы (build artefacts) для выполнения. Это можно делать руками, или в автоматическом режиме, что как человеку ленивому мне нравится гораздо больше. Я покажу как настроить деплой в Azure (но это только потому что у меня там уже есть аккаунт и еще осталось пара долларов). Если вам это не подходит, вы можете использовать build артефакты, которые для вас подготовит Travis. Достаточно поменять dotnet build на dotnet publish и отправить полученные результаты на ваш сервер, например:


  • AWS S3
  • GITHUB PAGES
  • NPM
  • HEROKU
  • GOOGLE APP ENGINE

И многое другое


Но сейчас не про это, а про Azure. Настройка деплоймента относительно проста и (что самое приятное) управляется извне самой Azure. Сейчас расскажу.


Для начала нам нужно создать свое веб приложение. Делается это очень просто. Заходим в Azure, затем Create a resource -> Web App или просто по этой ссылке. Там выбираем имя нашего будущего сайта, подписку (подписка это то место откуда у вас будут списываться деньги, так что лучше выбрать бесплатную, например для студентов) и ОС где наш сервер будет развернут. Поскольку мы используем core можно выбрать как Linux так и Windows. Я выбираю Windows так как мне комфортнее, но в целом разницы нет. Создайте свой веб сайт, проверьте что он действительно работает и давайте начнем настройку деплоймента. Для этого, в корневую папку проекта нужно добавить два файла:


  • .deployment // описывает что нам нужно вызвать для деплоймента)
  • build.cmd // описывает что именно делать во время деплоймента

На первый взгляд, build.cmd выглядит довольно страшно, но внутри происходят вполне тривиальные вещи:


  • Проверяется наличие Node.js // вы не поверите, но в процессе деплоймента Microsoft использует Node.js
  • Выставляются переменные с путями // где будут лежать наши файлы и файлы сервера
  • Устанавливается kudusync // утилита для копирования артефактов в папку сервера
  • Выполняется команда dotnet publish // компилируем наш код
  • Результаты паблиша копируются в wwwroot папку. // отдаем результат серверу для запуска нашего приложения

После того, как мы добавили эти файлы, нам осталось только связать наше веб приложение с репозиторием на GitHub. Для этого выбираем наше приложение, затем DeploymentOptions -> Github -> Project -> Branch. Кстати там же можно настроить и нагрузочные тесты. Все, теперь осталось дождаться пока все синхронизируется и задеплоится.


Кодовое название проекта, который потом превратился в Azure было "Project Red Dog".

Git хуки


Говорить о CI и не упомянуть о git hooks наверное было бы не правильно. Тем более, что именно хуки являются тем инструментом, благодаря которым стали возможны описанные в статье возможности.


Git hooks, или перехватчики — это специальный механизм, который позволяет выполнять пользовательские сценарии до или сразу после важных событий в вашем репозитории. С помощью них вы можете модифицировать коммиты, вести change log, делать автоматическое форматирование и вообще все, на что хватит прав в системе. По умолчанию, они находятся в .git/hooks.


Немного не в стиле, но в dotnet тоже есть хуки, такие как pre-build и post-build. Они так же позволяют выполнять пользовательские сценарии.

Итоги


Сегодня, мы научились настройке полного цикла CI для проекта основанного на Git репозитории. Однако один из главных вопросов все еще остался без ответа — а надо ли вам CI как таковой. С одной стороны:


… времени пение берет самую малость, а пользы от этого пения, между прочим, целый вагон… ©

С другой стороны, если вы делаете POC или если у вас сроки уже давно сгорели, наверное, CI придется не код двору.


Другими словами — использовать CI или не использовать решать именно вам. Но если вы его еще не пробовали — рекомендую к дегустации.


Теги


  • InitialMaster
  • NewFSharpProject
  • TravisSupport
  • CodeCov
  • DeployToAzure

Ссылки



Со всем почтением.

  • +10
  • 6,7k
  • 4
Поделиться публикацией

Похожие публикации

Комментарии 4

    +1

    Огромное спасибо за публикацию. Давно хотел попробовать Travis но хорошей, подробной статьи (и к тому-же для F# проекта) на русском мне не попадалось.


    Travis не делает различий между C# и F#, и директивы fsharp нет

    Здесь я представил свои безуспешные попытки скормить метку fsharp.


    Если не возражаете, то вопрос оффтопик


    "Строим полноценное веб приложение на F# + Giraffe с нуля."

    Почему вы решили использовать именно Giraffe, а не, например, Saturn или Suave?




    Тут


    Кстати, если посмотрите на процесс билда, вы увидите, что он проходит под Lixux, что не может не радовать.

    наверное, опечатка: Linux.

      0
      Спасибо за комментарий, рад что статья понравилась. Хотя судя по реакции она оказалась слишком длинной и пространной.

      Здесь я представил свои безуспешные попытки скормить метку fsharp.


      Да, было смешно, с F# с Travis я пытался подружить в первый раз и немного «завис» на этом моменте.

      Почему вы решили использовать именно Giraffe, а не, например, Saturn или Suave?


      Две причины. Я пробовал Suave до этого. Субъективно он мне понравился даже больше чем Jiraffe по причине лаконичности. Но у меня с ним были проблемы с деплойментом и какими то еще моментами уже точно не помню. А Giraffe проще в настройке и перспективнее так как многие инструменты заимствует из .net core. Соответственно с/на Giraffe перейти проще.

      За опечатку спасибо, поправил.
        0
        Хотя судя по реакции она оказалась слишком длинной и пространной.

        Совсем напротив


        Я пробовал Suave до этого. Субъективно он мне понравился даже больше чем Jiraffe по причине лаконичности. Но у меня с ним были проблемы с деплойментом и какими то еще моментами уже точно не помню. А Giraffe проще в настройке и перспективнее так как многие инструменты заимствует из .net core. Соответственно с/на Giraffe перейти проще.

        Спасибо, понял. Хороший совет при выборе начинать с деплоя, чтобы потом не застрять на этом.

      0
      Continues => Continuous (https://en.wikipedia.org/wiki/Continuous_integration)

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое