Непрерывная интеграция.
- modified:
- reading: 7 minutes
Так получилось, что на текущей работе мне пришлось настраивать CCNet build сервер для создания среды непрерывной интеграции. Что это такое и зачем нужно можно почерпнуть из многих источников, из которых можно выделить статью Мартина Фаулера [1], а так же книгу Непрерывная интеграция. Улучшение качества программного обеспечения и снижение риска [2], о которой Мартин Фаулер так же хорошо отзывался. Про эту книгу я могу сказать одно, она мне попалась в переведенном варианте пару лет назад (или даже больше), и переведенный вариант был ну просто ужасен, после которого я посчитал, что нужно бы начинать читать книги только на оригинале (если оригинал - английский язык, что бывает в большинстве случаев).
Все же я обрисую ниже основные моменты данного процесса и постараюсь дать некоторые советы, которые я считаю, являются полезными в настройке данной среды, и буду рад услышать советы, замечание и предложения от вас. Итак, что же такое непрерывная интеграция? В данном случае интеграция подразумевается не между системами, как может прийти на ум, услышав данное словосочетание впервые (по крайней мере, так было у меня), а интеграция между участниками процесса разработки внутри проекта.
Требования
Первое требование, накладываемое на команду, является то, что все исходные коды должны лежать внутри системы для хранения исходного кода, неважно какой. Это требование на первый взгляд сложно считать требованием, так как сложно представить команду, у которой не используется инструмент для хранения исходного кода, но и такое на самом деле бывает. Правда, данное требование несет в себе немного больше, чем может показаться поначалу. В системе хранения исходного кода помимо самого исходного кода должны находиться все библиотеки, которые использует ваше приложение, в разумных пределах. То есть понятно, что нет смысла хранить базовые библиотеки .net. Но вот, в случае использования Toolkit’ов для Silverlight/WPF или дополнительные библиотеки, вроде MS MVC, MS Charts для .net, то есть библиотеки которые требуют явного дополнительного инсталлирования, которое можно избежать следует положить в репозитарий (систему хранения исходного кода). То есть, если один из разработчиков посчитает, что ему нужно использовать некоторый набор новых библиотек, то лучше ему положить эти библиотеки сразу же в репозитарий, и установить ссылку на них при помощи относительных путей, тогда он будет точно уверен, что в данном случае ему не нужно будет дополнительно устанавливать данные библиотеки на build сервер, и его коллеги не получат на следующий день головную боль в виде поиска необходимых библиотек.
Итак, при помощи того, что весь исходный код проекта в купе с дополнительными библиотеками лежит в одном месте и любой из команды может получить доступ к данному репозитарию, то мы, вроде как, обеспечили возможность сборки проекта в любой момент. Дальше играет, конечно же, человеческий фактор того, что все участники команды публикуют в репозитарий только в том случае, если проект собирается у них локально на компьютере. Правда, иногда проблемы бывают и с непривычно неудобными системами хранения исходных кодов (мне к сожалению приходится работать с одной из таких в данный момент).
Обеспечение того, что проект собирается еще не означает, что публикация исходного кода одного разработчика никак не влияет на код другого разработчика, в плане того, что первый разработчик верно использует методы и классы, написанные вторым разработчиком. И более сложный случай, когда первый разработчик изменяет или дополняет код второго разработчика. В данном случае, если новый код вызовет проблемы в приложении об этом могут узнать совсем не скоро – на этапе тестирования интерфейсов или еще хуже в момент демонстрации заказчику. Для избегания подобных проблем обязательно нужно использовать unit тесты совместно с билд сервером. Понятное дело, что в случае уже немаленького проекта заставлять каждого разработчика запускать все тесты локально перед публикацией нового исходного кода - это дело не благодарное. Да и понятно дело, что не все и не всегда разработчики будут это делать, а вот возложить данную задачу на билд сервер – дело хорошее. То есть, после каждой компиляции приложения билд сервер запускает unit тесты для проверки работоспособности кода и устойчивости системы. В данном случае понятно, что тесты в сумме не должны занимать много времени в процессе билда. Решать данную проблему можно разными способами. В большинстве случаев мы имеем, в принципе, небольшие приложения для автоматизации бизнес-процессов, и вряд ли настанет момент, когда наши тесты будут в сумме занимать одни сутки, хотя все конечно еще зависит от того, как эти тесты написаны. В случае действительно большого приложения данная проблема решается либо разбиением тестов на команды. То есть так как данное большое приложение, скорее всего, разрабатывается при помощи независимых модулей, в таком случае присутствуют тесты, которые тестируют именно отдельные независимые модули, разрабатываемые независимыми командами, а есть тесты, которые тестируют взаимодействие между этими модулями, так сказать это разный уровень интеграции внутри проекта, и в данном случае можно запускать тесты для модулей почаще – после каждого билда, а тесты между модулями один раз ночью. Правда, иногда и такого разделения бывает не достаточно и тогда уже нужно иметь систему с небольшим ИИ, которая будет выставлять приоритеты тестам, то есть тестировать пока это возможно по времени, и в первую очередь прогонять те тесты, которые чаще всего падали до текущего времени или падали действительно недавно, и затем по убывающей. Если перед вами возникнет такая ситуация, то могу порекомендовать поискать по данной теме научные статьи, они действительно есть и пару раз я встречал выступления на научных конференциях по данной тематике.
Настройка
После того как мы обеспечили устойчивость нашего проекта к новым изменениям. давайте подумаем о том, что нам следует еще настроить и чего требовать еще от билд сервера. Понятное дело, что в случае проблем с билдом сервер должен уведомлять участников проекта для своевременной починки – самое простое - это использовать email письма, ну и для наглядности вешать на человека сломавшего билд шуточную футболку с соответствующим текстом (такое практикуется в некоторых больших конторах, чтобы разработчики больше никогда не хотели публиковать в репазитарий код, который не работает). Так же хотелось бы видеть некоторую статистику по проекту, вроде проблемных участков кода, которые в будущем будут все сложнее сопровождать (с большими зависимостями), либо библиотеки, в которых очень низкое покрытие кода тестами. Так же хорошо бы иметь инструмент для выявления дублирующегося кода, а такое бывает часто в случае когда в команде присутствуют молодые (не зрелые) участники, когда код бывают просто копируют, не зная как вынести логику в отдельный метод или класс, либо просто в случае независимого написания одинакового кода разными разработчиками.
Так же, именно билд сервер должен выставлять текущую версию приложения. В большинстве случаев билд сервер ведет подсчет номера билда, который можно использовать в маркировке библиотек. В случае .NET приложений маркировка часто имеет вид x.x.x.x, где первым двумя цифрам можно выставлять версию приложений, которые можно разделять разными этапами разработки приложений (первая цифра) и доработки связанные с этими этапами (вторая цифра), третей цифре можно и часто выставляют номер итерации в процессе разработки данного проекта, а последней просто номер билда в рамках данной итерации. Получается, что первые две цифры зависят только от вас, какую версию вы решите им выставить, а последние две цифры независимые от времени и управляемые билд сервером. Такая детальная маркировка дает хорошую возможность определения того, что ваш клиент не использует последнюю версию, и скорее всего проблема о которой он говорит, может быть вылечена установкой более новой версии.
Так же на билд сервер можно возложить создание установочного пакета с уже готовым к употреблению набором для установки и использования. Это может быть как просто zip пакет, так и полноценный установщик - все зависит от требований вашего проекта. Прелесть того, что в любой момент времени у вас есть собранный последний пакет - я думаю очевидна: менеджер или тестер может в любой момент времени взять последнюю версию и посмотреть как выглядит продукт на текущем этапе и протестировать, не упрашивая программиста о получении последней копии, не отвлекая его (это конечно в случае если установка достаточно проста, как, например, если мы имеем обычное Windows приложение). Когда у нас Web приложение, которое нужно располагать на веб-сервере, то было бы хорошо, если наш билд сервер производил деплой последней версии на некий demo сервер, чтобы опять таки далекий от программирования человек, мог в любой момент времени получить доступ к последней версии проекта.
Что дальше?
Это пока вступительная статья для будущего цикла статей о непрерывной интеграции. О чем я еще хочу сказать: о том как использовать ресурсы вроде баз данных в случае развертывания билд сервера, как держать все базы данных в актуальном состоянии. Дальше будет практическая часть, я постараюсь рассказать об основах настройки CCNet, с каким проблемами можно столкнуться и как их решить, а так же о всевозможных дополнительных инструментах, которые можно использовать совместно с этим билд сервером, вроде nAnt, Semian, NDepend, NCover. А так же после того, как мы (наша компания) полностью перейдем на TFS, то постараюсь сравнить TFS билд сервер с CCNet. И самое главное - это решение проблем по настройки билд сервера при разработке Silverlight приложений (для автоматизации БД).
Ссылки
- Martin Fowler - Continuous Integration
- Рецензия на книгу Поль М. Дюваль, Стивен М. Матиас III, Эндрю Гловер - Непрерывная интеграция. Улучшение качества программного обеспечения и снижение риска на RSDN.
- Автоматизация для программистов: Антишаблоны постоянной интеграции
- Автоматизация для программистов: Часть 2. Антишаблоны непрерывной интеграции