Своя система сборки на Linux

image
Здравствуйте! Я давно не появлялся здесь в качестве оратора, но в этот раз я решил поделится кое-чем, что сделал сам, а также узнать — нужно это, не нужно, как можно доработать и вообще услышать любые отзывы о моих деяниях.

Мотивация
Проблема сборки и запуска проекта на разных машинах преследовала меня всегда. Для того, чтобы реалистично смоделировать работу разрабатываемого сайта на локальной машине нужно установить Web-сервер, Application-сервер, возможно, к ним присоединится какой-нибудь ещё промежуточный сервер, установить базу данных, настроить базу данных. Для того, чтобы установить тестовый сайт на тестовый сервер, нужно проделать такую же работу. И позже тоже самое с рабочим сервером.

Кажется, что проблема решается легко — напиши все команды в файл и просто запускай его везде. Решение относительно хорошее, но не идеальное, и вот почему. К примеру, на одном из серверов уже установлены нужные пакеты и база данных там готова. Но не до конца, к ней не применены последние миграции. Придётся открывать файл с командами и вытаскивать оттуда нужные, дабы не получить ошибку или чтобы что-то не сломать.

Но это не такая серьёзная проблема, большую проблему я определил для себя при работе с Django. Django, как известно, при его запуске висит в памяти, и если код изменён — эти изменения никак не повлияют на сайт. Приходится постоянно перезапускать сервер. Несложно. А если изменены модели, нужно ещё и миграции создать и применить? А если настройки веб-сервера изменены, то нужно и их ещё применить и перезапустить веб-сервер? А если всё вместе, а проект я открывал месяц назад и абсолютно не помню, что я там менял и мне бы «сделать хорошо», но я не хочу утомительно вбивать все команды? А если проект огромный и я не хочу тратить время на лишние команды при запуске и сборке? И таких «А если…» может быть тьма тьмущая.

Решение пришло само — нужна автоматизация, сборщик проектов. Конечно же, на Linux. Погуглив, я нашёл уйму сборщиков проектов… Для одного языка или одной технологии. Ничего действительно универсального, чтобы прописал команды — и он их по надобности запускает — нет. Есть cmake, но его я не взял, потому что придумал решение получше)

В этот момент была создана первая схема велосипеда. Сначала я написал в файл все команды, но при малейших изменениях перезапуск всего занимал длительное время — это раздражало. Сначала смирился. Потом хотел, чтобы у скрипта были настройки, написал в первых строках переменные и описал алгоритм их изменения через аргументы запуска скрипта. Потом мне всё же захотелось не выполнять некоторые команды, если этого не требуется, и я сделал функции проверки. Потом пришла идея разграничить команды, некоторые объединить друг с другом.

Объединённые команды я назвал «target». В скрипт отправляется имя цели, а потом она выполняется. Оказалось, что некоторые цели неспособны выполнится без выполнения других целей — так появилась иерархия. Потом функции проверки команд превратились в функции проверки целей. Потом захотелось упростить работу с установкой пакетов, и была создана сущность «package».

В общем, процесс разработки я могу описывать долго — это, наверное, скучно.

Результат
Итоговым рабочим вариантом получился bash скрипт в 400 строк, который я назвал xGod. Я так его назвал, потому что этот файл стал незаменим для меня при работе, как воздух.
Как работает xGod:

Запускается из консоли — bash ./xgod build.xg run
build.xg — это файл сборки, в котором прописаны все цели и дополнительные функции
run — это цель, которую нужно выполнить

Из чего состоит build.xg:

1. Из обычных строк на языке bash — они выполняются последовательно по мере считывания файла
2. Из целей

Например:

target syncdb: virtualenv createmysqluser
	source "$projectpath/venv/bin/activate"
	python3 "$projectpath/manage.py" makemigrations
	python3 "$projectpath/manage.py" migrate
	deactivate


syncdb — название цели; virtualenv createmysqluser — это цели, которые надо выполнить до выполнения цели syncdb, так называемые зависимости; всё остальное — это обычный bash код, которым и достигает саму цель.

3. Пакеты:

Например:

package gunicorn: python
	all:
		name: python3-gunicorn


gunicorn — название пакета (или цели, потому что для скрипта это такая же цель); python — зависимость; all — это название дистрибутива, к которому применяются вложенные настройки, all означает, что данные настройки применяются ко всем дистрибутивам без исключения, в данный момент реализована поддержка только debian и ubuntu, потому что с другими я не работал; name — это название пакета, используемое для установки.

4. Функции проверки:

Например:

check syncdb()
	# any code
	return 1 # or return 0
endcheck


Функция проверки позволяет проверить, нужно ли выполнять цель syncdb или нет. Сохраняется и выполняется она как обычная функция, возвращает 1 (если цель надо выполнять) или 0 (если цель не надо выполнять)

Также была написана система поддержки расширений. Цели package как раз-таки являются расширениями. Синтаксис расширений не сильно отличается от синтаксиса файлов сборки, в нём могут присутствовать:

1. Обычные команды на языке bash
2. Обязательно функция действия.

Например:

action
	# any code with $1
endaction


Это функция принимает на вход имя цели и выполняет её по своим правилам. Все внутренности цели она может получить из переменной ${TARGETS[$1]}

3. Функция проверки цели

Например:

check
	# any code with $1
	return 1 # or return 0
endcheck


Также получает на вход имя цели и проверяет, нужно ли её выполнять. Если нужно, то обязана вернуть 1, а если нет, то 0

Ещё применения
Применение данного скрипта может быть более крупным, чем просто сборка и запуск проектов из нулевого состояния машины. Например, у меня есть собственный набор пакетов, который я хочу видеть каждый раз при установке системы. Каждый раз в новых дистрибутивах меняют набор стандартных пакетов, поэтому после установки я не знаю, есть ли такие пакеты в системе или нет. Конечно же, я могу узнать, но мне лень. Намного проще набить все нужные пакеты в скрипт и одной командой запустить их установку. Те, которые уже есть в системе, он пропустит, а те, которых нет — установит. Всё просто.

В следствии такого применения скрипта его главным условием было — это минимальные зависимости для запуска. Поэтому вместо Python или C++ он написан на bash — чтобы его можно было запустить из любой среды Linux без дополнительных действий. Единственный минус — bash должен быть не меньше 4 версии, так как там ассоциативные массивы не поддерживаются.

Ссылку на код оставлю здесь.

© Habrahabr.ru