Node.js role for OUTSTANDING project (RTB, Big Data, Machine Learning)! Let’s talk today!

Hudson и непрерывная интеграция django-проектов

Настройка тестовой конфигурации

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

Самому ностесту не под силу запустить джанго тесты из-за самой же джанги. И поэтому нужен отдельный стартер тестов, прокси к ностестам. Можно написать свой, но можно установить джанго-нос и не тратить свое драгоценное время. После установки надо прописать настройки в конфигурационный файл проекта:

INSTALLED_APPS = ( ... 'django_nose', ... )
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
И запускать тесты стандартным для джанго способом (через manage.py test). Если вызвать помощь с настроенным джанго-носом (manage.py help test), то можно увидеть все параметры, которые поддерживает джанго и в дополнении те, что поддерживает ностест.

Установка Гудзона

Так как Гудзон написан на яве, установить его можно на любую операционную систему. Для Debian процесс установки занимает буквально 4 команды:
wget -O /tmp/key http://hudson-ci.org/debian/hudson-ci.org.key
sudo apt-key add /tmp/key
wget -O /tmp/hudson.deb http://hudson-ci.org/latest/debian/hudson.deb
sudo dpkg --install /tmp/hudson.deb
Для Windows можно обратиться к документации по установке Гудзона и установке его в виде службы. Это я увы не пробовал, поэтому не рискну дать комментарий.

По умолчанию Гудзон запускается на порту 8080, что не всегда удобно, а то и вообще не запустится, потому что именно этот порт занят уже другим приложением. В таком случае придется изменить порт, поправив конфиг Гудзона.

В убунте конфиг расположен в файле /etc/default/hudson. А порт меняется путем редактирования параметра HUDSON_ARGS. Например:

HUDSON_ARGS="--webroot=/var/run/hudson/war --httpPort=10080"
Если меняете конфиг, то и Гудзон надо обязательно перезапустить.

Билд-скрипт

Зачем запускать тесты, постоянно вводя manage.py test [опции]. Можно написать шелл-скрипт, который сделает все за нас, или определить настройки ностестов в setup.cfg проекта.

В джанго-проектах, которые мы разрабатываем, пишется ant-скрипт build.xml, и потом в каталоге с ним запускается команда ant, чтобы проверить, что скрипт работает и тесты успешно выполняются. Когда скрипт готов, можно травить его Гудзону.

<?xml version="1.0" encoding="utf-8" ?>
<project name="dou" default="nosetests">
  <property name="python.binary" value="python" />
  <property name="src.dir" value="src"/>
  <property name="src.python" value="${src.dir}/dou" />
  <property name="src.tests.dir" value="${src.python}/tests" />
  <!-- Build reports -->
  <property name="report.dir" value="${src.tests.dir}/reports"/>
  <property name="report.xunit" value="${report.dir}/nosetests.xml"/>
  <target name="nosetests" depends="report-prepare">
    <exec executable="${python.binary}">
      <env key="PYTHONPATH" path="${src.dir}"/>
      <arg line="${src.python}/manage.py test --where=${src.python}/tests --noinput --with-xunit
--xunit-file=${report.xunit} --settings=dou.tests.setting" />
    </exec>
  </target>
  <target name="report-prepare">
    <mkdir dir="${report.dir}" />
  </target>
</project>
Этот скрипт запускает тесты с дополнительными параметрами
—where — определяет, где расположены ваши тесты
—noinput — специальная опция, которая пропускает все пользовательские запросы на ввод. Такое может произойти, когда джанго после падения тестов не удалила тестовую базу данных, а при новом старте спрашивает «не хотите ли удалить уже существующую тестовую базу? ». Естественно, когда скрипт будет выполнятся на сервере без этого параметра выполнение тестов просто может повиснуть.
—with-xunit — опция которая сгенерит xml с форматом junit отчетов. Позже в Гудзоне зададим одну из опций.
—xunit-file — указывает в какой файл надо сохранить отчет.

Создание проекта в Гудзоне

Итак, Гудзон установлен и запущен. Осталось создать проект и запустить первый билд. Идем в браузер и вводим урл http://localhost:8080/ (я буду ссылаться на конфигурацию гудзона по умолчанию).

Первым делом видим вот такую картину:
hudson

«New Job» — как раз то, что нам нужно. Как только перешли на страницу с созданием, Гудзон предложит создать один из типов проекта. Смело выбирайте «Build a free-style software project», вводитм название проекта и нажимаем на кнопку «ОК».

Дальше у вас огромное количество опций, большинство из которых вам скорее всего не понадобится поначалу. Более значимые это «Source Code Management», «Build Triggers» и «Build». На скриншоте приведен пример настройки проекта с чистого листа.

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

В опции «Build» указываем таргеты, которые будут выполнены. У нас в build.xml в проекта стоит атрибут default= «nosetests», это значит, что если вызвать ant без таргетов, то выполнится таргет nosetests. Поэтому можно просто выбрать Ant и ничего не вводить в остальные появившиеся поля.

Еще момент с Build Trigger. Кто не знаком с кроном, пять звездочек означает «проверять каждую минуту». Формат MINUTE (0−59) HOUR (0−23) DAY (1−31) MONTH (1−12) DAYOFWEEK (0−7)

Отдельным пунктом стоит опция «Post-build Actions». Это как раз то место, где происходит главная магия генерации отчетов. В build.xml указан параметр куда надо сохранять отчет, поэтому заполним пункт с «Junit» относительным путем до xml-файла с отчетом сгенерированного ностестом.

Сохраняем и ждем пока настанет время билда. Если все успешно, то после пары запусков тестов на главной странице проекта появится график.

Консоль

Если тесты не прошли, то билд будет обозначен красным кружком, и чтобы понять, что же все-таки произошло, можно воспользоваться консолью Гудзона. В Build History на странице проекта содержатся ссылки на пройденные билды. Выбираете любой и уже детально изучаете его.

Дополнительные фишки (плагины)

Ознакомиться с имеющимися плагинами можно на официальной вики-странице. Установка не занимает много времени. Процесс примерно такой. Вы скачиваете hpi-файл и копируете его в каталог plugins Гудзона. После чего перезапускаете.

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

Cobertura позволяет сгенерировать отчет по покрытию кода тестами. Violations показывает графики по дублированию кода или соблюдению pep8, это все как пример. На самом деле там целых 9 типов нарушений, которые можно использовать, но мы используем только 2.

Для кобертуры необходимо поставить coverage пакет, дополнительно плагин для ностестов nose-xcover, и немного изменить билд-скрипт.

<target name="nosetests" depends="report-prepare">
    <exec executable="${python.binary}">
      <env key="PYTHONPATH" path="${src.dir}"/>
      <env key="PYTHON_EGG_CACHE" path="/tmp" />
      <arg line="${src.python}/manage.py test --where=${src.python}/tests --noinput --with-xunit
 --with-xcoverage --xunit-file=${report.xunit} --cover-package=dou --settings=dou.tests.setting" />
    </exec>
</target>
Как видно добавились опции —with-xcoverage и —cover-package=dou. Вторая опция задает именно тот пакет, который мы тестим, пропуская лишние. И натравить кобертуру на сгенерированный после выполнения тестов xml-отчет, через пункт «Publish Cobertura Coverage Report». Для нас достаточным было указать строку «Cobertura xml report pattern» в **/coverage.xml.

Для violations ставим дополнительно пакеты pylint и clonedigger. И дополняем наш билд-файл новыми таргетами, которые затем указываем в настройках проекта в Гудзоне.

<?xml version="1.0" encoding="utf-8" ?>
<project name="dou" default="nosetests">
  <property name="pylint.binary" value="pylint" />
  <property name="python.binary" value="python" />
  <property name="cpd.binary" value="clonedigger" />

  <property name="src.dir" value="src"/>
  <property name="src.python" value="${src.dir}/dou" />
  <property name="src.tests.dir" value="${src.python}/tests" />

  <!-- Build reports -->
  <property name="report.dir" value="${src.tests.dir}/reports"/>
  <property name="report.pylint" value="${report.dir}/pylint.txt"/>
  <property name="report.cpd" value="dou/tests/reports/clonedigger.xml"/>
  <property name="report.xunit" value="${report.dir}/nosetests.xml"/>

  <target name="nosetests" depends="report-prepare">
    <exec executable="${python.binary}">
      <env key="PYTHONPATH" path="${src.dir}"/>
      <arg line="${src.python}/manage.py test --where=${src.python}/tests --noinput --with-xunit
--with-xcoverage --xunit-file=${report.xunit} --cover-package=dou --settings=dou.tests.setting" />
    </exec>
  </target>

  <target name="report-prepare">
    <mkdir dir="${report.dir}" />
  </target>

  <target name="report-clonedigger" depends="report-prepare">
    <exec dir="${src.dir}" executable="${cpd.binary}">
      <arg line="--cpd-output -o ${report.cpd} dou" />
    </exec>
  </target>

  <target name="report-pylint" depends="report-prepare">
    <exec dir="${src.dir}" executable="${pylint.binary}" output="${report.pylint}">
      <arg line="-f parseable -i y dou --output-format=parseable --ignore-comments=y
 --min-similarity-lines=4 --ignore=settings.py,manage.py"/>
    </exec>
  </target>
</project>
Дополнительно Гудзону необходимо знать откуда брать новые отчеты, для этого заполняем в конфигурации в раздел «Report Violations» пункты cpd и pylint. По примеру моего билд-файла и структуры каталогов они принимают значения в **/tests/reports/clonedigger.xml для cpd и **/tests/reports/pylint.txt для pylint соответственно.

Я не затронул тему об авторизации пользователей, установки прав, но это и не было целью данного опуса.

Ну и пара-тройка скринов Гудзона:
скрин раз, скрин два, скрин три, скрин четыре.

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

  • Популярное

3 комментария

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

Намучившись с nose, написал интеграцию со стандартным unittest runner’омhttp://pypi.python.org/pypi/dj...

Так же пришел к выводу, что держать билд таски в хмл/ант — плохая практика.

Тема багов не раскрыта.Из-за того что nose использует хитрый тест-лоадер возникают странные проблемы, если включать поддержку doctest’ов.Но даже без doctest’ов недавно накткнулся на косяк с десериализацией. С обычным django-runner’ом проблем не возникало.Кстати, для запуска тестов под hudson я пользуюсь shell скриптом — http://github.com/kmmbvnr/djan...

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