×Закрыть

Построение «правильного» процесса разработки на платформе.NET

Содержание

  1. Введение
  2. Windows Server 2008
  3. Visual Studio 2008
  4. SVN
  5. Nant
  6. Cruise Control.NET
  7. Заключение

Введение

Достаточно часто программисты игнорируют (а возможно и не знают) некоторые приемы, позволяющие значительно облегчить весь процесс разработки.

Предлагаю вам взглянуть на «альтернативный», но не единственный правильный процесс разработки в команде, позволяющий значительно сократить усилия, чтобы еще осталось время попить пивка с коллегами.

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

Прежде чем начать предлагаю вам ответить на ряд вопросов, которые будут раскрыты в конце статьи:

  • сколько действий вам нужно сделать, чтобы собрать проект, включая установку Windows Services, дейплоймент веб сайта, запуск тестов (вы же пишете тесты?)?
  • что вам нужно сделать, чтобы настроить всю инфраструктуру на машине нового члена команды?
  • как часто у вас собирается проект на Тест-сервере?
  • насколько отличается процесс деплоймента на машине девелопера, qa сервере и production сервере?
  • сколько человек в команде сумеют развернуть проект на production сервере?

Итак, приступим...

Windows Server 2008

При написании статьи использовалась именно эта операционная система и этому есть ряд причин:

  • прежде всего это не Windows Vista
  • в Windows Server 2008 может быть установлен IIS 7, который очень легко конфигурируется из командной строки
  • эта операционная система стоит у автора статьи (что является основной причиной:)).

Т. к. мы собираемся писать скрипты для IIS 7, хотелось бы сделать ряд замечаний по поводу других операционных систем.

Windows Vista — никаких трудностей, скорее всего, не будет, весь код из данной статьи без проблем будет работать.

Windows Server 2003 — могут возникнут затруднения при переносимости скриптов, т. к. в Server 2003 установлен IIS 6. Но все примеры можно применить, приложив некоторые усилия и бубен.

Windows XP SP2/3 — возникнут проблемы с переносимостью скриптов для настройки IIS т. к. в XP возможно установить только IIS 5.1 (Фанаты Windows Script Host могут справяться и с этим заданием).

Более ранние версии Windows — к этой аудитории у меня есть резонный вопрос — вы в каком веке живете?

Итак, в Windows Server 2008 должен быть установлен IIS 7. Чтобы съэкономить место, предлагаю вам зайти на этот ресурс, там подробно описано как это сделать (это первая ссылка из гугла на запрос «как установить IIS 7»).

Visual Studio 2008

Пример в статье будет разработан именно в этой среде, т. к. Visual Studio — это основное средство любого. NET разработчика.

На протяжении всей статьи будет рассматриваться процесс разработки веб приложения со слоем бизнес логики, unit тестами и windows сервисом.

В качестве фрэймворка для тестов будет использоваться Nunit, поэтому прежде чем приступить к разработке какркаса приложения идем по этой ссылке и качаем Nunit (у меня установлена версия 2.4.7, но вполне возможно установить и более свежую версию).

Запускаем Visual Studio 2008

File→New→Project

Выбираем Other Projetc Types → Blank Solution

Name: TestApp, Location = «C: \», папка «C: \TestApp» будет создана автоматически.

Blank Solution

Рис 1. Создание пустого проекта.

Добавляем новый веб сайт

новый веб сайт

Рис 2. Новый Веб сайт, шаг 1

Меняем Language на «C#» и Location на «C: \TestApp\WebApp»

Меняем Language и Location

Рис 3. Новый веб сайт, шаг 2

Создаем проект «BusinessObjects», содержащий набор бизнес объектов

BusinessObjects

Рис 4. Новый проект — BusinessObjects, шаг 1.

Тип проекта — «Class Library», имя — «BusinessObjects».

BusinessObjects 2

Рис 5. Новый проект — BusinessObjects, шаг 2.

После создания проекта удаляем автоматически созданный класс с именем «Class1» за ненадобностью.

Создаем проект для тестов.

проект для тестов

Рис 6. Новый проект — Tests.

Удаляем «Class1»...

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

создаем проект для Windows сервиса

Рис 7. Новый проект TestService.

После всех манипуляций должен получиться каркас проекта аналогичный представленному на Рис 8:

каркас проекта

Рис 8. Каркас проекта.

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

Итак, следуя принципам TDD, начнем создание логики с написания тестов. Задача предельно проста — необходимо создать класс, содержащий один единственный метод, принимающий string, и возвращающий «Hello » + переданный параметр, если передать null должен быть выкинут ArgumentNullException.

В тестовой сборке добавляем референс на Nunit.framework

добавляем референс на Nunit.framework

Рис 9. Добавление ссылки на сборку nunit.framework, шаг 1.

шаг 2

Рис 10. Добавление ссылки на сборку nunit.framework, шаг 2.

Создаем класс с именем «TestClass», код с пояснениями представлен ниже:

using System;
using NUnit.Framework;

namespace Tests
{
    [TestFixture] //все тестовые классы должны быть с аттрибутом TestFixture
    public class TestClass //обязательно указывать public, иначе класс не будет запущен
    {
        [Test] //каждый тест должен быть с аттрибутом Test
        public void GetHelloTest()
        {
            Entity entity = new Entity();
            //Провряем на идентичность полученных результатов с ожидаемыми
            Assert.AreEqual("Hello Eugene", entity.GetHello("Eugene"));
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))] //ожидаем ArgumentNullException, если Exception-a не будет, тест не пройдет
        public void GetHelloExceptionTest()
        {
            Entity entity = new Entity();
            entity.GetHello(null);
        }
    }
}

Если сейчас попробовать собрать проект, ничего не выйдет, т. к. не существует класса с именем Entity. Создадим его.

Добавляем класс к сборке BusinessObjects, называем его Entity:

Entity

Рис 11. Новый класс в сборке BusinessObjects.

Добавляем референс в проекте Tests на проект BusinessObjects для того, чтобы тесты имели возможность использовать только что созданный класс:

Добавляем референс

Рис 12. Добавление ссылки на сборку BusinessObjects, шаг 1

шаг 2

Рис 13. Добавление ссылки на сборку BusinessObjects, шаг 2

Добавляем using в тестовый класс:

using BusinessObjects;

После этих шагов пробуем собрать проект, и опять ничего не получится, т. к. мы не добавили метод с именем GetHello. Добавим его. Весь код класса Entity приведен ниже.

using System;

namespace BusinessObjects
{
    public class Entity
    {
        public string GetHello(string name)
        {
            if(name == null)
            {
                throw new ArgumentNullException();
            }
            return "Hello " + name;
        }
    }
}

После всех проделанных шагов проект успешно собирается.

Пришло время проверить, правильно ли проходят тесты (хотя это нужно было делать немного раньше — до добавления логики к методу GetHello, и только после этого имплементировать логику в методе, но об этом я предлагаю почитать в литературе посвященной TDD и DDD).

Я предпочитаю использовать Resharper для запуска тестов, т. к. этот плагин очень юзабильный и позволяет запускать тесты из студии (а также содержит большое количество фичей для рефакторинка и поиска), единственная проблема — платность продукта (триал версию можно скачать тут). Также существует бесплатный плагин для студии — Test Driven. NET который нужнен только для тестов (можно скачать тут).

В конкретном случая, я предлагаю воспользоваться отдельным приложением Nunit GUI, т. к. данное приложение устанавливается вместе с nunit.framework.

Идем в Пуск -> All Programs -> NUnit 2.4.7 запускаем NUnit GUI

Жмем File -> Open Project -> «C: \TestApp\Tests\bin\Debug\Tests.dll»

Получаем приблизительно следующее:

Nunit GUI, запуск тестов

Рис 14. Nunit GUI, запуск тестов

Жмем «Run», должны получить такую же картинку как на рис 15. Если видите красный цвет, тогда где-то ошибка и нужно вернуться на пару шагов назад и проверить правильность кода.

Nunit GUI, «послезапуск» тестов

Рис 15. Nunit GUI, «послезапуск» тестов

Итак, логика написана, тесты пройдены, пришло время написать пару строк кода на веб странице.

Добавляем ссылку в веб сайте на сборку BusinessObjects.

Добавление ссылки

Рис 16. Добавление ссылки на сборку BusinessObjects, шаг 1

шаг 2

Рис 17. Добавление ссылки на сборку BusinessObjects, шаг 2

Открываем файл «Default.aspx.cs» в веб сайте. Добавляем using для сборки бизнес объектов, а также для сборки System.Web.Configuration чтобы использовать класс WebConfigurationManager (для чего это нужно будет описано в разделе посвященном Nant-у). Также добавляем несколько строк кода.

Весь код приведен ниже:

using System;
using System.Web.Configuration;
using BusinessObjects;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Entity entity = new Entity();
        Response.Write(entity.GetHello("Eugene"));
        Response.Write("<br/>");
        Response.Write("Environment: " + WebConfigurationManager.AppSettings["env"]);
    }
}

Код достаточно прозрачный, думаю объяснения излишние.

Открываем файл web.config, и изменяем блок appSettings следующим образом:

  <appSettings>
    <add key="env" value="DEV"/>
  </appSettings>

После чего можно нажать F5 и посмотреть, что получится в браузере

SVN

Каркас проекта создан, пора его залить в репозиторий. Существует несколько наиболее популярных видов репозитория, к которым относятся CVS, SVN, VSS и др. В конкретном случае будет использоваться SVN, т. к. его невероятно просто настроить, а также он тесно интергирован с Cruise Control. NET, но обо всем по порядку.

Итак, идем по этой ссылке качаем «Visual SVN Server» (на момент написания статьи актуальной была версия 1.7.2). После чего запускаем только что скачанный инсталлер:

После принятия лицензионного соглашения, есть возможность настроить пути установки сервера и репозитория (Рис 18). Для простоты оставим все как есть.

установка сервера и репозитория

Рис 18. Установка Visual SVN Server.

После установки SVN сервера нужно сделать 2 очень важных шага.

1. Добавить путь к папке с бинарными файлами SVN Server (в моем случае это «C: \Program Files\VisualSVN Server\bin») в PATH (Рис 19).

Добавление пути

Рис 19. Добавление пути к Visual SVN Server bin к PATH

2. Добавить новый элемент в User Variables с именем «SVN_EDITOR» и значением «notepad» (можете выбрать любой редактор):

User Variables

Рис 20. Добавление нового элемента в User Variables

После этого идем в Start -> All Programs -> Visual SVN -> VisualSVN Server Manager и создаем нового юзера с именем «Eugene» и паролем «1» (Рис 21, 22):

Создание нового пользователя

Рис 21. Создание нового пользователя, шаг 1

шаг 2

Рис 22. Создание нового пользователя, шаг 2

Далее создаем новый репозиторий (Рис 23):

Создание нового репозитория

Рис 23. Создание нового репозитория, шаг 1

Имя репозитория будет «TestApp». Также предлагается создать стандартную структуру папок (trunk, branches, tags), данная структура подразумевает разработу с версионированием, т. е. вся разработка ведется из папки trunk, когда выходит новая версия продукта, она перекладывается в папку branches, а в папке trunk продолжается разработка над следующей версией продукта. Таким образом папка trunk будет содержать актуальную и еще не выпущенную версию продукта, а папка branches будет содержать папки v1, v1.1, v2, и т. е. версии, которые уже в релизе.

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

шаг 2

Рис 24. Создание репозитория, шаг 2

Итак репозиторий создан, пора залить в него проект. Для того, чтобы проводить манипуляции с svn существует несколько достаточно удобных клиент-интерфейсов, к которым относятся TortoiseSVN, VisualSVN и т. д. Но мы будем пользоваться командной строкой, чтобы наиболее четко ощутить все прелести примера.

Хотелось бы еще пару слов сказать о менеджерах командной строки (или файловых менеджерах). Существует много вариантов — NC, VC, Total Commander, и т. д, но как сказал один очень сильный программист (Ден привет:)): «Все нормальные поцаны используют Far». Поэтому в этом примере будем использовать именно этот менеджер.

Заходим в VisualSVN Server Manager и копируем url к репозиторию (Рис 25):

Копирование url

Рис 25. Копирование url к репозиторию в clipboard

После чего открываем Far, переходим в корень диска C:, создаем папку «Projects», переходим в «C: \Projects» и выполняем команду «svn co» + вставляем только что скопированный url. Получится приблизительно то же что и на Рис 26:

Checkout

Рис 26. Checkout из репозитория

Жмем «Enter», будет предложено отклонить®, принять временно (t) или принять (p) сертификат сервера. Принимаем сертификат (p). Важно выбрать именно (p), почему — будет описано в разделе посвященному CruiseControl. NET. Также возможно потребуется ввести User Name и Password для юзера репозитория — вводим их для юзера, который был создан в VisualSVN Server Manager.

После проделанных шагов в консоль будет выведено сообщение «Checked out revision 0.» и будет создана папка «TestApp».

Если зайти в только что созданную папку «C: \Projects\TestApp» можно обнаружить скрытую папку с именем «.svn» — эту папку нельзя ни в коем случае удалять, т. к. именно в ней хранятся все локальные изменения, и системные файлы SVN.

после svn checkout

Рис 27. Папка TestApp после svn checkout.

Наконец то мы дошли до залития всего проекта в репозиторий. Переходим в C: \TestApp, удаляем все ненужные файлы и папки, такие как _ReSharper. TestApp, TestApp.4.5.resharper.user, bin, obj. Перемещаем все содержимое из C: \TestApp в C: \Projects\TestApp.

Переходим в C: \Projects\TestApp и выполняем команду

«svn add TestService Tests BusinessObjects TestApp.sln WebApp». Результат должен быть приблизительно таким же как показано на рис. 28:

Добавление проекта в репозиторий

Рис 28. Добавление проекта в репозиторий.

Далее, нужно залить все в репозиторий. Выполняем команду «svn ci». Будет открыт блокнот с перечнем файлов для коммита (именно для этого нужно было добавлять новую переменную SVN_EDITOR) в переменные среды. Сохраняем и закрываем файл (если не сохранить и попробовать закрыть, вам будет предложено решить — нужно ли делать коммит или нет).

С этого момента вы можете использовать репозиторий с проектом. В данной статье не будет рассматриваться как осуществляется работа с репозиторием svn. Чтобы получить более подробную информацию, советую обратиться к специализированным источникам.

Nant

Следующим шагом в построении процесса разработки должна стать разработка автоматизированного процесса деплоймента и запуска тестов. Для этих целей, как правило, используются 2 подхода.

Первый подход — создание *.bat файлов с кодом для развертывания проекта. У данного подхода существует ряд ограничений, основным из которых является, неудобство использования.

Второй подход — использование специализированных средств, к которым относятся Nant (для. NET) или Ant, Continuum (для Java) и др.

В данной статье будет использоваться open source консольное приложение Nant. Загрузить это тул можно здесь — nant.sourceforge.net. Советую загружать не ниже версии 0.86-beta1, т. к. только она на момент написания статьи поддерживает тип проекта Visual Studio 2008.

После загрузки, распаковываем архив, и помещаем его, скажем, в папку «C: \Program Files\nant-0.86».

Добавляем путь к «bin» в PATH (Рис 29):

пути к бинарным файлам Nant в PATH

Рис 29. Добавление пути к бинарным файлам Nant в PATH

Открываем Far, пишем в консоли «nant» и жмем «Enter». Если видим вывод, показанный на рис. 30, значит все впорядке — путь к nant работает правильно (если фар был открыт до добавления значения в PATH, его нужно закрыть и открыть заново).

Проверка пути к nant

Рис 30. Проверка пути к nant

Далее предлагается краткий обзор основ конфигурирования используя nant.

Для того чтобы конфигурировать билд, необходимо иметь 1-н или более файлов *.build, которые представляют собой обыкновенные xml файлы. Точкой входа является файл default.build.

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

Итак, создаем файл с именем default.build (В какой папке создать файл абсолютно без разницы). Добавляем в него следующее содержимое:

<?xml version="1.0"?>
<project name="TestApp">

  <target name="test">
    <echo message="Test message" />
  </target>

</project>

После чего в консоли пишем «nant test». Вывод должен быть следующим:

test:
     [echo] Test message
BUILD SUCCEEDED

Мы только что создали target — основной элемент, на основании которого построена вся работа в nant, после чего передали имя target-а в качестве параметра приложению.

Немного усложним пример. Изменим файл default.build как показано ниже.

<?xml version="1.0"?>
<project name="TestApp">

  <property name="app.author" value="Eugene" />

  <target name="test">
    <echo message="${app.author}" />
  </target>

</project>

После чего опять запустим «nant test». Вывод будет следующим:

test:
     [echo] Eugene
BUILD SUCCEEDED

В *.build файлах могут содержаться как таргеты (targets), так и проперти (property). Как показано в примере для того, чтобы использовать property, необходимо поместить его имя в фигурные скобки после знака $.

Еще усложним пример.

<?xml version="1.0"?>
<project name="TestApp">

  <property name="app.author" value="Eugene" />
  <property name="app.path" value="${directory::get-current-directory()}" />

  <target name="test">
    <echo message="${app.author}" />
    <echo message="${app.path}" />
  </target>

</project>

Вывод будет зависеть от папки, в которой находится файл default.build, но он должен быть приблизительно следующим:

test:
     [echo] Eugene
     [echo] C:\Projects\TestApp
BUILD SUCCEEDED

Как вы видите, существует возможность использования как констант, так и встроенных функций. Весь перечень функций можно найти здесь: nant.sourceforge.net/...help/functions/index.html

Итак, пора бы написать что-нибудь полезное, поэтому закончим с основами, и перейдем непосредственно к автоматизации проекта TestApp. Для тех кто хочет подробно изучить возможности nant настоятельно рекомендую прочитать книгу «Expert. NET Delivery Using NAnt and CruiseControl. NET».

Прежде чем приступать непосредственно к процессу написания скриптов для автоматизации процесса деплоймента, необходимо продумать стратегию (последовательность шагов) сборки.

Предлагаемая стратегия сборки:

  1. Подменить *.config файл в веб приложении
  2. Собрать проект
  3. Запустить тесты
  4. Создать *.bat файл для деплоймента веб приложения в IIS, а также установки сервиса.
  5. Если environment==’QA’, будем архивировать бинарники сервиса и копировать их в папку «C:\build\» (это может показаться бессмысленным, но для примера сойдет)

4-й пункт выглядит странно, но это связано с политикой безопасности в Windows Server 2008 и Windows Vista.

Итак, поехали...

Создаем файл default.build в папке «C: \Projects\TestApp».

Копируем файл C: \Projects\TestApp\WebApp\web.config в C: \Projects\TestApp\WebApp\web.config.standard. В *.standard файле подменяем значение раздела appSettings:

<appSettings>
    <add key="env" value="[[[config.environment]]]"/>
</appSettings>

После чего модифицируем файл default.build как показано ниже (самые нетерпеливые читатели могут сразу перейти в конец раздела, где представлен полный код файла default.build):

<?xml version="1.0"?>
<project name="TestApp">

  <property name="solution.dir" value="${directory::get-current-directory()}" />

  <property name="config.file.path" value="${solution.dir}\WebApp\web.config" />
  <property name="config.environment" value="QA" />


  <target name="build.recreateConfig">
    <echo message="build.recreateConfig starded at: ${datetime::now()}" />
    <copy verbose="true"
          file="${config.file.path}.standard"
          tofile="${config.file.path}"
          overwrite="true">
      <filterchain>
        <replacestring from="[[[config.environment]]]" to="${config.environment}" />
      </filterchain>
    </copy>
    <echo message="build.recreateConfig completed at: ${datetime::now()}" />
  </target>

</project>

В таргете «build.recreateConfig» выводится на экран дата и время запуска таргета, после чего копируется файл «web.config.standard» в файл «web.config» с заменой значения [[[config.environment]]] на значение «QA».

Идем дальше, следующим шагом необходимо создать таргет для сборки проекта. Сделаем это...

Добавляем проперти:

  <property name="<a href="http://solution.file.name" target="_blank">solution.file.name</a>" value="TestApp.sln" />
  <property name="build.framework.dir"
            value="C:\WINDOWS\<a href="http://Microsoft.NET" target="_blank">Microsoft.NET</a>\Framework\v3.5\" />
  <property name="build.configuration" value="Release" />
  <property name="build.platform" value="Any CPU" />
  <property name="build.msbuildTargetList" value="Clean;Rebuild" />
  <property name="build.verbosity" value="quiet" />

Создаем таргет для билда:

  <target name="build">
    <echo message="build starded at: ${datetime::now()}" />
    <exec program="msbuild.exe"
          basedir="${build.framework.dir}"
          workingdir="${solution.dir}"
          resultproperty="build.result" failonerror="true">
      <arg value="${<a href="http://solution.file.name" target="_blank">solution.file.name</a>}" />
      <arg value="/property:Configuration=${build.configuration};Platform=${build.platform}" />
      <arg value="/target:${build.msbuildTargetList}" />
      <arg value="/verbosity:${build.verbosity}" />
    </exec>
    <echo message="build completed at: ${datetime::now()}" />
  </target>

После этого следует сохранить файл default.build, перейти в консоль, и выполнить команду «nant build». Если вы увидите вывод как показано далее, значит все в порядке, проект успешно собрался.

build:
     [echo] build starded at: 06/06/2009 23:27:52
     [exec] Microsoft (R) Build Engine Version 3.5.30729.1
     [exec] [Microsoft .NET Framework, Version 2.0.50727.3053]
     [exec] Copyright (C) Microsoft Corporation 2007. All rights reserved.
     [exec]
     [echo] build completed at: 06/06/2009 23:27:56
BUILD SUCCEEDED

Если в выводе появится «BUILD FAILED» значит нужно вернуться на несколько шагов назад и перепроверить правильность кода.

Следующий пункт — запуск тестов. Создаем папку «C: \Projects\TestApp\Tests\Results» для файла c результатами тестирования.

Добавляем проперти в файлу default.build:

  <property name="tests.dir" value="${solution.dir}\Tests" />
  <property name="tests.results.dir" value="${solution.dir}\Tests\Results" />
  <property name="<a href="http://tests.assembly.name" target="_blank">tests.assembly.name</a>" value="Tests.dll" />

Добавляем таргет для запуска тестов:

  <target name="tests.build">
    <echo message="tests.build starded at: ${datetime::now()}" />
    <nunit2 haltonfailure="false" failonerror="true">
      <formatter type="Xml"
                 usefile="true"
                 extension=".xml"
                 outputdir="${tests.results.dir}" />
      <formatter type="Plain" usefile="false" />
      <test assemblyname="${tests.dir}\bin\${build.configuration}\${<a href="http://tests.assembly.name" target="_blank">tests.assembly.name</a>}" />
    </nunit2>
    <echo message="tests.build completed at: ${datetime::now()}" />
  </target>

Выполняем команду «nant tests.build» в консоли. Вывод должен быть таким, как показано ниже:

tests.build:
     [echo] tests.build starded at: 06/06/2009 23:46:16
   [nunit2] Tests run: 2, Failures: 0, Not run: 0, Time: 0.114 seconds
   [nunit2]
   [nunit2]
   [nunit2]
     [echo] tests.build completed at: 06/06/2009 23:46:18
BUILD SUCCEEDED

Мы закончили первые 3 пункта, переходим к самому интересному — созданию скрипта для развертывания веб сайта и установки сервиса. Чтобы немного усложнить задачу допустим для сайта должно быть настроено несколько биндингов (Bindings):

1. testapp.ua:80

2. admin.testapp.ua:80

Создаем папку «C: \Projects\TestApp\Scripts», в которой создаем файл «deployWebApp.bat.standard». Этот файл будет копироваться в файл «C: \Projects\TestApp\Scripts\deployWebApp.bat», причем будут подменяться некоторые параметры.

Код файла «C: \Projects\TestApp\Scripts\deployWebApp.bat.standard»:

REM create standard app pool for the application
%windir%\system32\inetsrv\AppCmd DELETE APPPOOL /<a href="http://apppool.name" target="_blank">apppool.name</a>:"[[[webapp.appPoolName]]]"
%windir%\system32\inetsrv\AppCmd ADD APPPOOL /<a href="http://apppool.name" target="_blank">apppool.name</a>:"[[[webapp.appPoolName]]]"
REM create web site
%windir%\system32\inetsrv\AppCmd delete site "[[[webapp.siteName]]]"
%windir%\system32\inetsrv\AppCmd add site /<a href="http://site.name" target="_blank">site.name</a>:"[[[webapp.siteName]]]" /bindings:http/*:80:[[[webapp.appPoolName]]]
REM create web app in the root of the web site
%windir%\system32\inetsrv\AppCmd ADD APP /<a href="http://site.name" target="_blank">site.name</a>:"[[[webapp.siteName]]]" /path:/ /applicationPool:[[[webapp.appPoolName]]] /physicalPath:"[[[webapp.physicalPath]]]"
REM add bindings to the site
%windir%\system32\inetsrv\AppCmd set site /<a href="http://site.name" target="_blank">site.name</a>:[[[webapp.siteName]]] /+bindings.[protocol='http',bindingInformation='*:80:admin.[[[webapp.siteName]]]']

В этой статье написано достаточно подробно как в IIS 7 создавать сайты, приложения, пулы и т. д. используя AppCmd, поэтому здесь я не буду тратить место для объяснения что делает каждая команда в этом файле.

Далее добавляем новые проперти в файл default.build:

  <property name="webapp.siteName" value="<a href="http://testapp.ua" target="_blank">testapp.ua</a>" />
  <property name="webapp.appPoolName" value="<a href="http://testapp.ua" target="_blank">testapp.ua</a>" />
  <property name="webapp.physicalPath" value="${solution.dir}\WebApp" />
  <property name="webapp.deploy.file" value="${solution.dir}\Scripts\deployWebApp.bat" />

А также новый таргет для создания *.bat файла:

  <target name="deploy.createIisBat">
    <copy verbose="true"
          file="${webapp.deploy.file}.standard"
          tofile="${webapp.deploy.file}"
          overwrite="true">
      <filterchain>
        <replacestring from="[[[webapp.siteName]]]" to="${webapp.siteName}" />
        <replacestring from="[[[webapp.appPoolName]]]" to="${webapp.appPoolName}" />
        <replacestring from="[[[webapp.physicalPath]]]" to="${webapp.physicalPath}" />
      </filterchain>
    </copy>
  </target>

После чего можно попробовать выполнить команду «nant deploy.createIisBat», и запустить файл «Scripts\deployWebApp.bat». Если у вас выключен UAC, тогда вам необходимо запустить файл от имени администратора (Рис 31).

Запуск *.bat файла

Рис 31. Запуск *.bat файла от имени администратора.

После запуска файла запускаем «inetmgr», и видим приблизительно следующее:

IIS Manager

Рис 32. IIS Manager после запуска скрипта.

На данном этапе необходимо сделать один «кастомный» шаг — внести изменения в файл hosts, для того, чтобы можно было запускать сайт.

Открываем notepad от имени администратора, далее File-> Open-> «C: \Windows\System32\drivers\etc\hosts» и добавляем строку «127.0.0.1 testapp.ua», как показано на рис. 33:

Правка файла hosts

Рис 33. Правка файла hosts.

Пришло время протестировать веб приложение. Открываем браузер и вводим в строку url: «testapp.ua/

Тест веб сайта.

Рис 34. Тест веб сайта.

Как видим, все работает.

Следующим шагом будет написание скрипта для установки Windows Service

Создаем файл «C: \Projects\TestApp\Scripts\deployService.bat.standard» с следующим содержимым:

sc delete [[[<a href="http://service.name" target="_blank">service.name</a>]]]
sc create [[[<a href="http://service.name" target="_blank">service.name</a>]]] binPath= "[[[service.path]]]  -d runservice " start= demand DisplayName= "[[[<a href="http://service.name" target="_blank">service.name</a>]]] Rocks"

Добавляем проперти в файл default.build:

  <property name="service.deploy.file" value="${solution.dir}\Scripts\deployService.bat" />
  <property name="<a href="http://service.name" target="_blank">service.name</a>" value="TestAppService" />
  <property name="service.path" value="${solution.dir}\TestService\bin\${build.configuration}\TestService.exe" />

А также добавляем таргет:

  <target name="deploy.createServiceBat">
    <copy verbose="true"
          file="${service.deploy.file}.standard"
          tofile="${service.deploy.file}"
          overwrite="true">
      <filterchain>
        <replacestring from="[[[<a href="http://service.name" target="_blank">service.name</a>]]]" to="${<a href="http://service.name" target="_blank">service.name</a>}" />
        <replacestring from="[[[service.path]]]" to="${service.path}" />
      </filterchain>
    </copy>
  </target>

Выполняем команду «nant deploy.createServiceBat» (проект должен быть собран, чтобы была возможность проинсталировать TestService.exe).

Запускаем «Scripts\deployService.bat» от имени администратора.

Далее открываем «services.msc» и ищем добавленный сервис:

Установленный сервис

Рис 35. Установленный сервис

Последним шагом является «странное» требование — архивирование бинарников TestService и копирование архива в папку «C: \builds\»

Добавляем проперти в файл default.build:

  <property name="service.zip.folder" value="C:\builds" />

Добавляем новый таргет:

  <target name="deploy.zipService">
    <if test="${config.environment=='QA'}">
      <if test="${not directory::exists(service.zip.folder)}">
        <echo message="${service.zip.folder} doesn't exist, creating the folder..." />
        <mkdir dir="${service.zip.folder}"/>
      </if>
      <zip zipfile="${service.zip.folder}\build_arc.zip">
        <fileset basedir="${solution.dir}\TestService\bin\${build.configuration}">
          <include name="**/*" />
        </fileset>
      </zip>
    </if>
  </target>

Выполняем команду «nant deploy.zipService» и если проперти с именем «config.environment» было установлено в «QA», тогда можно открыть папку «C: \builds» и убедиться, что *.zip файл создается:

озданный *.zip файл

Рис 36. Созданный *.zip файл

Остался последний шаг — создать таргет, который будет собирать в себе все перечисленные ранее. Код таргета представлен ниже:

  <target name="deploy">
    <call target="build.recreateConfig" />
    <call target="build" />
    <call target="tests.build" />
    <call target="deploy.createIisBat" />
    <exec program="${solution.dir}\Scripts\deployWebApp.bat" />
    <call target="deploy.createServiceBat" />
    <exec program="${solution.dir}\Scripts\deployService.bat" />
    <call target="deploy.zipService" />
  </target>

Тут нужно сделать одно замечание: если на машине выключен UAC, тогда проект не будет собираться, необходимо будет закоментировать вызовы «*.bat» файлов, и запускать их вручную. Если UAC выключен — тогда все что нужно сделать это выполнить команду «nant deploy».

Весь код файла default.build представлен ниже:

<?xml version="1.0"?>
<project name="TestApp">

  <property name="solution.dir" value="${directory::get-current-directory()}" />
  <property name="<a href="http://solution.file.name" target="_blank">solution.file.name</a>" value="TestApp.sln" />

  <property name="config.file.path" value="${solution.dir}\WebApp\web.config" />
  <property name="config.environment" value="QA" />

  <property name="build.framework.dir"
            value="C:\WINDOWS\<a href="http://Microsoft.NET" target="_blank">Microsoft.NET</a>\Framework\v3.5\" />
  <property name="build.configuration" value="Release" />
  <property name="build.platform" value="Any CPU" />
  <property name="build.msbuildTargetList" value="Clean;Rebuild" />
  <property name="build.verbosity" value="quiet" />

  <property name="tests.dir" value="${solution.dir}\Tests" />
  <property name="tests.results.dir" value="${solution.dir}\Tests\Results" />
  <property name="<a href="http://tests.assembly.name" target="_blank">tests.assembly.name</a>" value="Tests.dll" />

  <property name="webapp.siteName" value="<a href="http://testapp.ua" target="_blank">testapp.ua</a>" />
  <property name="webapp.appPoolName" value="<a href="http://testapp.ua" target="_blank">testapp.ua</a>" />
  <property name="webapp.physicalPath" value="${solution.dir}\WebApp" />
  <property name="webapp.deploy.file" value="${solution.dir}\Scripts\deployWebApp.bat" />

  <property name="service.deploy.file" value="${solution.dir}\Scripts\deployService.bat" />
  <property name="<a href="http://service.name" target="_blank">service.name</a>" value="TestAppService" />
  <property name="service.path" value="${solution.dir}\TestService\bin\${build.configuration}\TestService.exe" />
  <property name="service.zip.folder" value="C:\builds" />

  <target name="build.recreateConfig">
    <echo message="build.recreateConfig starded at: ${datetime::now()}" />
    <copy verbose="true"
          file="${config.file.path}.standard"
          tofile="${config.file.path}"
          overwrite="true">
      <filterchain>
        <replacestring from="[[[config.environment]]]" to="${config.environment}" />
      </filterchain>
    </copy>
    <echo message="build.recreateConfig completed at: ${datetime::now()}" />
  </target>

  <target name="build">
    <echo message="build starded at: ${datetime::now()}" />
    <exec program="msbuild.exe"
          basedir="${build.framework.dir}"
          workingdir="${solution.dir}"
          resultproperty="build.result" failonerror="true">
      <arg value="${<a href="http://solution.file.name" target="_blank">solution.file.name</a>}" />
      <arg value="/property:Configuration=${build.configuration};Platform=${build.platform}" />
      <arg value="/target:${build.msbuildTargetList}" />
      <arg value="/verbosity:${build.verbosity}" />
    </exec>
    <echo message="build completed at: ${datetime::now()}" />
  </target>

  <target name="tests.build">
    <echo message="tests.build starded at: ${datetime::now()}" />
    <nunit2 haltonfailure="false" failonerror="true">
      <formatter type="Xml"
                 usefile="true"
                 extension=".xml"
                 outputdir="${tests.results.dir}" />
      <formatter type="Plain" usefile="false" />
      <test assemblyname="${tests.dir}\bin\${build.configuration}\${<a href="http://tests.assembly.name" target="_blank">tests.assembly.name</a>}" />
    </nunit2>
    <echo message="tests.build completed at: ${datetime::now()}" />
  </target>

  <target name="deploy.createIisBat">
    <copy verbose="true"
          file="${webapp.deploy.file}.standard"
          tofile="${webapp.deploy.file}"
          overwrite="true">
      <filterchain>
        <replacestring from="[[[webapp.siteName]]]" to="${webapp.siteName}" />
        <replacestring from="[[[webapp.appPoolName]]]" to="${webapp.appPoolName}" />
        <replacestring from="[[[webapp.physicalPath]]]" to="${webapp.physicalPath}" />
      </filterchain>
    </copy>
  </target>

  <target name="deploy.createServiceBat">
    <copy verbose="true"
          file="${service.deploy.file}.standard"
          tofile="${service.deploy.file}"
          overwrite="true">
      <filterchain>
        <replacestring from="[[[<a href="http://service.name" target="_blank">service.name</a>]]]" to="${<a href="http://service.name" target="_blank">service.name</a>}" />
        <replacestring from="[[[service.path]]]" to="${service.path}" />
      </filterchain>
    </copy>
  </target>

  <target name="deploy.zipService">
    <if test="${config.environment=='QA'}">
      <if test="${not directory::exists(service.zip.folder)}">
        <echo message="${service.zip.folder} doesn't exist, creating the folder..." />
        <mkdir dir="${service.zip.folder}"/>
      </if>
      <zip zipfile="${service.zip.folder}\build_arc.zip">
        <fileset basedir="${solution.dir}\TestService\bin\${build.configuration}">
          <include name="**/*" />
        </fileset>
      </zip>
    </if>
  </target>

  <target name="deploy">
    <call target="build.recreateConfig" />
    <call target="build" />
    <call target="tests.build" />
    <call target="deploy.createIisBat" />
    <exec program="${solution.dir}\Scripts\deployWebApp.bat" />
    <call target="deploy.createServiceBat" />
    <exec program="${solution.dir}\Scripts\deployService.bat" />
    <call target="deploy.zipService" />
  </target>
</project>

Cruise Control.NET

Заключительным шагом будет установка и настройка «Continuous Integration» сервера для автоматического запуска сборки проекта и запуска тестов.

Идем на сайт ccnet.thoughtworks.com и качаем последнюю версию Cruise Control. NET (на момент написания статьи актуальной была версия 1.4.4).

Запускаем установщик...

37_ccnet_install_1

Рис 37. Установка CruiseControl.NET, шаг 1

38_ccnet_install_2

Рис 38. Установка CruiseControl.NET, шаг 2

39_ccnet_install_3

Рис 39. Установка CruiseControl.NET, шаг 3

40_ccnet_install_4

Рис 40. Установка CruiseControl.NET, шаг 4

41_ccnet_install_5

Рис 41. Установка CruiseControl.NET, шаг 5

42_ccnet_install_6

Рис 42. Установка CruiseControl.NET, шаг 6

Необходимо сделать одно замечание. На шаге 4, была отмечена опция «Create virtual directory in IIS for Web Dashboard», но, несмотря на это, виртуальная папка в IIS 7 не будет создана (можете называть это багом). Будем надеяться, что в следующих версиях такой проблемы не будет.

Необходимо сделать несколько дополнительных шагов для того, чтобы можно было запускать «Dashboard» в браузере, если этого не нужно (например, если вы будете использовать CCTray), можете смело пропустить эту часть раздела.

Открываем «inetmgr» и добавляем приложение к «Default web site»

Web Dashboard

Рис 43. Добавление приложения для «Web Dashboard», шаг 1

Далее заполняем форму как показано ниже. Очень важно поменять значение «Application pool», т. к. по умолчанию оно устанавливается в «DefaultAppPool», если не поменять этот параметр на «Classic», приложение запускаться не будет.

шаг 2

Рис 44. Добавление приложения для «Web Dashboard», шаг 2

Web Dashboard установлен, теперь нужно проверить это. Открываем браузер, в строке url пишем «http://localhost/ccnet/». Если вы видите такую же картинку как на рис. 45, значит все установлено правильно, если нет — вернитесь на пару шагов назад и проверте правильность своих действий.

Web Dashboard

Рис 45. CruiseControl.NET Web Dashboard

Пришло время приступить к настройке ccnet на работу с тестовым приложением TestApp.

Модифицируем файл «C: \Program Files\CruiseControl. NET\server\ccnet.config» как показано ниже:

<!DOCTYPE cruisecontrol [
  <!ENTITY PROJECT_NAME "TestApp_v1">
  <!ENTITY ROOT_FOLDER "C:\Projects\TestApp">
  <!ENTITY ARTIFACTS_FOLDER "C:\Projects\TestApp\Tests\Results">
  <!ENTITY REPOSITORY_FOLDER "https://JonyW2008:8443/svn/TestApp">
  <!ENTITY TEST_USERNAME "Eugene">
  <!ENTITY TEST_PASSWORD "1">
  <!ENTITY NANT_EXECUTABLE "nant.exe">
]>
<cruisecontrol>
  <project>
    <name>&PROJECT_NAME;</name>

    <sourcecontrol type="svn">
      <trunkUrl>&REPOSITORY_FOLDER;</trunkUrl>
      <workingDirectory>&ROOT_FOLDER;</workingDirectory>
      <username>&TEST_USERNAME;</username>
      <password>&TEST_PASSWORD;</password>
      <autoGetSource>true</autoGetSource>
    </sourcecontrol>

    <tasks>
      <nant>
        <executable>&NANT_EXECUTABLE;</executable>
        <baseDirectory>&ROOT_FOLDER;</baseDirectory>
        <buildFile>default.build</buildFile>
        <targetList>
          <target>build</target>
          <target>tests.build</target>
        </targetList>
        <buildTimeoutSeconds>600</buildTimeoutSeconds>
      </nant>
    </tasks>

    <publishers>
      <merge>
        <files>
          <file>&ARTIFACTS_FOLDER;\*-results.xml</file>
        </files>
      </merge>
      <xmllogger />
    </publishers>

  </project>
</cruisecontrol>

Несколько важных замечаний:

  • Элемент «PROJECT_NAME» не должен содержать пробелов, иначе вы будете получать exception в IIS, когда попытаетесь посмотреть результаты билда
  • не забудьте подменить значение елемента «REPOSITORY_FOLDER» — самый простой способ сделать это — открыть VisualSVN Server Manager и скопировать url к вашему репозиторию

Далее открываем «services.msc», находим сервис с именем «CruiseControl. NET Server», заходим в настройки сервиса.

шаг 1

Рис 46. Настройка сервиса CruiseControl.NET Server, шаг 1

По умолчанию сервис будет запускаться как «Local System account», необходимо поменять это поведение, сервис должен запускаться с правами пользователя, у которого есть сертификат для работы с SVN. Грубо говоря, необходимо, чтобы сервис логинился как юзер, который выполнял команду «svn co», т. к. именно после этой команды пользователь должен принять или отклонить сертификат, как говорилось в разделе посвященном SVN.

Меняем настройки аутентификации сервиса:

47_ccnet_service_properties_2

Рис 47. Настройка сервиса CruiseControl.NET Server, шаг 2

Сохраняем изменения и запускаем сервис.

Открываем браузер, переходим по следующей ссылке «http://localhost/ccnet/».

Cruise Control.NET Dashboard

Рис 48. Cruise Control.NET Dashboard

С этого момента проект можно собирать нажатием 1-й кнопки — «Force», при этом будет взята последняя версия проекта из репозитория, запущен билд и тесты.

На самом деле, еще много чего можно сделать, например можно настроить автоматический билд после каждого коммита, вместо билда по нажатию кнопки. Можно настроить рассылку писем всем разработчикам с результатами билда (достаточно юзабильная вещь), в письме можно будет увидеть последние изменения и определить кто завалил билд.

Скачать исходный код примера.

Заключение

Существует методика, которая называется «12 шагов Джоэла Спольски». Это набор из 12-ти субъективных методов, которые могут привести к успеху в разработке проекта. В данной статье было рассмотрено 3 шага из 12-ти, а это уже что-то (существуют фирмы, в которых не наберется даже 1-го балла).

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

Теперь давайте ответим на вопросы, которые были заданы в начале статьи:

— сколько действий вам нужно сделать, чтобы собрать проект, включая установку Windows Services, дейплоймент веб сайта, запуск тестов (вы же пишете тесты?)?

Если выключен UAC, тогда 1 — запустить правильный таргет

— что вам нужно сделать, чтобы настроить всю инфраструктуру на машине нового члена команды?

Сделать checkout из репозитория, запустить таргет, модифицировать файл hosts

— как часто у вас собирается проект на Тест-сервере?

Несколько раз в день (возможно настроить на сборку после каждого коммита)

— насколько отличается процесс деплоймента на машине девелопера, qa сервере и production сервере?

Абсолютно идентичен, все сводится к запуску правильного таргета

— сколько человек в команде сумеют развернуть проект на production сервере?

Кто угодно сумеет это сделать, т. к. для этого нужно всего лишь запустить таргет для деплоймента

  • Популярное

60 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.
"

данная структура подразумевает разработу с версионированием, т. е. вся разработка ведется из папки trunk, когда выходит новая версия продукта, она перекладывается в папку branches, а в папке trunk продолжается разработка над следующей версией продукта. Таким образом папка trunk будет содержать актуальную и еще не выпущенную версию продукта, а папка branches будет содержать папки v1, v1.1, v2, и т. е. версии, которые уже в релизе.

"

Ошибка же. По идее trunk содержит рабочую версию доступную для всех разработчиков. Если хотим дорабатывать новую фичу, то создаем папку в branches и там работает, потом сливаем в trunk, разрешаем конфликты, и удаляем свой branch. Если хотим сделать снимок trunk, то для этого есть папка tags в которой и хранятся версии продукта. Разве нет?

большое спасибо за статью. единственно замечание — тут в коментах предлагались варианты с teamcity, как лучшей альтернативы cruisecontrol. такие коментарии сбивают столку, т.к. нет информации о лицензиях. teamcity конечно лучше, но он и денег стоит не мало, а cruisecontrol бесплатен.

TeamCity бесплатен в версии Professional. Это 20 билд конфигураций и 20 пользователей: www.jetbrains.com/...mcity/download fromIndex

Фирма ProspX, г.Симферополь. Считаю, что мне очень повезло, что я работал в этой компании — отличные специалисты и суперовый менеджмент. Пользуясь случаем, передаю еще раз привет из Киева всем ребятам из ProspX.

Молодец Жека. Жаль не указано — какой компании популяризован процесс был.

Необходимо сделать одно замечание. На шаге 4, была отмечена опция «Create virtual directory in IIS for Web Dashboard», но, несмотря на это, виртуальная папка в IIS 7 не будет создана (можете называть это багом). Будем надеяться, что в следующих версиях такой проблемы не будет.

Скрипт создания виртуальной папки заточен под IIS 6.

Еще такой вопрос: если в приведенном примере добавить проект с WixSetup, что надо править в конфиг. файлах NAnt, CC.Net?

Ждем с нетерпением:) Хотелось бы еще увидеть применение NCover, FXCop.А у вас только на этом сайте статьи? (2 шт. вижу)

Сейчас используем SVN + TeamCity + NUnit, нет никаких проблем. Переход на VS2010 ничего не поменяет — утилиты для разработки останутся те же, т.к. они уже настроены и процесс разработки уже отлажен, а переход на использование TFS качественно ничего не поменяет.PS: только сейчас понял, что в статье нет информации о разработке базы данных в комманде... так что ждите вторую часть статьи.

Да нет, TFS дорого. Я немного другое хотел узнать.Имелось ввиду, если бы вы сейчас писали статью используя, например, не Visual Studio 2008 Team System, а Visual Studio 2010 Ultimate — что бы вы изменили?

Какую модель использовать — зависит только от вас. В VS2010 можно запускать сборки с тестированием после каждого коммита, причем все это можно настроить НЕ ИСПОЛЬЗУЯ сторонних приложений — все решаемо на стороне TFS (team foundation server). Основная проблема — цена вопроса. TFS достаточно дорогостоящий продукт (около $2500), поэтому решать вам.

Спасибо, теперь понятнее стало.Хотелось бы узнать, как может изменится данная модель разработки, если Visual Studio будет 2010 версии? Другими словами, есть ли что-нибудь новое в 2010 студии, что может заменить некоторые звенья в описанной вами модели?

Пример надуманый, но все же кое-какая логика тут присутствует. Например, зачем вам создавать архивы билдов на локальной машине? Поэтому, например, если environment==QA или BuildServer или что-нибудь там еще — мы создаем архивы, чтобы можно было потом их достать. А если envorinment==Dev, тогда не создаем.Но как многие тут уже советовали — можете использовать TeamCity, если у вас маленький проект или есть лишние 2т$ (цены на TeamCity здесь: http://www.jetbrains.com/teamc...). Используя TeamCity отпадает необходимость создавать архивы в nant скрипте, т.к. там можно настроить сохранение билда в репозитории TeamCity.

С этим уже разобрался. Можно изменить логику второго теста на что-то подобное:

Assert.Throws<ArgumentNullException>( () => entity.GetHello(null));

Вопрос такой: когда выполняется архивирование билда, зачем делать такую проверку??? Для чего вобще нужны манипуляции с config.environment?

alga, а как заставить nant использовать nunit 2.5?

novice__ 7.10.2009 в 17: 40 У меня при выполнении nant tests.build не проходит второй тест (с эксепшеном). Если выполнять тест вручную, через nunit — оба теста проходят на ура.

nant локально использет nunit 2.2. А в проекте добавлена ссылка на 2.5. Поэтому через nunit.exe все работает, так как используется новая версия.

Может быть такие утилиты и есть, но я о них ничего не слышал... Как вариант можно создать свой таргет и написать C# код который будет реализовывать эту логику:

<target name="build.updateBuildVersion" description="Stamp the version info onto assemblyinfo.cs"> <script language="C#"> <code><![CDATA[ public static void ScriptMain(Project project) { StreamReader reader = new StreamReader(project.Properties["build.assembly.assemblyInfo.path"]); string contents = reader.ReadToEnd(); reader.Close(); MatchCollection m = Regex.Matches(contents, @"Version(.)]"); int majorVersion = 0; int minorVersion = 0; int buildVersion = 0; int revisionVersion = 0; foreach (Match match in m) { string assemblyString = match.ToString(); Match assemblyVersions = Regex.Match(assemblyString, @""".*"""); string assemblyVersionsString = assemblyVersions.ToString(); string[] versions = assemblyVersionsString.Substring(1, assemblyVersionsString.Length - 2).Split('.'); if (!assemblyString.Contains("*")) { majorVersion = versions.Length >= 1 && versions[0] != "*" ? int.Parse(versions[0]) : 0; minorVersion = versions.Length >= 2 && versions[1] != "*" ? int.Parse(versions[1]) : 0; buildVersion = versions.Length >= 3 && versions[2] != "*" ? int.Parse(versions[2]) : 0; revisionVersion = versions.Length >= 4 && versions[3] != "*" ? int.Parse(versions[3]) : 0; break; } } buildVersion++; string replacement = string.Format("Version("{0}.{1}.{2}.{3}")]", majorVersion, minorVersion, buildVersion, revisionVersion); string newText = Regex.Replace(contents, @"Version(.)]", replacement); StreamWriter writer = new StreamWriter(project.Properties["build.assembly.assemblyInfo.path"], false); writer.Write(newText); writer.Close(); } ]]> </code> </script> </target>

Как с помощью всего этого добра организовать автоматическое инкрементирование билда при сборке и введение ревизии с репозитория в свойствах сборки? Проект состоит с одной сборки.В голову приходит написание какой-то утилиты что будет изменять в файле AssemblyInfo.cs атрибуты: [assembly: AssemblyVersion ( «1.0.0.0» )] [assembly: AssemblyFileVersion ( «1.0.0.0» )] Может есть уже готовые решения проблемы?

  1. Дело в том, что force лишь запускает нужный таргет (один или несколько), т.к. в конфиге ccnet.config указано, что нужно запускать «build» и «tests.build» — эти таргеты не публикуют сайт, если это необходимо — нужно дописать вызов еще одного таргета в ccnet.config.2. <! DOCTYPE cruisecontrol [...] > это лишь способ хранить все в одном месте — чтобы не просматривать весь конфиг. Поэтому в этот раздел нужно дописать нужные проперти и все. Должно получиться что-то вроде этого: <! DOCTYPE cruisecontrol [<! ENTITY PROJECT1_NAME «TestApp_v1» > <! ENTITY PROJECT2_NAME «TestApp_v2» > ] > < cruisecontrol> < project> < name>& PROJECT1_NAME;</name> ... </project> < project> < name>& PROJECT2_NAME;</name> ... </project> </cruisecontrol> 3. Для того, чтобы добавить email notification, необходимо добавить следущее описание в нод publishers: < email from= «cruisecontrol@yourdomain.com» mailhost=" [host ip] " includeDetails= «TRUE» > < users> < user name= «eugene» group= «developers» address= «eugene@eugene.eugene» /> </users> < groups> < group name= «developers» notification= «always» /> </groups> </email> Откравку писем можно настроить например с gmail — пример найти очень просто т.е. их достаточно много.

Большое спасибо за развернутый ответ! Вот что получилось: 1. Поменл 80 порт в «deployWebApp.bat.standard» на необходимый.Когда я выполняю nant.deploy — сайт публикуется на нужный мне порт.Когда я выполняю FORCE через вебинтерфейс cc.net — сайт не публикуется (предварительно я его удалил из IIS).2. Про нод понятно.А как быть вот с этим: <! DOCTYPE cruisecontrol [ ...По поводу ошибки: исходный код скачал — такой же результат: (Поэтому второй тест пришлось закомментировать.Если вас не затруднит, не могли бы осветить такие вопросы: сборка проекта при каждом коммите, night-build, оповещение по почте о сборке.Или хотя бы один из этих вопросов.Еще раз выражаю благодарность за такой четкий мануал!!!

  1. В файле «deployWebApp.bat.standard» необходимо подменить строку: %windir%system32inetsrvAppCmd add site /site.name: " [[[webapp.siteName]]] " /bindings: http/*: 8080: [[[webapp.appPoolName]]] Соответственно, если необходимо, чтобы и биндинги работали через другой порт — необходимо изменить их и в других местах в файле2. В ccnet.config может быть перечислено несколько проектов, все что нужно сделать — добавить новый нод project для другого проекта, например: < project> < name>...</name> ...</project> < project> < name>...</name> ...</project> По поводу ошибки — скачайте исходный код примера, думаю сразу увидите свою ошибку.

Вот текст ошибки: [nunit2] Failures: [nunit2] 1) Tests.TestClass.GetHelloExceptionTest: System.ArgumentNullException: Value cannot be null. [nunit2] at BusinessObjects.Entity.GetHello (String name) in c: ProjectsTestAppBusinessObjectsEntity.cs: line 11 [nunit2] at Tests.TestClass.GetHelloExceptionTest () in c: ProjectsTestAppTestsTestClass.cs: line 22Вопрос: 1) Как сделать чтобы сайт разворачивался не на 80 порт, а на другой? 2) Если несколько проектов — то как это оформить в ccnet.config?

Напишите текст сообщения, и посмотрим, что может быть причиной

Здравствуйте! У меня при выполнении nant tests.build не проходит второй тест (с эксепшеном).Если выполнять тест вручную, через nunit — оба теста проходят на ура.В чем может быть моя ошибка, не подскажите? P.S. Большое спасибо за статью!

Это достаточно распространенная проблема. У меня например на Windows Server 2008 то же самое. Скачал ночной билд — все заработало. Не думаю, что это проблема т.к. она решается достаточно просто, а от багов, как известно, не застрахован никто:)

В версии NAnt 0.86beta1 наткнулся на баг который решается установкой ночного билда (в котором возможны новые баги:))

> прежде всего это не Windows Vista очень веский аргумент... > С этого момента проект можно собирать нажатием 1-й кнопки — «Force», Ну уже можно бы и CCTRAY тогда поставить, чтоб одну кнопочку пыцать и на сайт не ходить...

фаирфокс как-то не к месту в этой подборке скриншотов. хотя в нем ведь тоже можно тестить силверлайт.

*.pdb файлы добавлять в репозиторий совсем не нужно

Я некоторые проекты оставил на CC.Net, он какой-то более гибкий и легковесный чем TeamCity. И я бы рекомендовал CC.Net людям, которые хотят понимать процесс сборки.MSBuild вполне заменяет Nant, но это ещё не причина на него переходить. А вот если речь идёт о работе с IIS, то очень даже стоит по причине того, что там есть MSbuildExtensionPack: В своё время мне пришлось написать трёхєтажній VBScript из-за того, для развёртки веб-приложений без MSBuildExtensionPack...

Отличная статья. Как раз то, что и нужно новичкам для более полного понимания процесса разработки.

2 NiFiGaNeLamer: Не согласен с несколькими замечаниями:

2. Плохая практика использовать ExpectedException. Не ясно в каком из методов происходит его возникновение. Если ексепшн будет вылетать в другом месте, тест все равно будет проходить. Assert.Throws (() => entity.Hello ());

Плохой практикой считается использование [ExpectedException (typeof (Exception))], т.к. тут будут попадать все эксепшены, однако хорошей практикой считается создание (или использование) специализированных эксепшенов. Также сам тест в идеале должен проверять одну часть логики — по-сути один метод, поэтому не вижу проблем.

3. Осмысленные имена тестов?

Согласен, но можно и отойти от этой практики, если использовать аттрибут [Description ( «...» )]

4. Раз юзаешь решарпер, то упомянул бы как он помогает при TDD разработке. Автоматическая генерация классов, методов, сигнатур и т.п.

Опять таки согласен, но в этом случае статья получилась бы намного больше (а букав там и так не мало)

6. TDD — говоришь? Red-Green-Refactor. А почему пропустил Red? Еще одна плохая практика.

Невнимательно читаете:)...

Пришло время проверить, правильно ли проходят тесты (хотя это нужно было делать немного раньше — до добавления логики к методу GetHello, и только после этого имплементировать логику в методе, но об этом я предлагаю почитать в литературе посвященной TDD и DDD)

7 — Каждый выбарает то, что ему удобно. Согласен с тем, что настройка cc.net задача весьма не тривиальная, но это нужно сделать всего 1 раз — потом можно пользовать и радоваться жизнью. Если есть желание, можете написать похожую статью с использованием TeamCity+MsBuild или описать процесс разработки используя TFS, с радостью ознакомлюсь:)

Мм-да, пару замечаний ИМХО: 1. NUnit должен быть скопирован локально, например в папку Tools, в корне солюшена и использующие его проекты, должны ссылаться на эту копию. Зачем? Затем, что возможные конфликты версий с установленными на машине версиями фреймворка.2. Плохая практика использовать ExpectedException. Не ясно в каком из методов происходит его возникновение. Если ексепшн будет вылетать в другом месте, тест все равно будет проходить. Assert.Throws< argumennullexception> (() => entity.Hello ()); 3. Осмысленные имена тестов? 4. Раз юзаешь решарпер, то упомянул бы как он помогает при TDD разработке. Автоматическая генерация классов, методов, сигнатур и т.п.5. TestDriven.NET быстрее гоняет тесты и меньше глючит чем встроенный в решарпер тест-раннер, имхо. Тот иногда забывает перебилдить проект. Дальше, в меню Tools -> Options есть страница свойств TestDriven.NET. Там можно настроить какие категории тестов исключать при локальном запуске тестов. Этого нет в решарперовском раннере. А фича очень удобная. Я маркирую все «долгоиграющие» тесты как Integration и они запускаются только на CI-сервере. Это значительно экономит время. Плюс в TestDriven.NET больше вариантов для настройки keyboard shortcuts.Alt+R — Run testShift+Alt+R — Rerun the last test (вообще полезная вещь) Alt+D — Debug testShift+Alt+D — Debug the last testНастраивается стандартно, через Tools-> Options-> Environment-> Keyboard".A от NUnit.Gui — никакого толку. Тормозит процесс. Намного удобнее гонять тесты прямо из студии, никуда не переключаясь.6. TDD — говоришь? Red-Green-Refactor. А почему пропустил Red? Еще одна плохая практика.7. Ох и нагородил огород с Continious Integration. Ты так всех распугаешь только:)... Все намного проще, чище и быстрее можно сделать при помощи других доступных инструментов. СС.NET и NAnt — это прошлый век. TeamCity и MSBuild — лучшая альтернатива. А в MSbuildExtensionPack уже есть готовые таски для разворачивания IIS сайтов — только параметры прописать. Там же есть таски для разворачивания БД, настройки MSMQ, установки (старт/стоп:) сервисов и еще черт знает сколько всего. Также есть MSBuildCommunityTasks — валом всего готового. А еще MSBuild’у можно файл солюшена скормить и он все сбилдит, а Nant’у? А вообще — молодец. За попытку приобщения народа к прекрасному:)...</argumennullexception>

Вместо CruiseControl попробуйте Hudson .Существует много плугинов, среди которых есть и для Redmine.

To Жека1. дэшбоард показывает текущую задачу, но если задача состоит из Нант скрипта — фиг ты увидишь какая часть этого скрипта выполняется в данный момент.2. стоит максимальное время, 2 часа, а толку-то? срабатывает далеко не всегда. попробуй прибить по таймауту фтп-задачу...3. логи к нанту будут сформированы только когда процесс закончится. я же говорю, что мне нужно знать, что в настоящий момент нант делает, и узнать это невозможно., а вот что написано в логах к нанту когда вывалился по таймауту: Log does not contain any Xml output from NAnt.Please make sure that NAnt is executed using the XmlLogger (use the argument: -logger: NAnt.Core.XmlLogger). сиди, гадай, что же черт возьми произошло? где логи-то, а? ведь ошибка — это штатная ситуация в такого рода приложениях.4. ну настроен, каждую ночь запускается. приходишь с утра — видишь что висит и запускать надо заново.5. дерьмо не нужно ни за платно, ни за бесплатно.

Могу еще посоветовать DBGhost для автоматизации работы с бд. Правда, он совсем не бесплатный. Я лично использую вместо nant утилиту rake (идет вместе с ruby)

2 TheRealAnonymous: Это намного лучше чем процесс деплоймента, который занимает 2 недели, т.к. никто не помнит (не знает) что, куда и как нужно устанавливать.

xmlвські скріпти це жорстоко, співчуваю

2 Вадим: 1. Билд сервер может быть настроен где угодно, на абсолютно любой машине и не обязательно это делать рядом с репозиторием svn. В идеале для каждого стэйджа должен быть свой билд сервер. Другими словами у вас должно быть 3 машины — svn repository server, dev server, production server. Но как я уже написал это в идеале — все тоже можно сделать в пределах одной машины. В примере рассматривалась именно одна машина для простоты.2. Как написано в статье — все зависит от стратегии деплоймента. На мой взгляд на dev сервере лучше начинать с чистого листа, на production естественно пересоздавать сайты, базы не получится (да и сервисы останавливать тоже проблематично будет), поэтому вы правильно написали — нужно името как минимум 2 деплоймент скрипта — 1-н для чистой установки, 2-й для накатывания изменений.3. От ошибок никто не застрахован:)

Хороший обзор для начала у вас вышел, опишите в деталях каждый из процессов и получится хороший цикл статей о средствах разработки

Спасибо очень интересная и полезная статья.Я только начинаю присматриваться к.NET в профессиональном плане. Но вот интересно было бы почитать про ещё один пункт: инсталяторы, bat это конеш круто. Но в студии например видел тип проекта инсталятор, там вроде есть что-то про IIS, пока не было времени подробнее попробовать, ещё знаю что многие.net разработчики любят wix.

Посмею сделать несколько замечаний по статье.1) Как я понял, процесс билда происходит на том же сервере, на котором надо деплойнуть код. Другими словами, для dev, beta и prod серверов вам надо отдельно на каждом сервере настраивать среду — ставить svn, nant, ccnet. Это просто, если сервер один-два, и все они имеют одинаковую програмную среду. Но что делать, если серверов десятки, и они отличаются программно (стоят разные ОС). К примеру на Windows 2000 нельзя установить.NET framework 3.5, а значит там нет MSBuild который понимает проекты VS 2008, что в свою очередь означает что вы не можете развернуть билд систему на Windows 2000. Вы вполне можете пользоваться VS 2008 и писать проекты которые будут использовать.NET 2.0 Runtime, но билдить вам их прийдется на отдельном выделенном билд сервере.2) Я не совсем согласен, что вы при каждом билде делаетет редеплоймент. Я не вижу смысла удалять и создавать сайты, сервисы при каждом билде. Ведь на эти операции требуется значительное (в масштабах общего времени исполнения билд скрипта) время. Мне кажется правильнее иметь отдельные деплой скрипт, который выполняется единожды при первичном деплое системы на сервер, либо при изменении деплой конфигурации, и отдельный билд скрипт, который предусматривает выполнения на «развернутом» сервере.3) Допустим что вы все таки делаете деплои при каждом билде. Но я как минимум нашел одну ошибку в вашей системе — первый раз сервис будет развернут успешно, и вы его сможете запустить (кстати вручную?), но в дальнейшем вы не сможете заново запустить билд, у вас выскочит ошибка что нельзя перезаписать.exe файл сервиса, потому как сервис в данный момент работает, а кода остановки сервиса я у вас не увидел.

посмотрите на TeamCity, он лучше имхо

  1. На Dashboard у cc.net видно какой именно таск выполняется — будь то svn update или таргет nant.2. Чтобы небыло проблем с зацикливанием можно ставить максимальное время которое занимает билд — после этого cc.net будет автоматически рубить процесс сборки.3. Логи к нанту можно посмотреть в nant-output в том же Dashboard.4. Если процесс сборки занимает много времени — скажем, 30 минут и более, можно настроить сборку не на новую версию в репозитории, а по таймеру — например 3 раза в день.5. Пользоваться Cruise Control никто не заставляет — это не спасет, если в команде одни обезьяны, но этот тул очень удобен и он бесплатный (сравните с TFS)

дерьмо это СС.НЕТ... расскажите мне, как узнать в СС где в настоящий момент находится НАнт скрипт, что он вычисляет?, а никак... и сиди, гадай, то ли у тебя НУнит где-то зациклился, то ли еще в чем проблема... ну прибил ты в процессах НАнт (да-да, потому что СС.НЕТ хоть и имеет кнопочку Аборт, только нифига не умеет прибивать), и что, думаешь остались где-то логи? хрен там, чтобы лог получился, нужно чтобы задача СС.НЕТ закончилась... и что делаешь? правильно, запускаешь НАнт ручками, из консоли, там хоть логи видны... Как можно создавать тулзу, у которой такие вещи — стандартная ситуация, и без логов??? ну ладно, есть у меня сиквел-база, апдейт которой занимает минут 20, есть приложение, которое компилится минут 20, сделал я коммит, все начинает строится, имеем 40 минут пока построится. вопрос -, а почему нельзя запустить две задачи параллельно, чтобы я потратил не 40 минут, а 20? задачи-то независимые, на разных компах выполняются... и НУниты тоже потом сидеть ждать, хотя все это должно параллельно вычислятьсяблин, лучше уж скрипт самому писать, чем этим СС.НЕТ пользоваться, да и НАнт туда же, все с таким трудом делается, через такие альпы, на скрипте куда проще.

ох это визуально программирование. Бегите от него, БЕГИТЕ.

схожа кофігурація, за виключенням cc.net у нас teamcity.Взагалі то так. На МС технологіях покищо забагато кліків. Чекаємо недочекаємось тулзи на кшталт Maven.

Что касается Subversion, репозиторий ставиться за две команды: 1-debian) sudo apt-get install subversion1-bsd) su root -c "make -C /usr/ports/devel/subversion install clean"2) svnadmin create $REPOSITORY_PATHПод Window$ вместо первой, вы будете ублажать инсталлер стимуляцией кнопки Next, а вторая команда та же для всех ОС.

мда, все передрано с джавы, Nunit, Nant, Resharper, Cruise Control.NETв оригинале ето Junit, Ant, IDEA, Cruise Control

2 Жека: Я так и сделал, просто для автора заметка, думал опечатка. За статью спасибо, очень полезная оказалась.

Мамма миа. Это такое количество мышевозения — для выполнения элементарных вещей??? Хвала аллаху, что я далек от Виндовс разработки.

Список всех приложений, с которыми работает cc.net можно найти здесь

еще вопрос: круиз интегрируется с VSS и TFS?

2 usix: возможно юзать и TFS, но как сказал комрад Александр Маненко — дешевле выходит, т.к. все кроме Resharper-а бесплатно.2 Igor: 1 — насколько я знаю таких тулзовин нет, но можно погуглить2 — можно создать таргет в нанте для «svn up», но прелесть cruise control в том, что он автоматически проверяет наличие новых коммитов + запускает билд + выводит результаты + отсылает результаты всем людям, перечисленным в email list. Все это сделать используя только нант было бы сложно.

хорошая статья1вопрос — есть какие-нить удабные тулзы для созданияредактирования Nant файлов (build) или только в текстовом редакторе? 2 вопрос — cruisecontrol -можно заменить Nant? для скачивания послед версии с SVN и запуска всех тестов можно написать еще один Nant build. Напонятно преимущесвто круиза.

Автор, гуд. Отлично все собрал в одном месте. Полностью согласен с SVN+RESHARPER+NUNIT+NANT+CRUISE CONTROL, тоже юзаем эту комбинацию.PS: Неплохой старт для зеленых менеджеров, которые знали, что все это должно быть, а вот как собрать все в кучу — нет.

Спасибо, интересно было познакомиться с некоторыми продуктами.

Мб потому что дешевле?:)

Вопрос холиварный, но все же почему SVN+RESHARPER+NUNIT+NANT+CRUISE CONTROL, а не TFS + MSBUILD?

Подписаться на комментарии