Системы контроля версий, и в частности Git, являются важными инструментами для отслеживания изменений кода, совместной работы с вашей командой и обеспечения стабильности вашей кодовой базы. Хотя Git в первую очередь предназначен для исходного кода, вы также можете использовать его в сочетании с базами данных MySQL для контроля версий и управления изменениями схемы.
В этой статье мы рассмотрим, как интегрировать Git с MySQL для управления версиями с помощью перехватчиков Git, с конкретными примерами в формате руководства. Все скрипты, представленные в листингах, полностью функциональны и полны. Вы можете воспроизвести их последовательно в своей тестовой среде.
Прежде всего, давайте создадим тестовую базу данных и пользователя:
create database testdb_remote; create user 'user_remote'@'localhost' identified WITH mysql_native_password by 'remote123'; grant all on testdb_remote.* to 'user_remote'@'localhost';
Далее мы создадим удаленный репозиторий. Это может быть репозиторий на любом удаленном сервере, но для простоты мы создадим его локально. Для удобства выполнения команд я использую git bash. На моем локальном компьютере уже есть папка git, поэтому я использую ее:
cd /c/git mkdir testdb.remote cd testdb.remote git init --bare
И создадим локальный репозиторий как клон удаленного:
cd /c/git git clone /c/git/testdb.remote testdb.local cd testdb.local git ls-files
В репозитории нет файлов; давайте создадим его и отправим наши изменения в удаленный репозиторий:
echo "Test DB repo" > readme.md git status git add . git commit -m "1st commit" git push
Проверим содержимое удаленного репозитория:
cd /c/git/testdb.remote git ls-tree --full-tree -r HEAD
В удаленном репозитории есть папкаooks, содержащая несколько файлов с примерами:
ls -1 /c/git/testdb.remote/hooks
Хуки — это сценарии, которые выполняются при возникновении определенных событий. В Git есть перехватчики на стороне клиента и на стороне сервера. Перехваты на стороне клиента запускаются такими операциями, как фиксация и слияние. Серверные перехватчики выполняются при сетевых операциях, таких как получение принудительной фиксации. Хуки подробно описаны здесь . Существуют разные варианты реализации логики; Я приведу пример использования серверного хука после получения .
В папке с крючками нам нужно создать файл с именем «post-receive», это обычный bash-скрипт:
#!/bin/sh while read oval nval ref do echo List of files changed in the commit: git diff --name-only $oval $nval done
Приведенный выше скрипт будет выполняться на сервере при успешном завершении отправки и выводить список измененных файлов. Давайте проверим, как это работает, добавив строку в readme.md и отправив изменения в удаленный репозиторий:
cd /c/git/testdb.local echo "New line" >> readme.md git add . git commit -m "Line added" git push
Вы можете видеть, что при выполнении команды git push выходные данные теперь содержат строки, начинающиеся с remote:
— это выходные данные скрипта после получения, который был выполнен на сервере.
Говоря об основных изменениях, файлы можно добавлять, изменять и удалять. Вы можете использовать разные подходы к тому, как применить эти изменения к базе данных:
Предположим, вы выбрали второй вариант, поэтому вам нужно выполнить файлы, которые были добавлены или изменены. Мы можем фильтровать такие файлы, как описано здесь , добавив параметр --diff-filter=AM
:
#!/bin/sh while read oval nval ref do echo List of files added or changed in the commit: git diff --name-only --diff-filter=AM $oval $nval done
Добавьте несколько файлов, а также еще раз измените readme.md:
echo "SELECT 1;" > test1.sql echo "SELECT 2;" > test2.sql echo "SELECT 3;" > test3.sql echo "New line 2" >> readme.md git add . git commit -m "New files" git push
Вывод скрипта перехватчика содержит 4 файла:
Редактируем файлы test1.sql и readme.md, удаляем test2.sql и добавляем еще один файл:
echo "SELECT 11;" > test1.sql echo "New line 2" >> readme.md rm test2.sql echo "SELECT 4;" > test4.sql git add . git commit -m "Modify, remove and add" git push
Отображаются только измененные и добавленные файлы:
Наша цель — выполнять SQL-скрипты после каждого успешного нажатия, поэтому нам нужно фильтровать файлы только этого типа; в нашем случае мы установим требование, чтобы все они имели расширение «.sql». Для фильтрации добавьте параметр -- "*.sql"
в команду git diff
:
#!/bin/sh while read oval nval ref do echo List of files added or changed in the commit: git diff --name-only --diff-filter=AM $oval $nval -- "*.sql" done
Для выполнения сценариев мы также должны иметь возможность подключаться к базе данных. Для этого мы создадим учетные данные и проверим соединение:
mysql_config_editor set --login-path=testdb_remote --host=localhost --port=3306 --user=user_remote --password mysql --login-path=testdb_remote --database=testdb_remote
Измените наш скрипт, чтобы он перебирал файлы «.sql», выполнял каждый файл и проверял результат. Нам также необходимо отсортировать вывод списка, чтобы выполнять файлы в необходимом порядке. С помощью команды git show мы отображаем содержимое SQL-скрипта и передаем его по каналу для выполнения MySQL .
Переменная «$?» будет содержать 0, если SQL-скрипт был выполнен успешно, и другое значение, если произошла ошибка:
#!/bin/sh while read oval nval ref do echo List of files added or changed in the commit: for file in $(git diff --name-only --diff-filter=AM $oval $nval -- "*.sql" | sort); do git show master:${file} | mysql --login-path=testdb_remote --database=testdb_remote echo "FILE: ${file} - result $?" done done
Выполните еще один тест — удалите все ранее созданные файлы «.sql» и создайте скрипты для:
Нам также необходимо добавить префикс (1_, 2_ и т. д.) к каждому имени файла, чтобы обеспечить желаемый порядок выполнения файлов:
rm *.sql echo "DB Initialization" >> readme.md echo " DROP TABLE IF EXISTS customers; CREATE TABLE customers ( id int UNSIGNED NOT NULL AUTO_INCREMENT, name varchar(255) DEFAULT NULL, PRIMARY KEY (id) ); " > 1_customers.sql echo " INSERT INTO customers (id, name) VALUES (1, 'John Doe'), (2, 'Jane Smith') AS new ON DUPLICATE KEY UPDATE customers.name = new.name; " > 2_customers_init.sql echo " DROP PROCEDURE IF EXISTS get_customer; DELIMITER $$ CREATE PROCEDURE get_customer(IN customer_id int UNSIGNED) BEGIN SELECT c.id, c.name FROM customers c WHERE c.id = customer_id; END $$ " > 3_get_customer.sql echo "SELECT FROM customers;" > 4_error_select.sql ls -1
Итак, у нас есть четыре файла «.sql», которые необходимо выполнить:
Вносим изменения в репозиторий:
И мы видим, что при выполнении git push
файлы выполняются последовательно, и отображается результат выполнения (код завершения команды MySQL) каждого файла. Файл «4_error_select.sql» содержит синтаксическую ошибку, поэтому результат его выполнения равен 1.
И напоследок проверим, что у нас есть в базе:
mysql --login-path=testdb_remote --database=testdb_remote show tables; call get_customer(1); call get_customer(2);
Как видите, таблица и процедура были созданы в удаленной базе данных. Процедура выполняется успешно и возвращает данные.
Чтобы улучшить читаемость вывода сценария ловушки, вы можете подавить вывод MySQL CLI или перенаправить его в файл журнала. Вы также можете проанализировать результат выполнения команды MySQL и добавить дополнительную логику в сценарий перехвата.
Например, вы можете выполнить SQL-скрипты на тестовой базе данных, а затем запустить на ней несколько тестов (как я описал здесь ). Если тесты успешно завершены в тестовой базе данных, запустите сценарии SQL в рабочей базе данных и, возможно, также проведите несколько тестов на ней.
Анализируя результаты каждого шага, можно создавать конвейеры любой конфигурации.
Конечно, каждый подход имеет ряд преимуществ и ограничений. При втором подходе необходимо обеспечить порядок выполнения скриптов, поскольку мы не можем, например, вставлять данные в таблицу, пока она не будет создана. Также необходимо обеспечить возможность повторного выполнения скриптов, т.е. корректно обрабатывать ситуации, когда создаваемый объект уже находится в базе данных, или база данных уже содержит добавляемые данные.
При использовании системы версионирования процесс разработки немного усложняется, поскольку необходимо дополнительно формализовать изменения в скриптах заранее заданного формата. Однако вы можете добиться некоторой гибкости, используя установочный файл.
Основным преимуществом описанной методики является реализация версионирования в базе данных с минимальными усилиями, а также возможность реализации CI/CD-конвейеров .
Чтобы оценить, может ли это быть для вас полезным, вы можете начать с вопроса: как часто вы пишете код непосредственно в рабочей среде?