В разрезе: новостной агрегатор на Android с бэкендом. Система последовательной интеграции +4


Вводная часть (со ссылками на все статьи)

«Система последовательной интеграции», не уверен, что перевод правильный – лучше называть система непрерывной интеграции (continuous integration).
С ними первый раз я столкнулся в начале своей работы, когда 5-7 программистов усиленно писали код и тесты, меняли конфигурационные файлы и лили все свои результаты в trunk/master. В итоге частенько (так частенько, что аж бесило) в основной ветке появлялось нечто нерабочее. Причём выявлялось это тогда, когда нужно было развернуть тестовый стенд, что сильно замедляло работу группы тестирования (ждали исправления) и разработчиков (соответственно исправляли). Т.е. работоспособность кода не контролировалась после помещения его в репозиторий.

image
Тогда на помощь нам пришёл Hudson (http://hudson-ci.org/) (сейчас более известен как Jenkins (https://jenkins.io/), хотя формально сам Hudson остался, но не так популярен и не так активно развивается) – он осуществлял множество дел:

  • выполнял сборку и запуск модульных тестов после каждого коммита;
  • после коммита в ветку с релизами убивал старый и запускал новый тестовый стенд для команды тестировщиков;
  • генерировал отчёты по исходным кодам (покрытие тесами, показатели соответствия стандартам тестирования);
  • генерировал документацию для wiki.
  • кроме того через его интерфейс можно было отслеживать кто какие коммиты вносил, как со временем менялось покрытие тестами, за какое время осуществлялась сборка и прогон тестов и самое главное — из-за кого был сломан билд.


Это было реальным шагом вперёд в части повышения качества и прозрачности работы команды разработчиков (я уже не говорю про минимизацию вероятности появления «каши» в master/trunk’е):
  • удалось выяснить, что какие типовые ошибки допускал при работе с системой контроля версий (кого-то наказали, кого-то обучили);
  • для предотвращения совместного редактирования одного большого файла с конфигурациями было выполнено его разделение, а так же изменена логика сборки;
  • были разработаны и зафиксированы правила работы с ветками (ранее работали по принципу «ведь все понимают…», т.е. официальных правил не было).


Но это всё было ранее. Теперь вопрос: в чём смысл использования системы непрерывной интеграции при работе в одиночку?
До определённого момента смысла не было реально никакого. До тех пор пока проект не разросся (даже при участии одного разработчика) и тут наличие сервера интеграции показало свою реальную ценность:
  • в связи с тем, что стенд для запусков интеграционных тестов формируется с помощью конфигурационных файлов, в которых прописаны версии других компонентов, которые потом подтягиваются системы хранения готовых артефактов (на базе Nexus Sonatype) – достаточно важным вопросом стало корректное указание версий компонентов и проверка их совместной работоспособности. Однако эта проверка вещь затратная по времени (время на закачку артефактов, сборку стенда, запуск, прогон интеграционных тестов). Поэтому на сервере интеграции я настроил задачу на свою рабочую ветку, в которую иногда сливаю, по моему мнению, рабочие варианты и продолжаю работу. По истечении какого-то времени могу проверить – верно ли моё предположение о работоспособности или нет;
  • запуск модульных тестов для локального кластера с Apache Storm, даже с подменой всех компонентов через Dependency Injection на Stub/Mock, занимает порядочное количество времени и я иногда забываю запускать их все целиком, что приводит (так же иногда) к неработающему коду в master – при наличии CI это выявляется моментально после «git push»;
  • (не систематично, но пару раз было) с учётом наличия в git не только кода, но так же и конфигурационных файлов и файлов с данными, при корректировке «.gitignore» возникали ситуации, когда из репозитория исключались вместе с ненужными и нужные файлы. При этом на моей машине они сохранялись и тесты работали, а вот на сервере CI – моментально этот факт отслеживался.


Дополнительным интересными особенностями Jenkins является его интерфейс, позволяющий наблюдать за:
  • временем выполнения pipeline и её стадий
    image;
  • графическим отражение количества отработанных/пропущенных/отработанных тестов
    image;
  • табличным отражение количества отработанных/пропущенных/отработанных тестов
    image;
  • детальными данными по последним коммитам, вызвавшим сборку
    image;
  • детальными данными по коммиту, вызвавшему конкретную сборку с возможностью перехода в тот же GitHub для детального изучения изменений
    image.


Pipeline


Самым значительным функциональным улучшением Jenkins по прошествии некоторого времени его неиспользования стало для меня появление plugin’а «Pipeline», который позволяет либо через интерфейс самого Jenkins, либо через отдельный конфигурационный файл самого проекта (файл Jenkinsfile) определить, что будет выполняться/запускаться/проверяться сервером CI при получении кода вашего проекта/модуля.

Данный подход позволяет разделить этапы настройки опроса репозитория (выполняется в интерфейсе Jenkins) и настройку самих процедур тестирования/интеграции (файл Jenkinsfile), давая возможность разработчику или человеку ответственному за развёртывание настроить/изменить/добавить необходимые этапы тестирования/интеграции, без доступа к Jenkins.

Например, в одном из моих под-проектов файл Jenkinsfile имеет следующее содержание:
#!groovy
node {
   def projectName = 'glr_parser'
   def gradleHome = tool name: 'gradle', type: 'gradle'
   stage('Checkout') { // for display purposes
      // Get some code from a GitHub repository
	  // parent directory
	  checkout changelog: true, poll: false, scm: [$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CleanBeforeCheckout']], submoduleCfg: [], userRemoteConfigs: [[url: 'story_line2_build.github.com:fedor-malyshkin/story_line2_build.git']]]
	  // project itself
	  checkout changelog: true, poll: true, scm: [$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "${projectName}"]], submoduleCfg: [], userRemoteConfigs: [[url: "story_line2_${projectName}.github.com:fedor-malyshkin/story_line2_${projectName}.git"]]]
	  // deployment
	//   checkout changelog: true, poll: false, scm: [$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'deployment']], submoduleCfg: [], userRemoteConfigs: [[url: 'story_line2_deployment.github.com:fedor-malyshkin/story_line2_deployment.git']]]
   }
   stage('Assemble') {
	   dir(projectName) {
	   // Run the maven build
	   sh "'${gradleHome}/bin/gradle' -Pstand_type=test assemble"
	   }
  }
  stage('Test') {
	  dir(projectName) {
	   try {
		   sh "'${gradleHome}/bin/gradle' -Pstand_type=test test"
	   }
	   finally {
		   junit "build/test-results/test/TEST-*.xml"
	   }
	   }
  }
}

Как видите – это фактически несколько модернизированный groovy (DSL для Jenkins). Pilpeline состоит из этапов, шагов и узлов. При этом этапы отражаются в Stage View Plugin’е, который все так любят показывать в презентациях и прочих выступлениях.

При этом разработчик вправе сам определить, чем он планирует ограничиться в этапе проверки: запуском модульных тестов, проверкой стандартов кодирования, запуском интеграционных тестов или размещением прошедших проверку артефактов в продуктив (реализуя таким образом принцип Continuous Delivery).

Tips


  • Проблемой при настройке задач Jenkins для меня была необходимость предоставления доступа на чтение к закрытому репозиторию GitHub (при этом забивать пароли с учётными записями в настройки не было ни малейшего желания), это было решено с использованием GitHub Deploy Keys (см. мою более раннюю статью)
  • Для отслеживания статуса задач на мобильном устройстве (Android) использую на удивление хороший клиент с очевидным именем Jenkins.
    Пара скриншотов:
    image
    image
    image
  • Есть ещё интересный толстый клиент для PC для отслеживания статуса сборки CatLight (но опыта использования нет).


На текущий момент из минусов Jenkins можно отметить лишь недостаточную документацию на продукт: часто в разделе документации на сайте разработчика можно увидеть надпись: «This is still very much a work in progress». Однако это не должно отталкивать от, по-настоящему, гибкого и мощного продукта.

Спасибо за внимание!
-->


К сожалению, не доступен сервер mySQL