Вы изучили команды Git но хотите представлять, как непрерывная интеграция (Continuous Integration, CI) происходит в реальности? Или может вы хотите оптимизировать свои ежедневные действия? Этот курс даст вам практические навыки непрерывной интеграции с использованием репозитория на GitHub. Данный курс не задуман как некий визард, который можно просто прокликать, напротив, вы будете совершать те же действия, что люди на самом деле делают на работе, тем же способом, которым они это делают. Я буду объяснять теорию по мере прохождения вами имеющих к ней отношение шагов.
Что мы будем делать?
По мере продвижения, мы будем постепенно создавать список типовых шагов CI, что есть отличный способ запомнить этот список. Другими словами, мы будем создавать список действий, которые разработчики совершают, осуществляя непрерывную интеграцию, осуществляя непрерывную интеграцию. Также мы будем использовать простой набор тестов чтобы приблизить наш процесс CI к реальному.
Этот GIF схематично показывает коммиты в вашем репозитории по мере продвижения по курсу. Как видите, тут нет ничего сложного и только самое необходимое.
Вы пройдёте такие стандартные для CI сценарии:
- Работа над фичей;
- Применение автотестов для обеспечения качества;
- Реализация приоритетной задачи;
- Разрешение конфликта при слиянии ветвей (merge conflict);
- Возникновение ошибки в продуктивной среде.
Что вы узнаете?
Вы сможете ответить на такие вопросы:
- Что такое непрерывная интеграция(CI)?
- Какие типы автотестов используются при CI, и в ответ на какие действия они запускаются?
- Что такое pull request и когда они нужны?
- Что такое разработка через тестирование (Test Driven Development, TDD) и как она соотносится с CI?
- Осуществлять слияние(merge) или применять изменения поверх(rebase)?
- Откатывать или чинить в следующей версии?
Я вначале везде переводил штуки типа "pull request", но в результате решил вернуть фразы на английском в некоторых местах чтобы снизить градус безумия в тексте. Я порой буду использовать "программистский суржик" вроде чудного глагола "закоммитить" там, где люди на самом деле на работе его используют.
Что такое непрерывная интеграция?
Непрерывная интеграция, или CI, это техническая практика, которая заключается в том, что каждый член команды интегрирует свой код в общий репозиторий хотя бы раз в день, причём результирующий код при этом должен хотя бы собираться без ошибок.
Существуют разночтения по поводу этого термина
Предметом спора является частота интеграции. Некоторые утверждают, что объединять код лишь раз в день недостаточно на самом деле интегрироваться непрерывно. В пример приводится команда, где все берут свежий код утром и интегрируются один раз вечером. Хотя это и разумное возражение, всё же в целом считается, что вариант определения "раз в день" достаточно практичен, конкретен, и подходит для команд разных размеров.
Другое возражение состоит в том, что C++ давно не единственный язык, используемый при разработке, и простое требование сборки без ошибок, как способ валидации, слабовато. Некий набор тестов (например, модульных(unit), выполняемых локально) также должен завершиться успешно. В данный момент, сообщество тяготеет к тому, чтобы такое требование было обязательным, и, в будущем "сборка + модульные тесты", видимо, станет общепринятым, если это уже не произошло.
Непрерывная интеграция отличается от непрерывной поставки (Continuous Delivery, CD) тем, что не требует релиз-кандидата после каждого цикла интеграции.
Список шагов, который мы будем использовать на протяжении курса
- Pull in the latest code. Create a branch from
master
. Start working. - Create commits on your new branch. Build and test locally. Pass? Go to the next step. Fail? Fix errors or tests and try again.
- Push to your remote repository or remote branch.
- Create a pull request. Discuss the changes, add more commits as discussion continues. Make tests pass on the feature branch.
- Merge/rebase commits from master. Make tests pass on the merge result.
- Deploy from the feature branch to production.
- If everything is good in production for some period of time, merge changes to master.
? Подготовка
Убедитесь в наличии нужного ПО
Чтобы пройти этот курс вам понадобится Node.js и Git-клиент.
Можете использовать любой Git-клиент, но я буду приводить команды только для командной строки.
Убедитесь, что у вас установлен Git-клиент, поддерживающий командную строку
Если у вас пока не установлен Git-клиент, поддерживающий командную строку, можете найти инструкции по установке здесь.
Подготовьте репозиторий
Вам потребуется создать личную копию(fork) репозитория-шаблона с кодом для курса на GitHub. Давайте договоримся называть эту личную копию репозиторием курса.
Сделали? Если вы не меняли настройки по умолчанию, ваш репозиторий курса скорее всего называется continuous-integration-team-scenarios-students
, он находится в вашем аккаунте на GitHub, и URL выглядит так
https://github.com/<ваше имя ползователя на GitHub>/continuous-integration-team-scenarios-students
Я буду называть этот адрес просто <URL репозитория>
.
Угловые скобки как <тут>
будут означать, что вы должны заменить такое выражение на соответствующее значение.
Убедитесь что GitHub actions включены для данного репозитория курса. Если они не будут включены, пожалуйста, включите их, нажав на большую кнопку в середине страницы, на которую вы можете попасть, кликнув Actions в интерфейсе GitHub.
У вас не получится пройти курс, следую моим инструкциям, если GitHub Actions не будут включены.
Вы всегда можете использовать способность GitHub отображать Markdown чтобы увидеть текущее состояние списка, который мы сочиняем, тут
https://github.com/<your GitHub user name>/continuous-integration-team-scenarios-students/blob/master/ci.md
Про ответы
Хотя лучший способ завершить этот курс — это делать всё своими руками, у вас могут и возникнуть сложности.
Если вы чувствуете, что не понимаете что делать и не можете продолжать, можете подсмотреть в ветку solution
, которая есть в вашем стартовом репозитории.
Пожалуйста, не осуществляйте слияние solution
в master
во время курса. Вы можете использовать эту ветку чтобы разобраться что делать, или чтобы сравнивать свой код с авторским, используя все возможности, которые даёт нам Git. Если вы совсем потерялись, можете полностью заменить свою ветку master
на ветку solution
и затем сбросить свою рабочую директорию до того шага курса, который вам нужен.
Используйте это только если вам это действительно нужно
Закоммитьте(commit) свой код
git add .
git commit -m "Backing up my work"
Эти команды
- переименовывают
master
вmaster-backup
; - переименовывают
solution
вmaster
; - переключают(checkout) на новую ветку
master
и переписывают содержимое рабочей директории; - создают ветку "solution" из "master" (которая раньше была "solution") на случай если вам в будущем понадобится ветка "solution".
git branch -m master master-backup
git branch -m solution master
git checkout master -f
git branch solution
После этих действий вы можете использовать git log master
чтобы выяснить, какой коммит вам нужен.
Вы можете сбросить свою рабочую директорию на этот коммит так:
git reset --hard <the SHA you need>
Если вы довольны результатом, в какой-то момент вам понадобится опубликовать вашу версий репозитория в удалённый репозиторий(remote). Не забудьте явно указать удалённую ветку когда будете это делать.
git push --force origin master
Пожалуйста обратите внимание, что мы используем git push --force
. Вряд ли вы часто захотите так поступать, но у нас тут крайне специфичный сценарий с одним пользователем репозитория, который вдобавок, понимает, что он делает.
Starting working
Начнем составлять наш список шагов CI. Обычно вы начинаете этот шаг с извлечения последней версии кода из удаленного репозитория, но у нас еще нет локального репозитория, поэтому вместо этого мы клонируем его из удалённого.
? Задание: обновите локальный репозиторий, создайте ветку из master
, начните работать
- Клонируйте репозиторий курса из
<URL репозитория>
. - Запустите
npm install
в каталоге репозитория курса; он нужен нам для установки Jest, который мы используем для запуска тестов. - Создайте ветку и назовите ее
feature
. Переключитесь на эту ветку. Добавьте тестовый код в
ci.test.js
между комментариями с просьбой сделать это.
it('1. pull latest code', () => { expect(/.*pull.*/ig.test(fileContents)).toBe(true); }); it('2. add commits', () => { expect(/.*commit.*/ig.test(fileContents)).toBe(true); }); it('3. push to the remote branch with the same name', () => { expect(/.*push.*/ig.test(fileContents)).toBe(true); }); it('4. create a pull request and continue working', () => { expect(/.*pull\s+request.*/ig.test(fileContents)).toBe(true); });
- Добавьте текст с первыми 4 шагами в файл
ci.md
.
1. Pull in the latest code. Create a branch from `master`. Start working. 2. Create commits on your new branch. Build and test locally. Pass? Go to the next step. Fail? Fix errors or tests and try again. 3. Push to your remote repository or remote branch. 4. Create a pull request. Discuss the changes, add more commits as discussion continues. Make tests pass on the feature branch.
Команды
# Клонируйте репозиторий курса
git clone <repository URL>
cd <repository name>
# Выполните npm install в каталоге репозитория курса; он установит Jest, который мы используем для запуска тестов.
npm install
# Создайте ветку и назовите ее feature. Переключитесь на эту в ветку.
git checkout -b feature
# Отредактируйте ci.test.js как описано выше.
# Отредактируйте ci.md как описано выше
Создавайте коммиты в новой ветке, осуществляйте сборку и тестирование локально
Мы собираемся настроить тесты чтобы они запускались перед коммитом, а затем закоммитить код.
Типовые сценарии, когда тесты запускаются автоматически
- Локально:
- Постоянно или в ответ на соответствующие изменения кода;
- При сохранении (для интерпретируемых или JIT-компилируемых языков);
- При сборке (когда требуется компиляция);
- При коммите;
- При публикации в общий репозиторий.
- На сервере сборки(build server) или в среде сборки:
- Когда код публикуется в персональную ветку / репозиторий.
- Тестируется код в этой ветке.
- Тестируется потенциальный результат слияния (обычно с
master
). - В качестве этапа непрерывной интеграции/конвейера непрерывной поставки
Как правило, чем быстрее выполняется набор тестов, тем чаще вы можете позволить себе его запускать. Типичное распределение по этапам может выглядеть так.
- Быстрые модульные тесты — при сборке, в конвейере CI
- Медленные модульные тесты, быстрые компонентные и интеграционные тесты — при коммите, в конвейере CI
- Медленные компонентные и интеграционные тесты — в конвейере CI
- Тестирование безопасности, нагрузочное и другие длительные или дорогостоящие тесты — в конвейерах CI/CD, но только в определенных режимах/этапах/конвейерах сборки, например, при подготовке релиз-кандидата или при запуске вручную.
? Задание
Я предлагаю сначала запустить тесты вручную, используя команду npm test
. После этого давайте добавим git hook для запуска наших тестов при коммите. Есть одна загвоздка: Git hooks не считаются частью репозитория и поэтому не могут быть клонированы из GitHub вместе с остальными материалами курса. Чтобы установить hook, вам нужно запустить install_hook.sh
или скопировать файл repo/hooks/pre-commit
в локальный каталог .git/hooks/
.
При коммите вы увидите, что выполняются тесты, и они проверяют, присутствуют ли в списке определенные ключевые слова.
- Запустите тесты вручную, выполнив команду
npm test
в папке репозитория вашего курса. Убедитесь, что тесты были выполнены. - Установите hook на коммит (pre-commit hook), запустив
install_hook.sh
. - Закоммитьте изменения в локальный репозиторий.
- Убедитесь, что тесты выполняются перед коммитом.
Ваш репозиторий должен выглядеть так после выполнения этих действий.
Команды
# Установите pre-commit hook выполнив install_hook.sh.
# Закоммитьте изменения в локальный репозиторий. Используйте "Add first CI steps" в качестве сообщения при коммите.
git add ci.md ci.test.js
git commit -m "Add first CI steps"
# Убедитесь, что тесты запускаются перед коммитом.
Опубликуйте код в удаленный репозиторий или удаленную ветку
Завершив работать локально, разработчики обычно делают свой код общедоступным, чтобы его можно было в конечном итоге интегрировать с общим. С помощью GitHub это обычно достигается публикацией работы либо в персональной копии репозитория(personal fork, форк), либо в личной ветке.
- При использовании форков разработчик клонирует удаленный общий репозиторий, создавая его личную удаленную копию, также известную как форк. После этого он клонирует этот личной репозиторий, чтобы работать с ним локально. Когда работа завершена и коммиты созданы, он помещает их в свой форк, где они доступны другим и могут быть интегрированы в общий репозиторий. Этот подход обычно используется в проектах с открытым исходным кодом на GitHub. Он также используется в моем расширенном курсе [Team Work and CI with Git] (http://devops.redpill.solutions/).
- Другой подход состоит в том, чтобы использовать только один удалённый репозиторий и считать только ветку
master
совместно используемого репозитория "защищенной". В этом сценарии отдельные разработчики публикуют свой код в ветки удаленного репозитория, чтобы другие могли этот код посмотреть, если все в порядке, объединить сmaster
общего репозитория.
В этом конкретном курсе мы будем использовать рабочий процесс, использующий ветки.
Давайте опубликуем наш код.
? Задание
- Опубликуйте изменения в удаленную ветку с тем же именем, что и ваша рабочая ветка
Команды
git push --set-upstream origin feature
Создайте pull request
Создайте pull request с названием Steps review. Установите feature
как "head branch" и master
как "base branch".
Убедитесь, что вы установили master
в своем форке репозитория в качестве "base branch", я не буду отвечать на запросы на изменения в репозиторий с материалами курса.
На сленге GitHub "base branch" — это ветка, на которой вы основываете свою работу, а "head branch" — это ветка, содержащая предлагаемые изменения.
Обсудите изменения, добавьте новые коммиты по мере продолжения обсуждения
Pull request(PR)
Pull request(PR) — это способ обсудить и задокументировать код, а также провести проверку(code review) кода. Pull request названы в честь общего способа интеграции отдельных изменений в общий код. Обычно человек клонирует удаленный официальный репозиторий проекта и работает над кодом локально. После этого он помещает код в свой личный удаленный репозиторий и просит ответственных за официальный репозиторий забрать(pull) его код в свои локальные репозитории, где они просматривают и, возможно, интегрируют(merge) его. Это понятие известно и под другими названиями, например, merge request.
На самом деле вам не обязательно использовать функцию pull request GitHub или аналогичных платформ. Команды разработчиков могут использовать другие способы связи, включая личное общение, голосовые звонки или электронную почту, но есть всё же ряд причин использовать такие pull requests в стиле обсуждения на форуме. Вот некоторые из них:
- организованные обсуждения, связанные с конкретными изменениями в коде;
- как место для просмотра отзывов о незавершенной работе как от автотестов, так и от коллег;
- формализация проверок кода;
- чтобы позже можно было выяснить причины и соображения, стоящие за тем или иным фрагментом кода.
Обычно вы создаете pull request когда вам нужно что-то обсудить или получить обратную связь. Например, если вы работаете над функцией, которая может быть реализована несколькими способами, вы можете создать запрос на внесение изменений еще до написания первой строки кода, чтобы поделиться своими идеями и обсудить свои планы с соавторами. Если работа более проста, pull request открывается, когда что-то уже сделано, зафиксировано и может быть обсуждено. В некоторых сценариях вы можете открыть PR только из соображений контроля за качеством: чтобы запустить автоматические тесты или инициировать проверку кода. Что бы вы ни решили, не забывайте @упоминать людей, одобрение которых требуется в вашем pull request'е.
Обычно при создании PR вы делаете следующее.
- Указываете, что вы предлагаете изменить и где.
- Пишете описание, объясняющее цель изменений. Вы можете захотеть:
- добавить что-нибудь важное, что не является очевидным из кода, или что-нибудь полезное для понимания контекста, например соответствующие #баги и номера коммитов;
- @упомянуть всех, с кем хотите начать работать вместе, или вы можете @упомянуть их в комментариях позже;
- попросить коллег помочь с чем-то или проверить что-либо конкретное.
После того как вы откроете PR выполняются тесты, настроенные для запуска в таких случаях. В нашем случае это будет тот же набор тестов, который мы запускали локально, но в реальном проекте могут быть дополнительные тесты и проверки.
Пожалуйста, подождите пока завершатся тесты. Вы можете увидеть статус тестов в нижней части обсуждения PR в интерфейсе GitHub. Продолжайте когда тесты будут завершены.
? Добавьте замечание о произвольности списка шагов CI
Список, использованный в этом курсе, является произвольным и субъективным, мы должны добавить примечание об этом.
? Задание: создание pull request для данного примечения
- Переключитесь на ветку
master
. - Создайте ветку с именем
bugfix
. - Добавьте текст примечания в конец файла
ci.md
.
> **GitHub flow** is sometimes used as a nickname to refer to a flavor of trunk-based development when code is deployed straight from feature branches. This list is just an interpretation that I use in my [DevOps courses](http://redpill.solutions). The official tutorial is [here](https://guides.github.com/introduction/flow/).
- Закоммитьте изменения.
- Опубликуйте ветку
bugfix
в удалённый репозиторий. - Создайте pull request с именем Adding a remark с головной веткой
bugfix
и базовой веткойmaster
.
Убедитесь, что вы установили master
в своем форке репозитория в качестве "base branch", я не буду отвечать на запросы на изменения в репозиторий с материалами курса.
Вот как должен выглядеть ваш репозиторий.
Команды
# Переключитесь на ветку master. Создайте ветку bugfix.
git checkout master
# Создайте ветку bugfix-remark.
git checkout -b bugfix
# Добавьте текст примечания внизу ci.md.
# Закоммитьте изменения
git add ci.md
git commit -m "Add a remark about the list being opinionated"
# Опубликуйте ветку bugfix в удалённый репозиторий.
git push --set-upstream origin bugfix
# Создайте pull request при помощи интерфейса GitHub как описано выше
Утвердите pull request "Adding a remark"
? Задание
- Создайте pull request.
- Нажмите "Merge pull request".
- Нажмите "Confirm merge".
- Нажмите "Delete branch", она нам больше не нужна.
Это диаграмма коммитов после слияния.
? Продолжайте работать и добавлять тесты
Совместная работа над pull request часто приводит к необходимости дополнительной работы. Обычно это результат проверки кода или обсуждения, но в нашем курсе мы собираемся смоделировать это, добавив новые элементы в наш список шагов CI.
При непрерывной интеграции обычно применяется некоторое тестовое покрытие. Требования к покрытию тестами различаются и обычно находятся в документе с названием вроде "руководство для авторов"(contribution guidelines). Мы поступим просто и добавим по тесту для каждой строки в нашем контрольном списке.
При выполнении заданий сначала попробуйте закоммитить тесты. Если вы правильно установили pre-commit
hook ранее, только что добавленный тест будет запущен, не будет пройден, и ничего не будет закоммичено. Обратите внимание: так мы узнаем, что наши тесты действительно что-то проверяют. Любопытно, что если бы мы начали с кода до тестов, прохождение тестов могло означать либо то, что код работает так, как ожидалось, либо что тесты на самом деле ничего не проверяют. Кроме того, если бы мы не написали тесты в первую очередь, мы могли бы вовсе их забыть, поскольку ничто не напоминало бы нам об этом.
Разработка через тестирование (TDD)
TDD рекомендует писать тесты перед кодом. Обычный процесс работы с использованием TDD выглядит так.
- Добавьте тест.
- Запустите все тесты и убедитесь, что новый тест не проходит успешно.
- Напишите код.
- Запустите тесты, убедитесь, что все тесты проходят успешно.
- Проведите рефакторинг кода.
- Повторите.
Поскольку результаты работы тестов, которые не были пройдены успешно, обычно отображается красным, а выполненные успешно — зеленым, цикл также известен как "красный-зеленый-рефакторинг"(red-green-refactor).
? Задание
Сначала попробуйте закоммитить тесты и дать им завершиться неуспешно, затем добавьте и закоммитьте сам текст списка шагов CI. Вы увидите, что тесты проходят ("зеленые").
Затем опубликуйте новый код в удалённый репозиторий и посмотрите, как выполняются тесты в интерфейсе GitHub в нижней части обсуждения запроса на внесение изменений, и обновляется статус PR.
- Переключитесь на ветку
feature
. Добавьте эти тесты в
ci.test.js
после последнего вызоваit (...);
.
it('5. Merge/rebase commits from master. Make tests pass on the merge result.', () => { expect(/.*merge.*commits.*tests\s+pass.*/ig.test(fileContents)).toBe(true); }); it('6. Deploy from the feature branch to production.', () => { expect(/.*Deploy.*to\s+production.*/ig.test(fileContents)).toBe(true); }); it('7. If everything is good in production for some period of time, merge changes to master.', () => { expect(/.*merge.*to\s+master.*/ig.test(fileContents)).toBe(true); });
- Попробуйте закоммитить тесты. Если
pre-commit
hook установлен, попытка коммита завершится ошибкой. - После добавьте этот текст в
ci.md
.
5. Merge/rebase commits from master. Make tests pass on the merge result. 6. Deploy from the feature branch with a sneaky bug to production. 7. If everything is good in production for some period of time, merge changes to master.
- Внесите и закоммитьте изменения локально.
- Опубликуйте изменения в ветку
feature
.
Теперь у вас должно получиться что-то вроде этого
Команды
# Переключительна ветку feature
git checkout feature
# Добавить тесты в ci.test.js как описано выше
# Добавьте в индекс ci.test.js чтобы позже закоммитить
git add ci.test.js
# Попытайтесь закоммитить тесты. Если pre-commit hook установлены, коммит не произойдёт.
git commit
# Теперь добавьте текст в ci.md как описано выше
# Внесите изменения и закоммитьте их
git add ci.md
git commit -m "Add the remaining CI steps"
# Опубликуйте изменения в ветку feature
git push
Конфликт слияния
Перейдите к запросу на внесение изменений Steps review.
Несмотря на то, что мы не сделали ничего плохого, и тесты для нашего кода прошли успешно, мы все еще не можем осуществить слияние ветки feature
и master
. Это потому, что другая ветка bugfix
была объединена с master
пока мы работали над этим PR.
Это создает ситуацию, когда удаленная ветка master
имеет более новую версию, чем та, на которой мы основывали ветку feature
. Из-за этого мы не можем просто перемотать HEAD master
до конца ветки feature
. В этой ситуации нам нужно либо осуществить слияние(merge), либо применить коммиты feature
поверх(rebase) master
. GitHub на самом деле может выполнять автоматическое слияние, если нет конфликтов. Увы, в нашей ситуации обе ветки имеют конкурирующие изменения в файле ci.md
. Эта ситуация известна как конфликт при слиянии(merge conflict), и нам нужно разрешить ее вручную.
Merge или rebase
Merge
- Создает дополнительный коммит слияния(merge commit) и сохраняет историю работы.
- Сохраняет исходные коммиты веток с исходными отметками времени и авторами.
- Сохраняет SHA коммитов и ссылки на них в обсуждениях запросов на изменения.
- Требует однократного разрешения конфликтов.
- Делает историю нелинейной.
- Историю может быть трудно читать из-за большого количества веток (напоминает кабель IDE).
- Усложняет автоматическую отладку, например, делает
git bisect
менее полезным — он только найдет коммит слияния.
Rebase
- Воспроизводит коммиты из текущей ветки поверх базовой один за другим.
- Формируются новые коммиты с новыми SHA, в результате чего коммиты в GitHub соотносятся с исходными pull requests, но не с соответствующими комментариями.
- Коммиты могут быть рекомбинированы и изменены в процессе или даже объединены в один.
- Может потребоваться разрешить несколько конфликтов.
- Позволяет поддерживать линейную историю.
- Историю может быть проще читать, если только она не является слишком длинной без разумных на то причин.
- Автоматическая отладка и устранение неполадок несколько проще: делает возможным
git bisect
, может сделать автоматические откаты более чёткими и предсказуемыми.
- Требуется публикации ветви с перенесёнными коммитами с флагом
--force
при использовании с запросами на внесение изменений.
Обычно команды соглашаются всегда использовать одну и ту же стратегию, когда им нужно объединять изменения. Это может быть "чистое" слияние или "чистый" применение коммитов поверх или что-то промежуточное, например, выполнение применение коммитов поверх интерактивном режиме(git rebase -i
) локально для веток, не опубликованных в общем репозитории, но слияние(merge) для "общедоступных" веток.
Здесь мы будем использовать слияние.
? Задание
- Убедитесь, что код в локальной ветке
master
обновлён из удалённого репозитория. - Переключитесь на ветку
feature
. - Инициируйте слияние с веткой
master
. Будет сообщено о конфликте слияния, связанном с конкурирующими изменениями вci.md
. - Разрешите конфликт так, чтобы в тексте остался и наш список шагов CI, и замечание о нем.
- Опубликуйте коммит слияния в удаленную ветку
feature
. - Проверьте статус pull request'а в пользовательском интерфейсе GitHub, дождитесь пока слияние не будет разрешено.
Команды
# Убедитесь, что код в локальное ветке `master` обновлён из удалённого репозитория.
git checkout master
git pull
# Переключитесь на ветку feature
git checkout feature
# Инициируйте слияние с веткой master
git merge master
# A merge conflict related to concurrent changes to ci.md will be reported
# => Auto-merging ci.md
# CONFLICT (content): Merge conflict in ci.md
# Automatic merge failed; fix conflicts and then commit the result.
# Разрешите конфликт так, чтобы и наш список шагов CI, и замечание о нем остались в тексте.
# отредактируйте ci.md чтоб он не содержал маркеров конфликта слияния
git add ci.md
git merge --continue
# при коммите можете оставить сообщение по умолчанию
# Опубликуйте коммит слияния в удаленную ветку feature.
git push
# Проверьте статус запроса на изменения в пользовательском интерфейсе GitHub, дождитесь пока слияние не будет разрешено.
Отличная работа!
Вы закончили работу со списком, и теперь вам нужно утвердить pull request в master
.
? Задание: Утвердите pull request "Steps review"
- Откройте pull request.
- Нажмите "Merge pull request".
- Нажмите "Confirm merge".
- Нажмите "Delete branch", так как она нам больше не нужна.
Это ваш репозиторий в данный момент
Ошибка на продуктиве
Говорят, что «тестирование можно использовать, чтобы показать наличие ошибок, но никогда чтобы показать их отсутствие». Несмотря на то, что у нас были тесты, и они не показали нам никаких ошибок, коварная ошибка прокралась в продакшн.
В подобном сценарии нам нужно позаботиться о:
- том, что развёрнуто на продуктиве;
- коде в ветке
master
с ошибкой, с которого разработчики могут начинать новую работу.
Откатывать или исправлять в следующей версии?
"Откатывание"(rolling back) — это развертывание заведомо исправной более ранней версии в продуктивную среду и отмена(revert) коммитов, содержащих ошибку. "Исправление в следующей версии"(fixing forward) — это добавление исправления в master
и развертывание новой версии как можно скорее. Поскольку API и схемы баз данных меняются по мере развертывания кода в производственной среде, при непрерывной поставке и наличии хорошего покрытия тестами, откатывание, как правило, намного сложнее и рискованнее, чем исправление в следующей версии.
Поскольку откатывание не несет в нашем случае никакого риска, мы пойдём этим путём, ведь это позволяет нам
- исправить ошибку на продуктиве как можно скорее;
- сделать код в
master
сразу пригодным для начала новой работы.
? Задание
- Переключитесь на ветку
master
локально. - Обновите локальный репозиторий из удалённого репозитория.
- Отмените коммит слияния PR Steps review в
master
. - Опубликовать изменения в удалённый репозиторий.
Это история репозитория с отмененным коммитом слияния
Команды
# Переключитесь на ветку master.
git checkout master
# Обновите локальный репозиторий из удалённого репозитория.
git pull
# Отмените коммит слияния PR Steps review в master.
# Мы отменяем коммит слияния, поэтому нам нужно выбрать ветку истории, которую мы захотим оставить
git show HEAD
# предположим, что коммит, который был последним в ветке master до слияния, был отображён предыдущей командой первым
git revert HEAD -m 1
# можете не менять сообщения коммитов
# Опубликуйте изменения в удалённый репозиторий
git push
? Самопроверка
Убедитесь, что ci.md
больше не содержит текста "sneaky bug" после отмены коммита слияния.
Исправить список шагов CI и вернуть его в master
Мы полностью отменили коммит слияния ветки feature
. Хорошая новость в том, что теперь у нас нет ошибки в master
. Плохая новость в том, что исчез и наш драгоценный список шагов непрерывной интеграции. Итак, в идеале, нам нужно применить исправление к коммитам из feature
и вернуть их в master
вместе с исправлением.
Мы можем подойти к задаче по-разному:
- отменить(revert) коммит, которая отменяет слияние
feature
сmaster
; - перенести коммиты из бывшей
feature
.
Разные команды разработчиков в данном случае используют разные подходы, мы же перенесём полезные коммиты в отдельную ветку и создадим отдельный pull request для этой новой ветки.
? Задание
- Создайте ветку под названием
feature-fix
и переключитесь на нее. Перенесите все коммиты из бывшей ветки
feature
в новую ветку. Разрешите конфликты слияния, которые возникли при переносе.
Добавьте регрессионный тест в
ci.test.js
:
it('does not contain the sneaky bug', () => { expect( /.*sneaky\s+bug.*/gi.test(fileContents)).toBe(false); });
- Запустите тесты локально, чтобы убедиться, что они не завершаются успешно.
- Удалите текст " with a sneaky bug" в
ci.md
. - Добавьте в индекс изменения тестов и изменения в списке шагов и закоммитьте их.
- Опубликуйте ветку в удалённый репозиторий.
У вас в результате должно получиться что-то похожее
Команды
# Создайте ветку под названием feature-fix и переключитесь на нее.
git checkout -b feature-fix
# Перенесите все коммиты из бывшей ветки feature в новую ветку. Разрешите конфликты слияния, которые возникли при переносе.
# используйте историю чтобы узнать хэши коммитов:
# - предшествующего коммиту с первой частью списка: C0
# - добавляющего последние элементы списка: C2
git log --oneline --graph
git cherry-pick C0..C2
# разрешите конфликты слияния
# - отредактируйте ci.md и/или ci.test.js
# - добавьте файлы в индекс
# - выполните "git cherry-pick --continue", можете не менять сообщение коммита
# Добавьте регрессионный тест в ci.test.js
# Запустите тесты локально, чтобы убедиться, что они не завершаются успешно.
# Удалите текст " with a sneaky bug" в ci.md.
# Добавьте в индекс изменения тестов и в списке шагов и закоммитьте их.
git add ci.md ci.test.js
git commit -m "Fix the bug in steps list"
# Опубликуйте ветку в удалённый репозиторий.
git push --set-upstream origin feature-fix
Создайте pull request.
Создайте pull request с названием Fixing the feature. Установите feature-fix
как "head branch", а master
как "base branch".
Пожалуйста, подождите, пока завершатся тесты. Вы можете увидеть статус тестов в нижней части обсуждения PR.
Убедитесь, что вы установили master
в своем форке репозитория в качестве "base branch", я не буду отвечать на запросы на изменения в репозиторий с материалами курса.
Утвердите pull request "Fixing the feature"
Спасибо за исправление! Пожалуйста, утвердите изменения в master
из pull request.
? Задание
- Нажмите "Merge pull request".
- Нажмите "Confirm merge".
- Нажмите "Delete branch", так как она нам больше не нужна.
Это то, что у вас должно быть в данный момент
Поздравляю!
Вы выполнили все действия, которые люди обычно совершают в процессе непрерывной интеграции.
Если вы заметили какие-либо проблемы с курсом или знаете как его улучшить, создайте issue в репозитории с материалами курса. У этого курса также есть интерактивная версия использующая GitHub Learning Lab в качестве платформы.
nin-jin
А где тут собственно непрерывная интеграция? Что с чем вы тут непрерывно интегрируете? Статья скорее про работу с гитом в фичеветках, чем про непрерывную интеграцию.
NickT Автор
Приведите Ваше определение непрерывной интеграции, обсудим.
nin-jin
Определение у вас в статье и так есть. А вот команда где-то потерялась. Один человек зачем-то жонглирует ветками в гите и всё.
NickT Автор
А какую реализацию курса вы предлагаете, конкретно? Мне правда интересно.
nin-jin
Два разработчика пилят параллельно фичи и постоянно подмёрживают их в мастер постоянно ломая друг другу сборку.
NickT Автор
В формате самоучителя? Чем это технически отличается от того, когда значимые сценарии воспроизводятся силами одного? Разве не лучше одному разработчику проделать все действия, чтобы понять ситуацию во всей полноте? Разве курс «на двоих», чтобы все сделали всё, не будет в 2 раза больше без адекватных на то причин?
nin-jin
Какая же тут полнота, если он ни разу тут мастер не ломает и сломанный мастер не растекается по всем фичеветкам?
NickT Автор
Как раз этот в точности кейз у нас в разделе «Ошибка на продуктиве».
Кстати, можете ответить и на другие мои вопросы.