Дочитав статью до самого конца, вы догадаетесь, почему в качестве КДПВ выбран бобренок в коробке
Всем здоровья, товарищи хаброжители. Совсем недавно столкнулся с необходимостью поднять и настроить сервис «Непрерывной интеграции» (далее CI) на одном очень небольшом проекте, очень косвенно связанном с моей работой. Время не поджимало, потому решил попробовать что-то новенькое (ранее использовал только Travis и Jenkins). Главным критерием выбора была: «простота и скорость развертывания системы на интеграционном сервере».
Под катом небольшая история и получившийся в ходе нее инструмент для CI, написанный за два вечера на Bash.
Идея
Возможно я плохо искал, возможно мне хотелось принести жертву богу велосипедов, но мне не попался на глаза ни один сервис CI, который можно было просто «закинуть» на сервер, прописать в конфигурацию Github пару webhooks и забыть о DevOps. Потому я решил больше не тратить время на поиск (а потратил я около 1-2 часов), а написать что-то простое на Bash.
Идея заключалась в следующем: написать простейший CI-trigger на Bash, который будет проходить по цепочке (конвееру, pipe) функций, настраиваемых из другого (конфигурационного) скрипта.
Плюсы данного решения очевидны:
- Написать его можно за десять минут
- Оно будет очень гибким
- Ничего для развертывания CI не потребуется, кроме переноса CI-trigger файла и конфигурации на сервер интеграции
Думаю о минусах этого решения вы уже и сами догадались. Я приведу лишь несколько:
- Нет удобного для использования и настройки GUI
- Нет готовых решений и плагинов, все придется писать на Bash или доступных ему для вызова
- Требуются некоторые знания в области подготовки, развертывания и тестирования проекта
Реализация
Сам CI-trigger элементарен:
CI-trigger
config=`pwd`/ci.config # Адрес конфигурационного скрипта
log=`pwd`/ci.log # Адрес лог-файла
# Разбор аргументов
# ...
# Дефолтные реализации функций-интеграции
function ci_bootstrap {
return 0
}
function ci_update {
return 0
}
function ci_analyse {
return 0
}
function ci_build {
return 0
}
function ci_unit_test {
return 0
}
function ci_deploy {
return 0
}
function ci_test {
return 0
}
function ci_archive {
return 0
}
function ci_report {
return 0
}
function ci_error {
return 0
}
# Подключение конфигурации и выполнение интеграции
. $config &&\
( \
ci_bootstrap &&\
ci_update &&\
ci_analyse &&\
ci_build &&\
ci_unit_test &&\
ci_deploy &&\
ci_test &&\
ci_archive &&\
ci_report || ci_error
) 1>>$log 2>&1
Я не стал приводить полный код скрипта, опустив логику разбора аргументов командной строки, так как это тривиально и не имеет отношения к задаче (в конце статьи будет ссылка на репозиторий проекта, в котором вы сможете лицезреть исходный код без сокращений).
Как видите, все сводится к объявлению 9 функций-интеграции, вызываемых конвейерно для выполнения интеграции при запуске CI-trigger. Оба выходных потока конвейера объединяются в одном файле-лога, который выступает в качестве отчета о результатах интеграции.
Перед выполнением конвейера интеграции вызывается конфигурационный скрипт от имени CI-tirgger (
. $config
), позволяя ему переопределить любые функции. В этом и кроется вся «магия» решения. В связи с тем, что конфигурационный скрипт написан на Bash, мы можем использовать любую логику для выполнения интеграции, просто сгруппировав ее в функции.Пример конфигурации
# Переход в каталог проекта
cd my-project
# Подготовка проекта к сборке
function ci_bootstrap {
mysql -uadmin -pmy_pass -e "DROP DATABASE db; CREATE DATABASE db"
}
# Загрузка изменений исходных кодов
function ci_update {
if test -d .git; then
return git pull
else
return git clone https://github.com/vendor/project ./
fi
}
# Сборка
function ci_build {
return npm install && npm run build
}
# Запуск модульных тестов
function ci_unit_test {
return npm run unit_test
}
# Развертывание проекта
function ci_deploy {
return mysql -uadmin -pmy_pass db < migration/schema.sql &&\
mysql -uadmin -pmy_pass db < migration/data.sql
}
# Уведомление о результатах интеграции
function ci_report {
return mail -s "CI report" my@mail.com < $log
}
# Уведомление об ошибке
function ci_error {
echo "== Error =="
return mail -s "CI report" my@mail.com < $log
}
Теперь нам остается только настроить логику вызова CI-trigger в соответствии с нашими требованиями.
Периодический вызов
Для этого достаточно настроить Cron, на пример так:
crontab
0 0 * * * /home/user/ci/trigger
Вызов при изменении
Это решение требует реализации механизма, отвечающего за прослушивание порта интеграционного сервера с вызовом CI-trigger при обращении на него. Я предлагаю использовать для этого netcat и следующий простой Bash-скрипт:
Прослушивание порта
while true; do
{ echo -ne "HTTP/1.0 200 OK\r\n\r\n"; } | nc -v -l -p 8000 && /home/user/ci/trigger
done
Теперь необходимо настроить используемую нами систему контроля версий для выполнения HTTP-запроса к этому порту при каждом push commits, на пример с помощью Curl:
.git/hooks/post-commit
curl -X POST http://ci-server.com:8000
Ссылки и все такое
Естественно данное решение далеко не идеально, вам придется много «поработать руками» для того, чтобы использовать его в большом проекте, но для быстрого запуска сервиса CI оно вполне подойдет (я так думаю!).