- PVSM.RU - https://www.pvsm.ru -

Continuous Integration для самых маленьких

Вы все еще публикуете проект вручную? Тогда мы идем к вам

Continuous Integration для самых маленьких
Под катом гайдлайн по внедрению CI для .NET проектов «с нуля», включающий:

  1. Автоматические ежедневные сборки
  2. Уведомления о проблемах
  3. Интеграцию с баг-трекером и системой контроля версий
  4. Версионирование продукта
  5. Версионирование базы данных
  6. Автоматизированные выкладки и бекапы


Начнем с того, что же такое «непрерывная интеграция».

Непрерывная интеграция (англ. Continuous Integration) — это практика разработки программного обеспечения, которая заключается в выполнении частых автоматизированных сборок проекта для скорейшего выявления и решения интеграционных проблем.

С практической точки зрения это значит, что в любой момент времени у вас должна быть «живая актуальная версия продукта», которую можно протестировать или продемонстрировать.
Для этого нужно:

  1. Чтобы разработчики вносили свой код в VCS по крайней мере каждый день
  2. Сборка продукта происходила в автоматическом режиме
  3. Выкладка продукта (в том числе, обновление базы данных) происходила в автоматическом режиме
  4. Тестирование продукта происходило в автоматическом режиме (насколько это возможно)

Continuous Integration относится к agile-практикам. Agile предполагает итерационный процесс, с многократным повторением цикла разработки, тестирования и выкладки продукта. Автоматизация рутинных процессов позволяет избежать многократного выполнения рутинных операций.

Работа с VCS

Для начала нам потребуется тестовое окружение для тестирования приложения. Если цикл тестирования достаточно длительный, а разработка ведется быстро, то разумно выделить еще и dev-окружение. Структура VCS будет отражать ваши окружения.
Все разработчики работают с основной веткой разработки, затем определенная версия фиксируется и мержится в тестовую ветку. Из тестовой ветки происходит выкладка на тестовое окружение. Производится тестирование, внесение фиксов и обратный мерж в дев. Протестированная версия отправляется в релизную ветку и от туда публикуется на продакшн. В случае нахождения ляпов на продакшне (к сожалению, такое бывает) чиним в авральном режиме из продакшн ветки и опять мержим в DEV-ветку.

В философии GIT будет немного иначе, так как при работе с GIT не принято комитить в master и даже dev. Вместо этого практикуется подход фича-бренч. Про git workflow можно почитать здесь: habrahabr.ru/post/60030/ [1]. Вообще, все предлагаемые структуры VCS преследуют одну цель: избежать ситуации, когда ветка разработки не стабильно, а совершенно необходимо что-то быстро «пофиксить» или «допилить» и выложиться. При выборе своей структуры задайте себе вопрос «смогу ли я выложиться на продакшн в течение одного дня и не поломать все. Если ответ «да», то структура вам подходит.

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

Конфигурации

Как бы мы не старались сделать окружения идентичными, конфигурационные файлы будут отличаться: тестовые и реальные аккаунты, токены, ID, внешние веб-сервисы и другое. .NET предлагает отличное средство поддержание конфигураций в актуальном виде: трансформации конфигов. Почему-то «из коробки» они включены только в веб-приложения. К счастью трансформации достаточно легко добавить и в другие приложения.

  <UsingTask TaskName="TransformXml"
    AssemblyFile="$(MSBuildExtensionsPath32)MicrosoftVisualStudiov10.0WebMicrosoft.Web.Publishing.Tasks.dll" />
  <Target Name="AfterCompile" Condition="Exists('App.$(Configuration).config')">
    <!--Generate transformed app config in the intermediate directory-->
    <TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config"
      Transform="App.$(Configuration).config" />
    <!--Force build process to use the transformed configuration file from now on.-->
    <ItemGroup>
      <AppConfigWithTargetPath Remove="App.config" />
      <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
        <TargetPath>$(TargetFileName).config</TargetPath>
      </AppConfigWithTargetPath>
    </ItemGroup>
  </Target>

Начинающие разработчики часто впадают в ступор при отладке трансформаций Web.config’а на локальных машинах: в отличие от App.config’ов они хранятся на уровне приложения, а не в папке bin, поэтому при локальной сборке трансформации не происходит. Трансформации применяются при публикации проекта. Если вам действительно нужно обойти это ограничение, можно создать файл Web.template.config и добавить трансформацию на PostBuild приложения. Не забудьте только убрать таск трансформации из проекта, иначе трансформация будет применяться дважды.
Если в приложении используют другие технологии, все-равно лучше иметь один основной конфигурационный файл с общими настройками и дополнительные конфигурационные файлы для изменения эталонного конфига. Это избавит вас от копипаста одинаковых настроек.

Версионирование продукта

.NET предоставляет 2 атрибута для версионирования сборок
[assembly: AssemblyVersion(«1.0.0.0»)]
[assembly: AssemblyFileVersion(«1.0.0.0»)]
Первые две цифры – это мажорный и минорный номер версии. Эти цифры интерпретируют инкремент изменения функционала. Третья цифра – это, так называемый, номер сборки. Каждый день, пока версия находится в разработке эта версия увеличивается. И последний номер – это номер ревизии. Этот номер увеличивается каждый раз при сборке версии на билд-сервере в течение дня. Подробно о политике версионирования в .NET написано в книге CLR via C# Рихтера.
Автоматическое версионирование может быть настроено разными способами. Подробно эта тема обсуждается здесь: stackoverflow.com/questions/1126880/how-can-i-auto-increment-the-c-sharp-assembly-version-via-our-ci-platform-hudso [2].

Основные подходы

  1. Использование версии вида 1.0.*.* (плохо сочетается с билд-сервером)
  2. Использование единого файла SharedAssemblyInfo для управления версиями всех сборок из оного места (создается один файл с номером версии и добавляется “as a link” ко всем проектам
  3. Использование msbuild, вместо файла AssemblyInfo
  4. Для TFS можно использовать WWF-Activity

На мой взгляд удобнее всего использовать msbuild и проставлять значение с помощью CI-сервера:

<Major>1</Major>
 <Minor>0</Minor>
<Build>$(BUILD_NUMBER)</Build>

Все современные CI-решения предлагают такую переменную во время сборки. Для того, чтобы этот подход работал, нужно добавить импортировать msbuild-таски из msbuildtasks.tigris.org/ [3] и добавить в конце проекта:

<Import Project="$(MSBuildExtensionsPath)MSBuildCommunityTasksMSBuild.Community.Tasks.Targets" Condition="Exists('$(MSBuildExtensionsPath)MSBuildCommunityTasksMSBuild.Community.Tasks.Targets')" />
  <Target Name="BeforeBuild" Condition="Exists('$(MSBuildExtensionsPath)MSBuildCommunityTasksMSBuild.Community.Tasks.Targets')">
    <Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)" />
    <AssemblyInfo CodeLanguage="CS" OutputFile="AssemblyFileInfo.cs" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyConfiguration="$(Configuration)" Condition="$(Revision) != '' " />
  </Target>

Версионирование базы данных

Я не знаю ни одного проекта, в котором не приходилось бы менять базу данных. .NET предлагает следующие решения:

SSDT-проект msdn.microsoft.com/ru-ru/data/tools.aspx [4]

Плюсы: процесс создания и редактирования БД напоминает то, как бы вы это делали с ManagementStudio.
Минусы: сложность написания миграционных скриптов. Т.к. инкрементальные изменения строит сам проект, сохранность данных обеспечивается за счет pre-deploy и post-deploy-скриптов. Придется опираться на наличие/отсутствие полей в БД или «изобретать» таблицу schema version, уже реализованную в миграционных движках.

ECM7 Migrator code.google.com/p/ecm7migrator/ [5]

Движок миграций с открытым кодом. Проект поддерживает читатель dima117 [6].
Плюсы: поддерживаются разные СУБД, если что-то вас не устраивает, код открыт. Мигратор поддерживается и обрастает новыми функциями. И, пожалуй, самое важное, поддерживает несколько ключей миграций в одной базе данных. Это может быть очень полезно, если ваше приложение модульное и поддерживает плагины в том или ином виде. Каждый плагин может выполнять свои миграции и при этом использовать одну БД
Минусы: нет плюшек Entity Framework Migrations.

Entity Framework Migrations blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx [7]

Плюсы: работает из коробки, автоматически создает миграции по Db-контексту, понимает команды из консоли visual studio, миграции публикуются с помощью стандартного Publish из Visual Studio.
Минусы: зависит от Entity Framework.


Я успел попробовать все 3 решения. Если вы используете EF, выбор – однозначно EF Migrations, для .NHibernate можно воспользоваться ECM7 Migrator. SSDT-проект подойдет любителям визардов и окошек.

Автоматизация публикации приложения

В 2012 студии система публикации web-проектов была значительно улучшена. В первую очередь это касается профилей публикации. Если вы публикуете приложение в azure, то профиль можно просто скачать с портала. В противном случае его нужно будет создать.
Continuous Integration для самых маленьких

Continuous Integration для самых маленьких

Как видно на скриншотах, в последней версии WebDeploy можно запустить EF-миграции с помощью всего одной галочки. Кроме этого публикация из студии умеет заменять строку подключения без использования трансформации.
Подробно про трансформации и публикации написано у Троя Ханта в статье: www.troyhunt.com/2010/11/you-deploying-it-wrong-teamcity.html [8]. Сейчас нас интересует пятый шаг его гайдлайна, а именно, автоматическая публикация с помощью билд-сервера: www.troyhunt.com/2010/11/you-deploying-it-wrong-teamcity_26.html [9]. Я большой фанат TeamCity, поэтому рассмотрим именно эту CI.

Автоматизация публикации Windows-служб

Для автоматического создания windows-служб проще всего воспользоваться командной sc:
sc [] creat|stop|start binpath= start= demand
Единственный тонкий момент – каким образом доставить бинарники в . Это можно сделать разными способами: залить по FTP, воспользоваться PowerShell, использовать xcopy/robocopy или rsync в расшаренную папку на сервере. Выбор зависит от вашего сетевого окружения и требований к безопасности.

TeamCity

Использование средств, описанных выше, сэкономит ваше время. Пойдем дальше и установим билд-сервер. Скачиваем TeamCity: www.jetbrains.com/teamcity/ [10] и запускаем инсталятор, жмем везде «Ок».
Два основных понятия TeamCity – «проект» и «билд». «Проект» соответствует вашему солюшну, а под билдом понимается любая осмысленная совокупность действий над вашим проектом: будь то сборка, запуск тестов, выкладка на сервер, создание бекапов и так далее.

Автоматизированная выкладка

Первым шагом, дадим возможность выкладывать новую версию тему у кого Visual Studio не устновлена.
Основная идея, это запустить msbuild-шаг с дополнительными параметрами, создайте новый Build Definition и выберите первый шаг Msbuild. В параметры командной строки нужно передать:

/P:Configuration=%env.Configuration%
/P:DeployOnBuild=True
/P:DeployTarget=MSDeployPublish
/P:MsDeployServiceUrl=https://%env.TargetServer%/MsDeploy.axd
/P:AllowUntrustedCertificate=True
/P:MSDeployPublishMethod=WMSvc
/P:CreatePackageOnPublish=True
/P:UserName=AutoDeployAdministrator
/P:Password=Passw0rd

Эти параметры укажут, куда нужно публиковать.
Для того, чтобы опубликовать миграции потребуется дополнительный шаг Command Line:

SET AssemblyName=MyMvc4App
SET StartUpDirectory=MyMvc4Appbin
SET ConnectionString=Server=tcp:XXXX.database.windows.net,1433;Database=XXXX;User ID=XXXX;Password=XXXX;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;MultipleActiveResultSets=True
SET ConnectionStringProvider=System.Data.SqlClient
SET ConfigFilePath=%CD%MyMvc4Appweb.config
SET MigrateExe=packagesEntityFramework.5.0.0toolsmigrate.exe
%MigrateExe% %AssemblyName%.dll /startUpDirectory:%StartUpDirectory% /startUpConfigurationFile:"%ConfigFilePath%" /connectionProviderName:"%ConnectionStringProvider%" /connectionString:"%ConnectionString%" /verbose

Сохраняете билд. Теперь кто угодно может опубликовать последнюю версию из VCS. Хорошая практика публиковаться на дев-стенд каждый день до начала рабочего дня. Таким образом вы будете отлавливать интеграционные проблемы максимально быстро.

Rolling builds

Хорошая практика – настроить триггер на запук билда, который просто будет запускать сборку вашего солюшна после каждого изменения в репо или по таймеру (например, каждые 10 минут), если над проектом работает одновременно очень много разработчиков. К этому билду следует подключить нотификацию. TeamCity поддерживает нотификации по почте, jabber, с помощью плагина для VisualStudio и Tray-приложения. Выберите вариант по вкусу. Мне по душе приложение в трее. Если билд сломался – надо срочно чинить.

Как не ломать билд

Какие бы практики вы не вводили, чтобы не делали, не существует никакого способа дать 100% гарантию, что разработчики не будут вносить в VCS код, который даже не собирается. Этот вопрос должен решаться с помощью административных мер: не уходить с работы, не проверив, что билд после твоих изменений собрался, кто сломал, тот и чинит. В одной компании, где я работал проблема со слишком часто сломанным билдом была решена правилом: сломал билд – принеси тортик. Сначала торты ели каждый день, потом перестали.

Автоматический запуск тестов

Этот шаг следует разделить на 2: юнит-тесты и интеграционные и приемочные тесты. Это важно, потому что юнит-тесты должны работать на любом окружении. Все внешние зависимости заменяются фейками.

Запуск юнит-тестов

Здесь все просто. Выбирайте Build Step “Nunit” или “MsTest”, вводите паттерн **.Test*.dll все остальное TeamCity сделает за вас, при условии, что вы используете паттерн .Tests в названии ваших тестовых проектов.

Запуск интеграционных и приемочных тестов

Эти тесты могут зависеть от многих факторов и их запуск может предполагать накатывание бекапов или скриптов инициализации. Возможно, что вы захотите построить дополнительные отчеты о запуске таких тестов. Лучше не захламлять ваш проект с выкладкой и создать для них специальный билд. TeamCity позволяет создавать цепочки билдов. В Build triggers билда с приемочными/интеграционными тестами вы можете добавить триггер, срабатывающий при удачном прохождении билда с выкладкой. Создание такого билда для запуска приемочных тестов я описал в топике: habrahabr.ru/post/182032/ [11].

Бекапы

Создание бекапов при выкладке также может быть автоматизировано. Для бекапа файловой системы, лично я не нашел ничего лучше, чем nnbackup: www.nncron.ru/index_ru.shtml [12].

nnbackup -n 10 verz -i <Src> -o <Destination> -s -e –v

Команда создает аривирует в zip папку source и копирует архив в destination. Устанавливать nnbackup на целевые машины или вызывать с билд-сервера: вопрос ваших предпочтений, расположения билд сервера, сетевых издержек и безопасности.

Бекапить sql-сервер можно с помощью T-SQL

BACKUP DATABASE AdventureWorks2012 TO DISK='X:SQLServerBackupsAdventureWorks1.bak', DISK='Y:SQLServerBackupsAdventureWorks2.bak', DISK='Z:SQLServerBackupsAdventureWorks3.bak' WITH FORMAT, MEDIANAME = 'AdventureWorksStripedSet0', MEDIADESCRIPTION = 'Striped media set for AdventureWorks2012 database; GO
RESTORE DATABASE AdventureWorks FROM DISK='$(Backup)'

Т.е. для автоматического бекапа, вам потребуется добавить еще один Command Line-шаг. Или вы можете воспользоваться msbuild-тасками из того-же самого community-пакета, а для nnbackup написать свой msbuild-таск.
Можно пойти дальше и поставить триггер на запуск автоматического отката из бекапа, если приемочные тесты не прошли. Тогда у вас будет цепочка билдов: Выкладка » Приемочные тесты » Откат из бекапа.

Интеграция с системой ведение проекта и баг-трекером

До этого момента, мы уже сделали много полезного. Осталась одна неприятная особенность. Билд-сервер все-еще никак не связан с нашим процессом разработки. Он ничего не знает о задачах текущей итерации разработки.
TeamCity поддерживает интеграцию с YouTrack, Jira и Bugzilla. Интеграция выполняется буквально в 2 клика. После этого, указывая номер задачи в комментарии к комиту, вы увидите ссылки на соответствующие задачи в информации о билде.
YouTrack поддерживает обратную интеграцию. Основное преимущество: YouTrack будет автоматически указывать номер билда, в котором баг или фича были закрыты.

Артефакты билда

Если ваше приложение коробочное, то вам, наверняка нужно озаботиться созданием инсталляторов или deploy packages для отгрузки клиентам. Создание инсталляторов – отдельная тема, я не буду ее рассматривать в рамках этого топика. Важно, что для коробочного продукта, инсталлятор или пакет публикации – это самая важная часть релиза. Как раз для этого и придуманы артефакты билда.

Артефакт – это любой файл, являющийся значимым результатом выполнения билда. Для билдов с инсталляторами вы можете указать my-installer.exe или my-package.zip, как маску для артефактов билда и TeamCity отобразит их в соответствующей вкладке.
Вот здесь и пригодится интеграция с Issue Tracker’ом. Тестировщик или менеджер может посмотреть закрытую задачу, перейти по ссылке с билдом и забрать версию с исправлениями из артефактов билда.

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

Автор: marshinov

Источник [13]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/razrabotka/41163

Ссылки в тексте:

[1] habrahabr.ru/post/60030/: http://habrahabr.ru/post/60030/

[2] stackoverflow.com/questions/1126880/how-can-i-auto-increment-the-c-sharp-assembly-version-via-our-ci-platform-hudso: http://stackoverflow.com/questions/1126880/how-can-i-auto-increment-the-c-sharp-assembly-version-via-our-ci-platform-hudso

[3] msbuildtasks.tigris.org/: http://msbuildtasks.tigris.org/

[4] msdn.microsoft.com/ru-ru/data/tools.aspx: http://msdn.microsoft.com/ru-ru/data/tools.aspx

[5] code.google.com/p/ecm7migrator/: https://code.google.com/p/ecm7migrator/

[6] dima117: http://habrahabr.ru/users/dima117/

[7] blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx: http://blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx

[8] www.troyhunt.com/2010/11/you-deploying-it-wrong-teamcity.html: http://www.troyhunt.com/2010/11/you-deploying-it-wrong-teamcity.html

[9] www.troyhunt.com/2010/11/you-deploying-it-wrong-teamcity_26.html: http://www.troyhunt.com/2010/11/you-deploying-it-wrong-teamcity_26.html

[10] www.jetbrains.com/teamcity/: http://www.jetbrains.com/teamcity/

[11] habrahabr.ru/post/182032/: http://habrahabr.ru/post/182032/

[12] www.nncron.ru/index_ru.shtml: http://www.nncron.ru/index_ru.shtml

[13] Источник: http://habrahabr.ru/post/190412/