Непрерывная интеграция (англ. Continuous Integration, CI) — это практика разработки программного обеспечения, которая заключается в выполнении частых автоматизированных сборок проекта для скорейшего выявления и решения интеграционных проблем. В обычном проекте, где над разными частями системы разработчики трудятся независимо, стадия интеграции является заключительной. Она может непредсказуемо задержать окончание работ. Переход к непрерывной интеграции позволяет снизить трудоёмкость интеграции и сделать её более предсказуемой за счет наиболее раннего обнаружения и устранения ошибок и противоречий.
Непрерывная интеграция является главным шагом на пути к непрерывному развертыванию ПО и одним из основных приёмов экстремального программирования.
В статье я опишу запуск тестов и проверку кода на различные стандарты. Но так же можно использовать тесты BDD, нагрузочные тесты, smoke тесты, тесты проверки безопасности (SQL инъекции, XSS, CSRF, clickjacking) и многие другие.
Установка сервера Continuous Integration
Средства непрерывной интеграции:
- Bamboo
- Hudson
- Jenkins
- CruiseControl
- TeamCity
- BuildBot
- Travis CI
- Scrutinizer
Свой выбор сделал в пользу TeamCity, т.к. эта система наиболее простая в установке и настройке.
В качестве сервера я использую Ubuntu и мои проекты написаны на языке PHP.
Поэтому на чистый сервер нужно установить необходимое ПО, например так:
$ sudo apt-get install acl curl mc git nginx php5-cli php5-common php5-mysql php-apc php5-gd php5-fpm php5-cgi php-pear php5-mcrypt php5-intl php5-xdebug php5-dev php5-curl php5 php5-dev php5-imagick php5-pspell php5-sqlite php5-xmlrpc php5-xsl php5-json libssh2-php redis-server openjdk-7-jdk sendmail npm nodejs supervisor ufw jpegoptim optipng graphviz
Далее необходимо убедиться, что установлен Composer - менеджер php пакетов.
После этого нужно установить инструменты тестирования качества кода.
Теперь можно приступить к установке TeamCity:
$ cd /usr $ sudo wget https://download-ln.jetbrains.com/teamcity/TeamCity-9.0.3.tar.gz $ sudo tar zxvf TeamCity-9.0.3.tar.gz $ sudo rm TeamCity-9.0.3.tar.gz $ sudo chown -R www-data:www-data /usr/TeamCity $ sudo mkdir /opt/TeamCity $ sudo chown -R www-data:www-data /opt/TeamCity
Здесь я скачал и распаковал последнюю на момент написания статьи версию TeamCity. Другие версии устанавливаются аналогично приведенным командам.
Далее выполнив команду, мы получим sh файл, который будем использовать для старта системы.
$ sudo nano /etc/init.d/teamcity
Содержимое файла:
#! /bin/sh # /etc/init.d/teamcity - startup script for teamcity export TEAMCITY_DATA_PATH="/opt/TeamCity/.BuildServer" # Carry out specific functions when asked to by the system case "$1" in start) echo "Starting script teamcity" start-stop-daemon --start -c www-data --exec /usr/TeamCity/bin/runAll.sh start ;; stop) echo "Stopping script teamcity" start-stop-daemon --start -c www-data --exec /usr/TeamCity/bin/runAll.sh stop ;; *) echo "Usage: /etc/init.d/teamcity {start|stop}" exit 1 ;; esac exit 0
И запустим сервис:
$ sudo chmod 0755 /etc/init.d/teamcity && sudo service teamcity start
CI сервер будет доступен по url: https://127.0.0.1:8111/
Для удобства можно создать proxy-server на 80 порту. Пример для nginx (не забудьте указать вместо 'ci.makedev.org' свой домен):
server { listen 80; server_name ci.makedev.org; access_log off; location / { proxy_pass https://127.0.0.1:8111; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-for $remote_addr; port_in_redirect off; proxy_connect_timeout 300; } }
При этом все остальные порты в целях безопасности нужно закрыть (не забыть открыть 22 порт для ssh). Для этого, например в Ubuntu Server, можно использовать ufw:
$ sudo apt-get install ufw $ sudo ufw enable $ sudo ufw default deny $ sudo ufw allow 8111 $ sudo ufw allow 80 $ sudo ufw allow 22 $ sudo ufw allow 80/tcp
Вот и все, сервер непрерывной интеграции установлен. Теперь осталось его настроить.
Настройка сервера Continuous Integration
Сразу предупреждаю, что описание максимально детализированное со множеством скриншотов. Главный принцип, который был использован при написании статьи, лучше один раз увидеть, чем сто раз услышать, или в нашем случае прочитать.
Всю настройку сервера CI можно сделать через веб админ панель (главная причина выбора TeamCity). Для этого нужно зайти по адресу https://127.0.0.1:8111/ или указать домен, который используется как proxy (например: https://ci.makedev.org/).
Если все сделали правильно, то мы увидим экран приветствия.
Далее нам нужно выбрать базу данных, в которой TeamCity будет хранить свои настройки. Я оставляю базу данных по умолчанию, т.к. не планирую использовать СУБД для своих проектов.
После система создаст базу данных автоматически.
И проведет инициализацию компонентов.
Перед началом работы нам нужно принять лицензионное соглашение.
После этого TeamCity предложить создать пользователя, который будет суперадминистратором.
Предварительная настройка системы закончена и мы видим профайл созданного пользователя. Здесь мы можем установить email, пароль и другие настройки.
Вот мы на странице проектов, но пока здесь пусто.
Создадим новый проект. Главный проект будет называться My bundles, здесь я буду тестировать все свои Symfony2 бандлы.
Так выглядит экран General Settings (Общие Настройки) нового пустого проекта.
Т.к. мы планируем использовать различные инструменты проверки качества кода, которые генерируют различные отчеты, нам нужно создать вкладки, в которых будут отображаться отчеты. Для перехода к окну редактирования вкладок отчетов нужно кликнуть Report Tabs:
Можно создать вкладки для проекта либо для сборки. Я использую вкладки для сборок, т.к. у меня каждая сборка представляет свой собственный бандл. Жмем Create new build report tab (Создать новую вкладку отчета сборки).
Для каждого отчета нужна своя вкладка. Я буду использовать следующие вкладки для соответствующих инструментов:
Code Coverage coverage.zip!index.html CodeBrowser cb.zip!/index.html phpDocumentor docs.zip!index.html PHPLOC phploc.html PHP Depend pdepend.html
Здесь мы видим особый формат запуска - из архива. Так работает TeamCity. Где геренируются эти архивы я расскажу позже.
Сделаю небольшое отступление. Есть возможность настроить очистку файловой системы от старых сборок и артефактов. Иначе есть возможность заполнить жесткий диск сервера. Это можно сделать в настройках проекта на вкладке Clean-up Rules (Правила очистки).
Edit позволит отредактировать правила для созданного проекта. Здесь я использую правило Everything для очистки всего (Clean everything including artifacts, history, and statistical data) и очищать все, что старше 10 последних успешных сборок.
Еще один важный момент. Т.к. у нас доступ к репозиторию через ssh, нам нужно настроить ssh ключ, который необходим для доступа к коду в системе VCS (в моем случае это git, об этом далее). Жмем на вкладку SSH Keys (SSH ключи).
Я создал свой ключ для доступа к github, инструкция описана в справке github. Нам нужен приватный ключ id_rsa, который хранится в папке .ssh. Нажимаем кнопку Upload SSH Key (Загрузка SSH ключа) и в диалоговом окне указываем путь к файлу.
После можно увидеть результат загрузки ключа.
С настройками проекта закончили. Теперь можно создать конфигурацию сборки. Вернемся к окну General Settings (Общие Настройки).
Создать сборку можно, нажав кнопку Create build configuration (Создать конфигурацию сборки). Я буду создавать конфигурацию сборки для своего проекта GtmBundle (Google Tag Manager) для Symfony2.
TeamCity сразу же предлагает нам настроить сборку, делая шаг за шагом. Первый шан - настройка vcs (системы контроля версий), в которой хранится код проекта.
Все свои проекты я храню на github, соответственно я использую систему контроля версий git.
Одно уточнение. Здесь я использую метод авторизации (Authentication method) - загруженный ключ (Uploaded Key), о котором речь шла ранее.
Теперь перед сохранением можно проверить подключение. Жмем Test connection (проверка подключения). Если все сделано правильно - увидим диалог с сообщением об успешном подключении.
Сохраняем настройки и видим наше подключение во вкладке сборки Version Control Settings (настройки системы контроля). Здесь можно менять или добавлять систему контроля версий и ее настройки.
Не забудьте настроить очистку файлов перед началом сборки. Для этого кликните по ссылке Show advanced options (показать дополнительные параметры) и поставьте галочку в поле Clean build (очистить сборку).
Здесь можно указать расположение проекта для сборки, указав Checkout directory (каталог для копирования проекта из VCS). Я использую Auto для выделения места внутри tmp, но если надо статический путь, например, если нужно открыть проект миру через nginx, его можно указать здесь, выбрав Custom path (пользовательский путь).
Настроим расписание запуска сборки. Оптимальный вариант проверять каждый коммит и ночью по расписанию проверять зависимости. Для настройки перейдем на вкладку Triggers (тригеры).
Нажимаем Add new Trigger (добавить новый триггер). Здесть VCS Trigger - запуск сборки по коммиту, Schedule Trigger - запуск по расписанию.
Теперь мы видим список наших тригеров.
Осталось самое главное - сделать шаги проверки, используя выше указанные инструменты. Я опишу каждый шаг, затем покажу пример работы скрипта и отчет, который получается после его выполнения.
Переходим на вкладку Build Steps (шаги сборки) и нажимаем кнопку Add build step (Добавить шаг сборки).
Все наши шаги будут работать путем выполнения скриптов из командной строки. Поэтому Runner type (тип запуска) выбираем Command Line (командная строка).
Поле Custom script (пользовательский сценарий) представляет собой bash скрипт. Он позволяет писать команды друг за другом в новой строке или через && в одной строке как простые Linux команды.
Описание всех командах можно прочитать в статье.
Первая команда будет CodeBrowser:
mkdir cb && phpcb -s . -o cb
Далее PhpDocumentor:
mkdir docs && phpdoc -d . -t docs
Далее PHPLint:
find . -type f -name '*.php' -exec php -l '{}' ';' log=`find . -type f -name '*.php' -exec php -l '{}' ';' | grep -v 'No syntax '`; if [ x"${log}" != x ]; then exit 1; else echo "No syntax errors detected in PHP scripts"; fi
Далее PHPDCD:
phpdcd .
Далее PHPMD:
phpmd . text codesize,unusedcode,naming
Далее PHPCS:
find . -type f -name '*.php' -exec phpcs --standard=Symfony2 '{}' ';' log=`find . -type f -name '*.php' -exec phpcs --standard=Symfony2 '{}' ';' | grep ERROR`; if [ x"${log}" != x ]; then exit 1; else echo "No syntax errors detected in PHP scripts"; fi
Далее PHPCPD:
phpcpd .
Далее PHP Depend:
#!/bin/sh log=`pdepend --summary-xml=summary.xml --jdepend-chart=jdepend.svg --overview-pyramid=pyramid.svg .`; echo "${log}" echo "<pre><code>${log}</code></pre> <p><img src='jdepend.svg' alt='jdepend.svg'/></p> <h5><strong>jdepend.svg</strong></h5> <p>Каждый из «шариков» отображает файлы определенного пакета-папки (в svg-файле при наведении на шарик всплывает подсказка с названием пакета). Размер шарика определяется количеством абстактных и конкретных классов в пакете.</p> <p>По оси Х откладываются значения <i>коэффициента абстракции</i> (abstraction ratio), по оси Y – <i>коэффициента нестабильности</i> (instability ratio).</p> <p>Прямая изображает оптимальное соотношение между этими коэффициентами.</p> <p><strong>Коэффициент абстракции</strong> (abstraction ratio, A) – отношение количества абстрактных классов и интерфейсов к общему числу классов в анализируемом пакете. Принимает значение от 0 до 1. Если равно 0, то пакет абсолютно конкретный, если 1 – то абсолютно абстрактный.</p> <p><strong>Коэффициент центростремительной связности</strong> (afferent couplings, Ca) – количество пакетов, которые зависят от классов текущего пакета.</p> <p><strong>Коэффициент центробежной связности</strong> (efferent couplings, Ce) – количество пакетов, от которых зависят классы текущего пакета.</p> <p><strong>Коэффициент нестабильности</strong> (instability ratio, I) – отношение коэффициента центробежной связности к сумме коэффициентов связности: I = Ce / (Ce + Ca). Показывает устойчивость пакета к изменениям. Принимает значение от 0 до 1. Если равен 0, то считается абсолютно стабильным пакетом (вообще не зависит от других пакетов), если 1 – то абсолютно нестабильным (полностью зависимый пакет, от которого не зависят другие).<p> <p><img src='pyramid.svg' alt='pyramid.svg' /></p> <h5><strong>pyramid.svg</strong></h5> <p>Данные отображены в виде пирамиды, потому что на ее внешних гранях располагаются коэффициенты, полученные как отношение значений характеристики с уровня ниже и текущего уровня. Значения коэффициентов «подкрашены» в зависимости от попадания его в диапазон значений – низкий, средний и высокий.</p> <p>Т.е., например, 0.176 = CYCLO / LOC.</p> <p>Теперь подробнее об используемых аббревиатурах.</p> <p>Слева:</p> <ul> <li><strong>CYCLO</strong> (Cyclomatic Complexity) – цикломатическая сложность пакетов (на основе числа ветвлений в коде типа if, for, foreach).</li> <li><strong>LOC</strong> (Lines Of Code) – число строк кода.</li> <li><strong>NOM</strong> (Number Of Methods+functions) – число методов классов + число функций.</li> <li><strong>NOC</strong> (Number Of Classes) – число классов.</li> <li><strong>NOP</strong> (Number Of Packages) – число пакетов.</li> <li><strong>AHH</strong> (Average Hierarchy Height) – средняя глубина иерархии.</li> <li><strong>AND</strong> (Average Number of Derived classes) – среднее число классов-наследников.</li> </ul> <p>Справа:</p> <ul> <li><strong>FANOUT</strong> (Number of Called Classes) – число использований классов (по-видимому, число созданий объектов классов).</li> <li><strong>CALLS</strong> (Number of Operation Calls) – число вызовов методов и функций.</li> </ul>" > pdepend.html
Далее PHPLOC:
log=`phploc --log-xml phploc.xml .`; echo "${log}" echo "<pre><code>${log}</code></pre>" > phploc.html
Далее нужно выполнить запуск модульных тестов (phpunit) и проверить покрытие кода тестами (code coverage). Но сначало надо добавить все зависимости. Это можно сделать с помощью Composer - менеджера php пакетов.
PHPUnit:
composer update mkdir coverage phpunit --coverage-text --coverage-html coverage
У меня получился такой список шагов:
Шаги можно включать / выключать, менять местами, двигая по списку.
Пришло время сделать первый запуск. Проверим проект и увидим список артефактов, которые потом сможем использовать в наших отчетах. Перейдем на главную страницу проектов:
И запустим сборку (Run):
В процессе настройки и добавлении шагов можно запускать и проверять выполнение сборки, экспериментировать. В первое время сборки могут быть неудачными пока окончательно не настроить проект (или новый проект может быть не отлажен всеми указанными инструментами). Так выглядит неудачная сборка:
Выбрав Build log (лог сборки), можно увидеть причину неудачи.
После исправления кода мы увидим прошедшую сборку.
При клике на ссылку Success (успех), мы увидим статистику сборки.
Здесь у нас должны быть вкладки с нашими отчетами, но их нет, т.к. мы не настроили Artifacts (артефакты) сборки.
Для настройки артефактов жмем Edit Configuration Settings (изменить настройки) и заполняем Artifact paths (пути артефактов).
cb => cb.zip coverage => coverage.zip docs => docs.zip jdepend.svg pyramid.svg phploc.xml summary.xml pdepend.html phploc.html
Конструкция cb => cb.zip как раз и формирует архивы, о которых мы говорили ранее. Эти архивы используются как своего рода локальные статические серверы для html файлов.
TeamCity заботливо сразу же предложит сохранить изменения. Сохраняем и перезапускаем сборку. Ждем выполнения, переходим в Success (успех) и видим наши вкладки отчетов (Code Coverage, CodeBrowser, phpDocumentor, PHPLOC, PHP Depend).
Code Coverage:
CodeBrowser:
phpDocumentor:
PHPLOC:
PHP Depend:
Поздравляю. На этом настройка практически закончена. Остался последний момент - настроить отправку писем с уведомлениями о проблемах, если такие будут.
Для настройки почты переходим в Administration (администрирование), затем Email Notifier (уведомления по почте) и заполняем поля. Т.к. я установил sendmail, мне достаточно указать локальный адрес сервера и email, с которого будет отправляться письма. Здесь небольшое уточнение, email должен включать в себя созданный домен (в моем случае ci.makedev.org).
Жмем Test connection (проверка подключения), в появившемся диалоговом окне вводим свой личный email, на которое отправится тестовое письмо, и видим сообщение об успешной отправке.
Теперь нужно настроить отправку писем для созданного ранее пользователя. Жмем на имя пользователя в верхнем меню и попадаем на профайл пользователя.
Здесь мы можем заполнить имя пользователя и обязательно надо настроить его email, на который и будут отправляться письма.
Далее настриваем правила отправки. На этой же странице находим Watched Builds and Notifications, в нем поле Email Notifier и жмем Edit.
Здесь я отметил только поле Only notify on the first failed build after successful (отправлять только при первой неудачной сборки после успешной сборки).
Жмем сохранить (Save) и на этом все. Поздравляю, сервер CI создан и полностью настроен для одного проекта.
Но у меня есть еще похожие проекты, которые я хотел бы проверить. Можно повторять шаги для каждого проекта. А можно скопировать конфигурацию, что я и сделаю далее.
Для этого выбираем в главном меню Projects, затем жмем My bundles и Edit Project Settings. Так мы попадаем в настройки главного проекта.
Здесь мы видим Build Configurations и созданную сборку GtmBundle. Напротив нее жмем кнопку More и выбираем Copy build configuration... (копировать конфигурацию сборки).
В появившемся диалоговом окне указываем название сборки и главный проект.
В новой сборке мы можем изменить описание. TeamCity сразу же предложит сохранить изменения, что мы и сделаем.
Осталось изменить github репозитойрий. Переходим на Version Control Settings. Видим подключенный репозиторий и жмем Edit.
Изменяем параметры репозитория.
TeamCity предложит использовать измененный репозиторий только для текущей сборки или использовать для всех сборок внутри проекта. Выбираю первый вариант.
Тестируем подключение
Теперь на главной странице проектов у меня 2 сборки внутри главного проекта.
Запускаем сборку и ждем завершения.
Осталось повторить это для остальных моих проектов.
Хорошей практикой считается создать backup. TeamCity позволяет нам это сделать. Переходим в Administration, затем находим Backup. Жмем Start Backup. После этого получим ссылку для скачивания.
Теперь действительно все. Система создана, настроена и сохранена.
Другие инструменты Continuous Integration
Есть и другие инструменты платные и условно бесплатные, которые можно использовать для проверки кода (тесты, качество кода).
Отличный инструмент Travis CI для автоматического запуска юнит тестов (phpunit), при этом он позволяет проверять одновременно разные ветки и на разных версиях php одновременно.
Для использования достаточно разместить .travis.yml файл в корне github репозитория.
language: php php: - 5.3 - 5.4 - 5.5 - 5.6 - 7.0 - hhvm matrix: allow_failures: - php: 7.0 sudo: false install: travis_retry composer install --no-interaction --prefer-source script: phpunit --coverage-text
Зайти в панель управления Travis CI и управлять проектом можно, используя github аккаунт.
Еще один интересный проект Scrutinizer чем-то похож на Travis CI, но является условно бесплатным. Позволяет бесплатно проверять качество кода, но тесты и покрытие кода доступны лишь в платной версии.
Каждый из этих серверов CI позволяет создать markdown (иконка со статусом проверки), который можно разместить в описании проекта.
Не забываем об инструментах тестирования качества кода и о том, что скорость загрузки JavaScript, CSS файлов и изображений является одним из показателей качества.