- PVSM.RU - https://www.pvsm.ru -
В данной статье хотелось бы рассказать про организацию процессов Continuous Integration [1] / Continuous Delivery, автоматизирующих сборку, тестирование и доставку приложений применимо к решениям на платформе InterSystems.
Рассмотрим такие темы как:
Несмотря на то, что основная тема это CD, Git [2], а точнее ряд фундаментальных принципов лежащих в его основе оказали большое влияние на процессы разработки программного обеспечения, поэтому начнём с краткого описания основных терминов Git.
Git — система контроля и управления версиями, в основе которой такие идеи, как:
Git не был первой системой контроля версий в которой данные идеи были реализованы, однако Git популяризовал данные идеи и существенно упростил их практическое применение.
Место, где хранятся и поддерживаются какие-либо данные.
Локальный репозиторий — репозиторий, расположенный на локальном компьютере
Удалённый репозиторий — репозиторий, находящийся на удалённом сервере
Зафиксированное состояние репозитория.
Хранит отличие (Diff) от какого-либо другого коммита который называется родительским.
Родителей бывает:
Указатель на коммит.
Всегда можно посмотреть на его историю до первого коммита. Например, ветка master
:
Коммиты образуют деревья — графическое представление репозитория. Рассмотрим самый простой линейный вариант, когда к двум существующим коммитам с картинки выше добавили ещё три коммита:
Теперь пример дерева посложнее: два разработчика работают одновременно и, чтобы не мешать друг другу каждый из них работает в своей ветке. Это выглядит так:
Через некоторое время им надо объединить изменения, для этого существуют запросы на слияние — запросы на объединение двух состояний репозитория в одно новое состояние. В нашем случае запрос на слияние ветки develop
в ветку master
. После того как запрос был рассмотрен и одобрен и слияние произошло репозиторий выглядит следующим образом:
После чего разработка продолжается:
Методология разработки на основе Git это серия подходов к разработке программного обеспечения использующих Git в качестве основы разработки. Существует множество методологий разработки, рассмотрим две из них:
GitHub flow является, наверное, одной из самых простых методологий разработки на основе Git. Вот она:
Кроме того существует ряд правил:
master
всегда находится в работоспособном состоянииmaster
не идёт разработкаmaster
== промышленное окружениеmaster
Окружение это сконфигурированный ресурс где исполняется код вашего приложения. Это может быть сервер, виртуальная машина или даже контейнер.
Вот как это выглядит:
Подробно про GitHub flow на Хабре уже писали [3] и не раз [4].
Если вы не готовы автоматически обновлять код на промышленном окружении, GitLab flow предоставляет единение GitHub flow с несколькими окружениями. Вот как это работает: разработка ведётся аналогично GitHub flow — в отдельных ветках, которые также сливаются в master
, но содержимое ветки master
развёрнуто на тестовом сервере. Дополнительно у вас есть ветки окружений содержимое которых соответствует содержимому ваших окружений. Обычно существует три окружения но их может быть больше или меньше в зависимости от ваших требований:
master
preprod
prod
Выглядит процесс разработки так:
Подробно про GitLab flow на Хабре тоже писали [5].
Существует ряд методологий разработки основанных на Git — от простых до сложных. Выберите ту методологию которая с одной стороны не переусложнена, с другой обеспечивает надлежащий уровень контроля за состоянием вашего проекта.
GitLab Workflow — методология разработки программного обеспечения, которая затрагивает не только этапы разработки но весь жизненный цикл продукта от идеи до отзывов пользователей. Вот как он выглядит:
Подробно каждый этап описан на сайте GitLab [6], я ограничусь описанием нескольких этапов.
Начальные этапы GitLab Workflow сосредоточены на задаче — новой функциональности, ошибке или каком-либо другом отдельном объёме работ. Задача имеет несколько целей, таких как:
Этап планирования позволяет группировать задачи по их приоритету, этапу, статусу.
Разработку мы обсудили выше, следуйте любой методологии разработки git. После того, как мы разработали нашу новую функциональность и слили её в ветку master
— что дальше?
Непрерывная доставка — это подход к разработке программного обеспечения, в котором команды разрабатывают программное обеспечение в коротких спринтах, гарантируя, что новая версия приложения может быть выпущена в любое время. Данный подход автоматизирует сборку, тестирование и доставку программного обеспечения. Этот подход помогает снизить затраты и риски внесения изменений, позволяя получать быстрые инкрементные обновления для приложений в промышленной эксплуатации. Для непрерывной доставки важно настроить простой и воспроизводимый процесс доставки.
В GitLab конфигурации непрерывной доставки определяется отдельно для каждого репозитория и хранится в файле конфигурации YAML в корне репозитория.
Определяет одно действие и какие условия должны выполняться для его запуска:
master
)?Окружение — это настроенный сервер или контейнер, в котором вы можете запускать скрипты.
Runner — служба, выполняет скрипты в определенном окружении. Подключен к GitLab и выполняют скрипты по мере необходимости.
Runner может быть развернут на сервере, в контейнере или даже на локальном компьютере разработчика.
master
срабатывает только в случае коммита в ветку master
). Этот набор называется запуском (pipeline).Вот пример запуска:
Он состоит из четырех этапов, выполняемых последовательно
Как мы видим, каждый скрипт выполнился успешно, если бы один из скриптов завершился неудачно, следующие скрипты не будут выполняться:
Если открыть скрипт, можно увидеть почему он закончился с ошибкой:
Running with gitlab-runner 10.4.0 (857480b6)
on test runner (ab34a8c5)
Using Shell executor...
Running on gitlab-test...
Fetching changes...
Removing diff.xml
Removing full.xml
Removing index.html
Removing tests.html
HEAD is now at a5bf3e8 Merge branch '4-versiya-1-0' into 'master'
From http://gitlab.eduard.win/test/testProject
* [new branch] 5-versiya-1-1 -> origin/5-versiya-1-1
a5bf3e8..442a4db master -> origin/master
d28295a..42a10aa preprod -> origin/preprod
3ac4b21..7edf7f4 prod -> origin/prod
Checking out 442a4db1 as master...
Skipping Git submodules setup
$ csession ensemble "##class(isc.git.GitLab).loadDiff()"
[2018-03-06 13:58:19.188] Importing dir /home/gitlab-runner/builds/ab34a8c5/0/test/testProject/
[2018-03-06 13:58:19.188] Loading diff between a5bf3e8596d842c5cc3da7819409ed81e62c31e3 and 442a4db170aa58f2129e5889a4bb79261aa0cad0
[2018-03-06 13:58:19.192] Variable modified
var=$lb("MyApp/Info.cls")
Load started on 03/06/2018 13:58:19
Loading file /home/gitlab-runner/builds/ab34a8c5/0/test/testProject/MyApp/Info.cls as udl
Load finished successfully.
[2018-03-06 13:58:19.241] Variable items
var="MyApp.Info.cls"
var("MyApp.Info.cls")=""
Compilation started on 03/06/2018 13:58:19 with qualifiers 'cuk /checkuptodate=expandedonly'
Compiling class MyApp.Info
Compiling routine MyApp.Info.1
ERROR: MyApp.Info.cls(version+2) #1003: Expected space : '}' : Offset:14 [zversion+1^MyApp.Info.1]
TEXT: quit, "1.0" }
Detected 1 errors during compilation in 0.010s.
[2018-03-06 13:58:19.252] ERROR #5475: Error compiling routine: MyApp.Info.1. Errors: ERROR: MyApp.Info.cls(version+2) #1003: Expected space : '}' : Offset:14 [zversion+1^MyApp.Info.1]
> ERROR #5030: An error occurred while compiling class 'MyApp.Info'
ERROR: Job failed: exit status 1
Ошибка компиляции стала причиной неудачного выполнения скрипта.
Перейдём от теории к практике.
Будем устанавливать GitLab на нашем собственном сервере. Впрочем можно пользоваться GitLab.com. Существует много способов установки GitLab: из исходников, из пакета, в контейнере. Выберите тот способ который вам нравится и следуйте инструкции по установке [7].
Требования:
Прежде всего настройте почтовые уведомления [8].
Далее, рекомендую установить Pages. Как было сказано выше, артефакты от выполнения скриптов могут быть загружены на GitLab. Пользователи могут загрузить их, но часто бывает полезно открыть их напрямую в браузере и для этого нужно установить pages.
Зачем нужны pages:
Так как в HTML может быть добавлено автоматическое перенаправление при загрузке страницы, то можно направлять пользователя на страницу с результатами юнит-тестов.
ClassMethod writeTestHTML()
{
set text = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(), "html").Data.Read()
set text = $replace(text, "!!!", ..getURL())
set file = ##class(%Stream.FileCharacter).%New()
set name = "tests.html"
do file.LinkToFile(name)
do file.Write(text)
quit file.%Save()
}
ClassMethod getURL()
{
set url = "http://host:57772"
set url = url _ $system.CSP.GetDefaultApp("%SYS")
set url = url_"/%25UnitTest.Portal.Indices.cls?Index="_ $g(^UnitTest.Result, 1) _ "&$NAMESPACE=" _ $zconvert($namespace,"O","URL")
quit url
}
XData html
{
<html lang="en-US">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="refresh" content="0; url=!!!"/>
<script type="text/javascript">window.location.href = "!!!"</script>
</head>
<body>
If you are not redirected automatically, follow this <a href='!!!'>link to tests</a>.
</body>
</html>
}
Кстати, я столкнулся с багом при использовании pages (Ошибка 502 при просмотре артефактов) — вот решение [10].
Для запуска CD скриптов необходимы окружения — настроенные серверы для запуска вашего приложения. Для начала предположим, что у вас установлен Linux-сервер с платформой InterSystems (скажем, InterSystems IRIS, но всё будет работать с Caché или Ensemble). Для соединения окружения с GitLab нужно:
gitlab-runner
вызывать InterSystems IRIS.Важное замечание по установке GitLab runner — НЕ клонируйте сервер после установки GitLab runner. Результаты непредсказуемы и нежелательны.
Расскажу поподробнее о шагах 2 и 3.
После запуска команды: sudo gitlab-runner register
Будет предложено несколько опций на выбор, и хотя большинство шагов довольно просто, некоторые из них стоит прокомментировать:
Доступно несколько токенов: для всей системы (доступен в настройках администрирования) либо для одного проекта (доступен в настройках проекта).
Когда вы подключаете runner для CD конкретного проекта, нужно указать токен именно этого проекта.
В CD конфигурации можно фильтровать, какие скрипты выполняются окружениях с определёнными тегами. Поэтому в самом простом случае укажите один тег, который будет названием окружения.
Вне зависимости от того используете ли вы docker, выбирайте shell для запуска скриптов.
gitlab-runner
вызывать InterSystems IRIS.После подключения к GitLab нужно разрешить юзеру gitlab-runner
вызывать InterSystems IRIS. Для этого:
gitlab-runner
должен иметь права для вызова irissession
либо csession
. Для этого его нужно добавить в группу irisusr
либо cacheusr
командой: usermod -a -G cacheusr gitlab-runner
gitlab-runner
и предоставьте ему права для выполнения скриптов CD (запись в БД и т.д.)Вместо пунктов 2 и 3 можно использовать другие подходы, например передачу пользователя/пароля, но вариант с ОС аутентификацией представляется мне более предпочтительным.
Итак, приступим к написанию конфигурации непрерывной доставки. Для начала опишем окружения и план.
У нас есть несколько окружений и соответствующих им веток:
Окружени | Ветка | Доставка | Кто может коммитить | Кто может сливать |
---|---|---|---|---|
Тестовое | master | Автоматическая | Разработчики, Владельцы | Разработчики, Владельцы |
Опытное | preprod | Автоматическая | Никто | Владельцы |
Промышленное | prod | По нажатию кнопки | Никто | Владельцы |
Тоже самое в графическом виде:
Наше тестовое приложение состоит из двух частей:
Из приведенного выше плана мы можем выделить этапы, которые нам необходимо определить в нашей конфигурации непрерывной доставки:
Начнём составлять конфигурацию в файле .gitlab-ci.yml
:
stages:
- load
- test
- package
- deploy
Следующая часть конфигурации — скрипты. Документация [15].
Начнём со скрипта load server
который загружает серверный код.
load server:
environment:
name: test
url: http://test.hostname.com
only:
- master
tags:
- test
stage: load
Что здесь происходит?
load server
это название скрипта.only: master
— запускает скрипт только если новый коммит в ветке master
.tags: test
— отправляет скрипт на запуск в runner, у которого есть такой тэг.stage
— определяет к какому этапу принадлежит скрипт.script
— определяет какую команду выполнять. В данном случае вызывается метод load
класса isc.git.GitLab
.Теперь надо создать класс isc.git.GitLab
. Все точки входа в него должны выглядеть так:
ClassMethod method()
{
try {
// code
halt
} catch ex {
write !,$System.Status.GetErrorText(ex.AsStatus()),!
do $system.Process.Terminate(, 1)
}
}
Такой метод может завершится только двумя способами
halt
, что считается успешным завершением скрипта.$system.Process.Terminate
— и выходом из процесса с ошибкой, что приводит к ошибке при выполнении скрипта.Вот код загрузки:
/// Do a full load
/// do ##class(isc.git.GitLab).load()
ClassMethod load()
{
try {
set dir = ..getDir()
do ..log("Importing dir " _ dir)
do $system.OBJ.ImportDir(dir, ..getExtWildcard(), "c", .errors, 1)
throw:$get(errors,0)'=0 ##class(%Exception.General).%New("Load error")
halt
} catch ex {
write !,$System.Status.GetErrorText(ex.AsStatus()),!
do $system.Process.Terminate(, 1)
}
}
Этот метод вызывает два других метода:
Но как мы можем получить директорию с репозиторием?
Когда GitLab выполняет скрипт, он определяет ряд переменных окружения [16]. Одна из них CI_PROJECT_DIR
— полный путь до корня репозитория. Так его можно получить в методе getDir
:
ClassMethod getDir() [ CodeMode = expression ]
{
##class(%File).NormalizeDirectory($system.Util.GetEnviron("CI_PROJECT_DIR"))
}
Вот скрипт запуска тестов:
tests:
environment:
name: test
url: http://test.hostname.com
only:
- master
tags:
- test
stage: test
script: csession IRIS "##class(isc.git.GitLab).test()"
artifacts:
paths:
- tests.html
Что изменилось? Конечно, название и код скрипта, но ещё добавился artifacts
. Артефакт — это набор файлов и папок, которые прикрепляются к скрипту после его успешного завершения. В нашем случае после завершения тестов мы можем сгенерировать HTML-страницу перенаправляющую пользователя на результаты тестов.
Обратите внимание на схожесть скриптов тестирования и загрузки. Части скриптов, такие как окружения, одинаковые во всех скриптах могут быть выделены в отдельные блоки. Определим тестовое окружение:
.env_test: &env_test
environment:
name: test
url: http://test.hostname.com
only:
- master
tags:
- test
Теперь наш скрипт tests
выглядит так:
tests:
<<: *env_test
script: csession IRIS "##class(isc.git.GitLab).test()"
artifacts:
paths:
- tests.html
Теперь напишем соответствующий серверный код, вызывающий юнит тесты [17] (статья на хабре [18]):
/// do ##class(isc.git.GitLab).test()
ClassMethod test()
{
try {
set tests = ##class(isc.git.Settings).getSetting("tests")
if (tests'="") {
set dir = ..getDir()
set ^UnitTestRoot = dir
$$$TOE(sc, ##class(%UnitTest.Manager).RunTest(tests, "/nodelete"))
$$$TOE(sc, ..writeTestHTML())
throw:'..isLastTestOk() ##class(%Exception.General).%New("Tests error")
}
halt
} catch ex {
do ..logException(ex)
do $system.Process.Terminate(, 1)
}
}
Настройка tests
— путь относительно корня репозитория до папки с тестами. Если он не задан, то пропускаем юнит тесты.
Метод writeTestHTML
(приведён выше) используется для генерации страницы-ссылки на результаты юнит-тестов.
Весь клиент это одна веб-страница, обращающаяся к REST API:
<html>
<head>
<script type="text/javascript">
function initializePage() {
var xhr = new XMLHttpRequest();
var url = "${CI_ENVIRONMENT_URL}:57772/MyApp/version";
xhr.open("GET", url, true);
xhr.send();
xhr.onloadend = function (data) {
document.getElementById("version").innerHTML = "Version: " + this.response;
};
var xhr = new XMLHttpRequest();
var url = "${CI_ENVIRONMENT_URL}:57772/MyApp/author";
xhr.open("GET", url, true);
xhr.send();
xhr.onloadend = function (data) {
document.getElementById("author").innerHTML = "Author: " + this.response;
};
}
</script>
</head>
<body onload="initializePage()">
<div id = "version"></div>
<div id = "author"></div>
</body>
</html>
И для того чтобы её "собрать", нужно заменить переменную ${CI_ENVIRONMENT_URL}
на её значение. В реальных приложениях всё будет намного сложнее (npm), но это просто пример. Вот скрипт:
package client:
<<: *env_test
stage: package
script: envsubst < client/index.html > index.html
artifacts:
paths:
- index.html
Наконец, развернём клиент переместив index.html
в корень веб-сервера.
deploy client:
<<: *env_test
stage: deploy
script: cp -f index.html /var/www/html/index.html
Вот и всё!
Что делать если нужно выполнить один и тот же скрипт в нескольких окружениях? Части скриптов также могут быть именованы, поэтому их тоже можно выделять в отдельные блоки. Вот пример конфигурации загружающей код в тестовое и опытное окружения:
stages:
- load
- test
.env_test: &env_test
environment:
name: test
url: http://test.hostname.com
only:
- master
tags:
- test
.env_preprod: &env_preprod
environment:
name: preprod
url: http://preprod.hostname.com
only:
- preprod
tags:
- preprod
.script_load: &script_load
stage: load
script: csession IRIS "##class(isc.git.GitLab).loadDiff()"
load test:
<<: *env_test
<<: *script_load
load preprod:
<<: *env_preprod
<<: *script_load
Так мы можем избавиться от дублирования кода.
Полная конфигурация CD доступна здесь [19] и она соответствует первоначальному плану перемещения кода между тестовым, опытным и промышленным окружениями.
Непрерывная доставка — это подход к разработке программного обеспечения, в котором команды разрабатывают программное обеспечение в коротких спринтах, гарантируя, что новая версия приложения может быть выпущена в любое время. Данный подход автоматизирует сборку, тестирование и доставку программного обеспечения. Этот подход помогает снизить затраты и риски внесения изменений, позволяя получать быстрые инкрементные обновления для приложений в промышленной эксплуатации.
Ваши приложения на платформе InterSytems легко могут быть адаптированы к процессам непрерывной доставки, увеличивая скорость разработки, улучшая качество кода и устраняя ошибки вызванные ручным управлением процессами сборки, тестирования и доставки.
Автор: eduard93
Источник [24]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/git/278699
Ссылки в тексте:
[1] Continuous Integration: https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%BF%D1%80%D0%B5%D1%80%D1%8B%D0%B2%D0%BD%D0%B0%D1%8F_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D1%8F
[2] Git: https://ru.wikipedia.org/wiki/Git#%D0%9F%D1%80%D0%B5%D0%B8%D0%BC%D1%83%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B0_%D0%B8_%D0%BD%D0%B5%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BA%D0%B8
[3] писали: https://habrahabr.ru/post/346066/
[4] раз: https://habrahabr.ru/post/189046/
[5] писали: https://habrahabr.ru/company/softmart/blog/316686/
[6] сайте GitLab: https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/
[7] инструкции по установке: https://about.gitlab.com/installation/
[8] почтовые уведомления: https://docs.gitlab.com/omnibus/settings/smtp.html
[9] И ряд других причин: https://docs.gitlab.com/ce/user/project/pages/#explore-gitlab-pages
[10] решение: https://gitlab.com/gitlab-com/infrastructure/issues/3064
[11] Установить GitLab runner: https://docs.gitlab.com/runner/
[12] Зарегистрировать: https://docs.gitlab.com/runner/register/index.html
[13] создайте пользователя: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_privileges
[14] Включите ОС аутентификацию: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_intro_authe_os
[15] Документация: https://docs.gitlab.com/ee/ci/yaml/README.html
[16] переменных окружения: https://docs.gitlab.com/ce/ci/variables/README.html
[17] юнит тесты: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=TUNT
[18] статья на хабре: https://habrahabr.ru/company/intersystems/blog/263677/
[19] здесь: https://github.com/intersystems-ru/GitLab/blob/master/.gitlab-ci.yml
[20] Серия статей на Developer Community про CD: https://community.intersystems.com/post/continuous-delivery-your-intersystems-solution-using-gitlab-part-vi-containers-infrastructure
[21] Тестовый репозиторий: http://gitlab.eduard.win/test/testProject
[22] Репозиторий с хуками GitLab: https://github.com/intersystems-ru/GitLab
[23] Документация по CD: https://about.gitlab.com/features/gitlab-ci-cd/
[24] Источник: https://habr.com/post/354158/?utm_source=habrahabr&utm_medium=rss&utm_campaign=354158
Нажмите здесь для печати.