
Дочитав статью до самого конца, вы догадаетесь, почему в качестве КДПВ выбран бобренок в коробке
Всем здоровья, товарищи хаброжители. Совсем недавно столкнулся с необходимостью поднять и настроить сервис «Непрерывной интеграции» (далее 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 оно вполне подойдет (я так думаю!).
