В течение многих лет меня учили, что ломать непрерывную интеграционную сборку — это то, чего следует избегать при любых обстоятельствах. Позвольте мне сначала процитировать несколько классиков. Дядя Боб в «Чистом кодере» говорит:
Команда должна постоянно поддерживать сборку. Если сборка не удалась, это должно быть событие «останов прессов», и группа должна встретиться, чтобы быстро решить проблему.
и позже в этом разделе:
У меня есть все разработчики, которые запускают скрипт непрерывной сборки перед тем, как их зафиксировать.
Окончательная цитата:
Они (тесты CI) никогда не должны терпеть неудачу. Если они терпят неудачу, тогда вся команда должна прекратить то, что они делают, и сосредоточиться на том, чтобы снова пройти пройденные тесты. Сломанная сборка […] должна рассматриваться как аварийная ситуация […]
В другой замечательной книге « Непрерывная доставка » Джеза Хамбла и Дэвида Фарли авторы идут дальше. Они представляют план из 7 пунктов, которому мы должны следовать при каждом коммите (!):
3.
Запустите скрипт сборки и тесты на своем компьютере разработчика, чтобы убедиться, что на вашем компьютере все по-прежнему работает правильно[…]
5.
Подождите, пока инструмент CI запустит сборку с вашими изменениями.[…]
7.
Если сборка прошла успешно, порадуйтесь и переходите к следующему заданию.[…]
Если фиксация выполнена успешно, разработчики могут и только тогда могут перейти к следующей задаче.
Они предлагают следующие лучшие практики:
Никогда не возвращайся домой на сломанной сборке
[…] Когда сборка прерывается при регистрации, попытайтесь исправить это в течение десяти минут.
Подводя итог, что литература говорит о непрерывной интеграции:
- перед выполнением всегда запускайте все тесты, чтобы убедиться, что все отображается зеленым
- внимательно посмотрите на свои сборки CI, чтобы убедиться, что они проходят, не переходите к дальнейшим задачам
- если вы нарушаете сборку, вы должны рассматривать это как чрезвычайную ситуацию и исправить это как можно скорее
- у вас очень мало времени на исправление, иначе ваши изменения будут отменены
Нередко бывает, что некоторые аппаратные тревоги запускаются, когда сборка становится красной. Я слышал о командах, в которых разработчикам, ломающим сборку, приходилось носить какую-то глупую шляпу или жертвовать доллар на благотворительность (ничего страшного в этом одном). Нарушение сборки считается грехом, убийством на продуктивность команды, небрежность и лень. CI-сервер становится ужасной, страшной машиной, которой боятся разработчики. Бамбук даже дает каждому разработчику очки, основанные на общем количестве сломанных и фиксированных сборок.
Я полностью понимаю эту точку зрения и поведение, но это не значит, что я согласен. Я чувствую, что этот рабочий процесс просто неправильно . Я знаю, что вся команда работает над одним и тем же HEAD / транком в управлении версиями, так что ломать это, возможно, является пробкой для всех из них. Но я против того, чтобы рассматривать CI / контроль источников как некоторый дефицитный ресурс, который настолько критичен.
Сервер непрерывной интеграции и VCS должны быть вашим личным товарищем по команде, делающим работу для меня. Никто на самом деле не платит мне за то, что я смотрю, скажем, на 5 минут в моей IDE перед каждым коммитом, чтобы убедиться, что все тесты все еще проходят. Если это так, я полагаю, что CI будет вслепую в течение следующих 5 минут. Если я сломал сборку, они ожидали, что я бросил все и просто запрыгнул, пытаясь исправить сборку в течение 10 минут, как предполагает Непрерывная доставка . Все это в экстренной, стрессовой манере. Почему?
Еще во времена Java 1.4 нас учили, что параллельное программирование с использованием wait()
и notify()
сложно. Но вместо того, чтобы отказаться от параллелизма, мы придумали более удобные и простые в использовании абстракции и библиотеки. В то же время мы не хотели переименовывать классы, так как это также переименовывало файлы .java
, операция не очень хорошо поддерживалась в CVS. Но вместо того, чтобы сохранить старые имена навсегда, мы перешли на более качественный SVN. Теперь, из-за технических ограничений серверов непрерывной интеграции и VCS, мы должны рассматривать CI-сервер как очень дорогое über-утверждение, которое никогда не должно потерпеть неудачу. Технологические недостатки, похоже, влияют на нашу производительность и рабочий процесс.
Я хочу сломать сборку, когда захочу! Когда я закончу и мои новые тесты пройдут, я просто захочу зафиксировать / протолкнуть свой новый материал и позволить серверу CI выполнить полное тестирование. Я надеюсь, что все летит, но если нет, я не хочу чувствовать себя виноватым. Я не хочу задерживаться или извиняться за своих товарищей по команде. Я исправлю эти неожиданные проблемы, когда смогу. Это не производство, это просто моя экспериментальная ошибка новой функции, которая никого не волнует.
Это, естественно, приводит к представлению об элементных ветвях . Концепция проста: вы разрабатываете свою функцию в отдельной ветке, сервер CI может даже построить все ваши изменения, и когда вы почувствуете, что готовы, вы просто объединяете свои изменения с основной веткой. Проблема с функциональными ветвями состоит в том, что это больше не непрерывная интеграция . После нескольких дней разработки ваша функция может быть зеленой и готова в одиночку, но объединить ее обратно может быть крайне проблематично. Также другие члены команды могут получить пользу от ваших изменений, даже если они не завершены (но уже выделены зеленым цветом ). Все эти наблюдения привели меня к следующим требованиям:
- Я хочу выдвигать свои изменения так часто, как мне нужно
- CI-сервер должен строить мои изменения изолированно, чтобы в случае их поломки никто их не видел и не заботился
- если мои изменения хороши, я хочу, чтобы они были автоматически и сразу видны другим
- Я также хочу увидеть изменения, сделанные другими, как можно скорее
К счастью, современные CI-серверы (я использую Bamboo в качестве эталона) и системы контроля версий (git здесь, Mercurial должен работать точно так же) способны поддерживать рабочий процесс, о котором я мечтал. Основным требованием является то, что я хочу перенести свои изменения как можно быстрее, не выполняя все тесты и не нарушая master
. Первый шаг — создать отдельную ветку и зафиксировать эту ветку. Мы никогда не должны брать на себя обязательство master
. Когда я думаю, что я готов к своей новой функции, я просто нажимаю на эту ветку и продолжаю. Нет локальных тестов, нет нервного мониторинга CI-сервера. Просто подтолкнуть и подойти к новым вызовам. Это может привести к значительной экономии времени, если ваш набор тестов займет несколько минут.
Перво-наперво, вот как вы настроили Bamboo. В разделе « Конфигурация и филиалы плана» выберите следующие выделенные параметры:
Автоматически управлять филиалами будет обнаруживать и строить все новые филиалы автоматически. Включение веток позволяет Bamboo автоматически так или иначе объединять новые ветки с master
. В конфигурации Gatekeeper мы отмечаем опцию Push on . Вот как это работает: я делаю несколько коммитов в своей ветке. Вы можете нажать их сразу или через некоторое время:
1
2
3
4
5
|
* 894217c (HEAD, feature, origin /feature ) More tests * 3883a5c Starting to work on a feature * 1bd4e34 (origin /master , master) Multiplication test * f5c886c Testing addition * 3e6ab7c Initial revision |
Обратите внимание, что моя ветвь feature
размещена поверх master
. master
ветвь все еще зеленая, и мои, возможно, ломающиеся, изменения изолированы. Здесь приходит волшебство. Я настроил Bamboo для обнаружения новых веток и их автоматического построения:
Что такого особенного в этом? Бамбук говорит мне, что мои изменения в порядке, поэтому я могу свободно интегрировать их в основную линию ( master
ветвь). Я? Нет, Бамбук уже сделал это! Он построил мои изменения, нашел их зелеными и автоматически объединил их в `master, чтобы другие могли их видеть. Слияние было простым, это просто перемотка вперед:
1
2
3
4
5
|
* 894217c (HEAD, origin /master , origin /feature , master, feature) More tests * 3883a5c Starting to work on a feature * 1bd4e34 Multiplication test * f5c886c Testing addition * 3e6ab7c Initial revision |
Хорошо, это было действительно так интересно? В конце концов, мы получили бы тот же результат, просто нажав непосредственно на master
… Хорошо, но что произойдет, если мы будем вносить критические изменения в неизмененную магистраль? Это ужасающий момент в большинстве команд. Я просто нажал на серьезные изменения, и все кричат на меня. Фикс. Быстро. YU НЕТ ПРОВЕРИТЬ ТЕСТ? Но не в этом подходе:
1
2
3
4
5
6
7
|
* cc4ea63 (HEAD, origin /feature , feature) Experimental changes * f0a1a95 Side effects * 894217c (origin /master , master) More tests * 3883a5c Starting to work on a feature * 1bd4e34 Multiplication test * f5c886c Testing addition * 3e6ab7c Initial revision |
Вот где вся эта боль начинает окупаться: ветвь feature
может быть нарушена, но master
не тронут. Слияния не произошло. Только моя собственная частная ветка сломана. Другие разработчики не пострадали. Если бы эта сборка была зеленой , мои экспериментальные изменения были бы автоматически объединены с master
и отправлены. Но я ошибся, и они остаются скрытыми. Никого не волнует, мои товарищи по команде до сих пор видят стабильного master
. Я могу пойти на обед или починить его завтра. Нет стресса, нет давления со стороны сверстников. Когда я все сделаю правильно, Bamboo автоматически применит мое исправление.
Все это было очень легко, поскольку Bamboo мог использовать быструю перемотку вперед вместо обычного слияния. Но что, если мы попытаемся внести хорошие изменения в модифицированный удаленный origin/master
? Предположим, мы работаем над нашей функцией, но в то же время другой разработчик внес некоторые изменения в ветку с исправлениями ошибок, которые оказались правильными, поэтому Bamboo решил немедленно объединить их:
01
02
03
04
05
06
07
08
09
10
11
12
|
* d600da5 (HEAD, feature) Cosmetics | * 25612d7 (origin /master , origin /bugfix , master, bugfix) Documentation | * 733b6a9 Urgent bug fix |/ * 4f258a6 (origin /feature ) Fixing test failure * cc4ea63 Experimental changes * f0a1a95 Side effects * 894217c More tests * 3883a5c Starting to work on a feature * 1bd4e34 Multiplication test * f5c886c Testing addition * 3e6ab7c Initial revision |
Как вы можете видеть, наша ветвь feature
еще не была перенесена в основной репозиторий, а ветка bugfix
уже интегрирована. Как Bamboo справится с выдвигаемой веткой? Поведение немного более сложное, но все же управляемое: Bamboo сначала проверяет master
(включая уже bugfix
ветку bugfix
) и пытается объединить изменения из feature
ветви. Если слияние прошло успешно (без конфликтов), выполняется обычная сборка. Если сборка прошла успешно, результаты слияния передаются master
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
* e58a2db (origin /master ) [bamboo] Automated branch merge |\ | * d600da5 (HEAD, origin /feature , feature) Cosmetics * | 25612d7 (origin /bugfix , master, bugfix) Documentation * | 733b6a9 Urgent bug fix |/ * 4f258a6 Fixing test failure * cc4ea63 Experimental changes * f0a1a95 Side effects * 894217c More tests * 3883a5c Starting to work on a feature * 1bd4e34 Multiplication test * f5c886c Testing addition * 3e6ab7c Initial revision |
Обратите внимание на коммит « [bamboo] Automated branch merge », неявно сделанный Bamboo. Как вы можете видеть, этот коммит объединяет все мои изменения в ветке feature
в master
ветку. Этот подход работает, но имеет несколько недостатков:
- через некоторое время ваша
master
ветвь истории может состоять только из сгенерированных бамбуком коммитов. Я бы предпочел увидеть обычные коммиты там - автоматическое объединение в Bamboo может завершиться неудачей
- моя ветвь
feature
прежнему не содержитbugfix
веток сbugfix
которые уже объединены с основной веткой
По указанным выше причинам лучше сначала слить локальную ветку feature
с master
и нажать ее. В этом сценарии вы почти гарантированы, что удаленное слияние на Bamboo никогда не завершится неудачей (только ускоренная перемотка вперед), это предсказуемо, и вы работаете с последним master
состоянием. И кстати, интересно, что произойдет, если автоматическое слияние на Bamboo не удастся?
Резюме
Такой подход к работе с управлением версиями объединяет в себе лучшее из обоих миров: функции и непрерывную интеграцию. Поскольку каждый разработчик работает над отдельной ветвью (или даже репозиторием!), Нарушенная фиксация никогда не переходит в master
ветку / основную ветку. С другой стороны, автоматическое объединение гарантирует, что наша ветвь функций будет всегда актуальной, и мы не столкнемся с проблемами при попытке объединить трудоемкие дни. Более того, хорошие коммиты сразу видны другим, а плохие остаются скрытыми.
Справка: взлом сборки не является преступлением от нашего партнера JCG Томаша Нуркевича из блога Java и соседей .