О структуризации и автоматизации

в 11:05, , рубрики: continuous integration, Песочница, разработка, метки: ,

Хотел бы поделится небольшим опытом по автоматизации и стандартизации процесса разработки. Статья не претендует быть истиной, но я надеюсь будет интерестным чтивом для конца рабочей недели.

В начале было слово

Для начала немножко расскажу с чего все начиналось:

  • Репозитарий в котором была только одна основная ветка.
  • 5 проектов, которые должны взаимодействовать в единой этосистеме. И еще несколько на подходе.
  • Постоянно дополняющиеся требования, приводящие к необратимым изменениям в протоколе общения между всеми компонентами (не самая лучьшая ситуация, но так уже было).
  • Каждый из проектов имел свой формат версии. Одни основывались на ревизии, другие на дате, третье и на том и на том.
  • Отсутсвие какой либо соглассованности между всеми компонентами (была только на словах).
  • Отсутсвие какой либо автоматизации процессов (что то всетаки было, но не густо).
  • Отсутствие единого хранилища всех дистрибутивов (одни хранились в svn, другие в Track Studio, третье пересобирались каждый раз из исходников (было забавно смотреть как тестировщики выгружали из svn исходники приложения и делали make clean all что бы получить новый бинарник для тестироания)).

В начале была структуризация

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

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

В ходе долгих дебатов о том “как правильно вести svn” был выбран вариант фиксирования тегов и сформулирован ряд требований/правил которому следует придерживаться:

Trunk

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

Tag N

  • все компоненты согласованы по функционалу
  • функционал не меняется
  • участвует в основном процессе тестирования
  • Может быть помечен как Production после тестирования
  • Может быть заморожен и забыт (удален) после функционального тестирования
  • Bug Fix мержутся в Trunk
  • Из Trunk мержуться только критические баги выявленные в более ранних версиях

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

Наши теги, не такие уж и теги. Но они ближе по своей сути именно в понятию “тег” нежели к понятию “бранч”.
На обсуждении был и другой формат ведения SVN — вести основную разработку в бранчах.Т.е. если у нас возникает мажорное изменение, то мы создаем отдельный бранч, реализуем там это изменение и мержим его в транк. В этом случае транк всегда должен быть стабильным, что бы в любой момент времени можно было взять все компоненты системы и они были как минимум согласованы по функционалу, как максимум готовы для продакшена. В нашем случае это выглядит, не жизнеспособным. Так как порой одно мажорное изменение может вылезти в момент реализации другого. Тогда пришлось бы создавать на него отдлельный бранч и переключатся на него. Проблемы начались бы в момент когда нужно было бы смержить две векти в транк.
У нас весьма долкий цикл разработки, и еще не обкатан процесс перехода с версии на версию. Поэтому отдавая в публичное тестирование наш продукт, мы хотели бы иметь возможность делать хот фиксы. Но не имея тега(ветки) на каждую финальную версию, сделать это было бы не возможно. Так же мы не имеем возможности говорить, что “мол это будет исправленно с ледующей версии”.
Дальше в статье я буду использовать термин “тег” именно в том значении которое я описал выше.

Мы срезаем скальпы

От тегов не было бы пользы не будь в нем все компоненты соглдасованы по функционалу. Узким местом в это случае является момент времени когда мы срезаем скальп создаем тег. Как правило на одной из планерок мы принимаем решение о том, что в течении ближайшей недели мы должны подготовить транк для создание тега. Т.е. каждый из проектов должен прийти в состояние когда он согласован по функционалу со всеми компонентами с которыми он непосредственно общается. После этого мы создаем тег. Который уходит в тестирование. Тег мы именуем в специальном формате MAJOR_NUM.MINOR_NUM.

Единый формат версии

Слепдующим важным шагом был ввод критерия основываясь на котором можно было бы смело заявить, что две компоненты согласованы между собой. Мы решили, что этим критерием будет версия. При этом версия сама по себе должна нести информацию основываясь на которой можно понять, что две компоненты согласованы.
Формат версии: MAJOR_NUM.MINOR_NUM.REVISION. Т.е. первая часть версии является так же номером тега. А мы знаем, что у нас все компоненты в теге согласованны. Указание сразу двух чисел в названии тега, позволяет делать нормальнй переход между мажорными версиями, т.е. вместо 1.1.x, 1.2.x, 1.3.x, 2.4.x, у нас будет 1.1.x, 1.2.x, 1.3.x, 2.1.x. Номер ревизии остается и используется как обычно. Это номер ревизии каждой из отдельных компонент, а не просто последняя ревизия тега.

Потом пришла автоматизация

Следующим шагом был процесс внедрения CI (Continuous Integration). Выбор пал на, думаю многим известную CI систему, Jenkins. Выбор был между Cruise control, с которым я имею не самый приятный опыт работы, Team City которым является платным для нормального его использования и непосредственно Jenkins, который бесплатен, широко распростренен и хорошо документирован. Фактор бесплатности был немаловажен, так как сташно просить у начальства деньги за что то, что не факт что приживется.

Я твой хозяин ты мой раб

Компоненты нашей системы весьма разнообразные и на текущий момент используют 3 платформы для сборки: windows, linux и mac os. Получается один мастер и 2 slave (не хочу я больше повторять слово “раб”). Вопрос где делать мастера не возникал, конечно же на Linux. Была развернута начальная конфигурация из одного мастера (Linux) и одного slave (Windows). Начался процесс автоматизации сборки всех наших компонент. В ходе работы, уже появился тег 1.1 и число задач в Jenkins перевалило за 10. И тут начались проблемы. В один прекрасный день Jenkins упал. Мы его подняли, он у пал снова. И еще раз. И продолжал падать, независимо от того, как мы его запускаем через Tomcat или как службу. Независимо от JAVA машины, open JDK или SUN java. Независимо от текущей версии. Вернее падал не сам Jenkins, а JAVA машина которую он рушил. Я даже завел отдельный баг в их багтрекете (JENKINS-16199, правда на это все и заглохло). Потратив полторы недели на всякие попытки понять что происходит или хотябы почему, было принято решение перенести мастера на Windows систему. И о чудо, в той же самой конфигурации все стало работать стабильно. Со времене число задач возросло в два раза, но все продолжает работат. В итоге конечная структура выглядит так:
Мастер на Windows, два Slave, на Linux и MacOS, взаимодействие по SSH через побличные ключи.

Об имплантантах

Стандартную поставку Jenkins мы расширили следующими плагинами:
Copy Artifact Plugin — Используем для того что бы артифакт одной сборки использовать другой. Тут есть один просто момент, на котором можно споткнуться.
Если вы в одной из задача сохраняете артифакт вот так
О структуризации и автоматизации
А в другой, используя данный плагин, загружаете этот арт
О структуризации и автоматизации
То вы как минимум должны указать путь до него так же как в первом случае, а еще не помешает установить опцию “Flatten directories” что бы отбросить лишние пути и скопировать только сам арт в то место куда вам нужно.
Jenkins description setter plugin — Позволяет указывать некоторую информацию о каждой сборке в поле Build Description. Те задачи которые имеют версию, выводят ее туда.
Extra Columns Plugin — позволяет добавить поле Build Description на View.
О структуризации и автоматизации
Publish Over SSH — Отличный плагин для заливки файлов и выполнения команд на удаленной машине. Правда пока его не используем в силу того, что автотестирование еще не налажено. Недостатком данного плагина является то, что требует предварительная конфигурации всех SSH соединений и в каждой задаче явно указывается кукое соединение использовать на этапе ее создания.
Python Plugin — часть шагов сборки написано на Python, потому что так проще. В том числе есть отдельная задача которая делает почти все тоже самое что Publish Over SSH но только позволяя на лету конфигурировать параметры доступа к удаленной машине.
XCode integration — Используется для сборки проекта на MacOS. Нам пришлось попотеть для того, что бы уметь делать сборки как для разных версий iOS. В итогде на билд машине было разрешено использовать комманду sudo xcode-select -switch без ввода пароля.

Особая, черная, магия

Я уже упоминал необходисость удаленной заливки и выполнения команд, которую почти прекрасно рещает плагин Publish Over SSH. Но который не позволяет непосредственно для каждого процесса сборки указать нужный таргет.
У нас есть свой инсталятор, несколько тестировшиков и несколько машин. Многим лень тащить этот файл себе и чтото делать с ним что бы установить продукт. Для этих целий была создана сцепиальна задача “Remote deploy”.
И так, что она умеет:
Забрать инсталятор из тега который укажет пользователь
Выполнить его установку на той машине которую укажет пользователь
Вот как это выглядит
О структуризации и автоматизации
Данная задача является параметризированной.
Параметры
TAG, HOST, USER, DEPLOY_DIR, OPENSSL_DIR, тут никакой магии, обычные строки.
JOBS_NUMBER — “Build selector for Copy Atrifact”, позволяет указать какую версию сборки брать, последнюю удачную или какотой определенный номер или что то еще.
PASSWORD это параметр типа “Password Parameter” — закрывает пароль звездочками.
Вот таким образом забирется нужная сборка из нужной задачи
О структуризации и автоматизации

Настройка Вида

Виды в Jenkins это средство кастомизации отображения проектов. Мы убрали одну колонку и добавили “Build Description” для наших видов Trunk, Tag_X. Мы хотели что бы на дефолтном виде Jenkins было так же. Но Jenkins не позволяет делать такие манипуляции с его главным видом. Вместо этого можно создать свой вид (Default), добавить в него все проекты по маске .* и сделать этот вид, видом по умолчанию.

Стреляем в холостую

У нас есть специальный скрипт для создание версии и всех ее возможных вариаций. В начале он появлялся в каждом из проектов в которм нужен был подобный функционал. Спустя время, мы его положили в одном месте и везде где он нужен, тянем его как внешнюю зависимость. И тогда случился Epic fail, когда его случайно поправили в одном из проектов, где он был зависимостью. В Jenkins выстроилось в очередь 15 проектов на пересборку, холостую пересборку.
Первое что пришло в голову, это заблокировать файл для изменений. На файл повесили свойство needs-lock и от специального build юзера заблокировали его (svn lock).
Но есть решение лучше. Горазду лучше. Jenkins позволяет ввести фильтрацию перед запуском сборки иницированной изменением репозитория. Это находится в разделе “Управление исходным кодом -> Расширенные”. Нас инетерсует параметр “Excluded Regions” в котором можно указать изменения в каких файлах и директориях стоит игнорировать.

В место заключения

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

Спасибо за внимание.

Автор: Cupper

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js