Les systèmes de contrôle de version, et Git en particulier, sont des outils essentiels pour suivre les modifications de code, collaborer avec votre équipe et garantir la stabilité de votre base de code. Bien que Git soit principalement conçu pour le code source, vous pouvez également l'utiliser en combinaison avec des bases de données MySQL pour le contrôle de version et la gestion des modifications de schéma.
Dans cet article, nous explorerons comment intégrer Git à MySQL pour le contrôle de version à l'aide de hooks Git, avec des exemples spécifiques sous forme de guide. Tous les scripts donnés dans les listes sont entièrement fonctionnels et complets. Vous pouvez les reproduire séquentiellement dans votre environnement de test.
Tout d’abord, créons une base de données de test et un utilisateur :
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';
Ensuite, nous allons créer un référentiel distant. Il peut s'agir d'un référentiel sur n'importe quel serveur distant, mais pour plus de simplicité, nous allons le créer localement. Pour faciliter l'exécution des commandes, j'utilise git bash. Ma machine locale possède déjà un dossier git, je l'utilise donc :
cd /c/git mkdir testdb.remote cd testdb.remote git init --bare
Et créez un référentiel local en tant que clone du référentiel distant :
cd /c/git git clone /c/git/testdb.remote testdb.local cd testdb.local git ls-files
Il n'y a aucun fichier dans le référentiel ; Créons-en un et transférons nos modifications au dépôt distant :
echo "Test DB repo" > readme.md git status git add . git commit -m "1st commit" git push
Vérifions le contenu du référentiel distant :
cd /c/git/testdb.remote git ls-tree --full-tree -r HEAD
Il existe un dossier hooks dans le référentiel distant, qui contient plusieurs fichiers avec des exemples :
ls -1 /c/git/testdb.remote/hooks
Les hooks sont des scripts exécutés lorsque des événements spécifiques se produisent. Git a des hooks côté client et côté serveur. Les hooks côté client sont déclenchés par des opérations telles que la validation et la fusion. Les hooks côté serveur s'exécutent sur des opérations réseau telles que la réception de validations poussées. Les crochets sont décrits en détail ici . Il existe différentes options pour implémenter la logique ; Je vais donner un exemple d'utilisation du hook côté serveur après réception .
Dans le dossier hooks, nous devons créer un fichier nommé « post-receive », il s'agit d'un script bash classique :
#!/bin/sh while read oval nval ref do echo List of files changed in the commit: git diff --name-only $oval $nval done
Le script ci-dessus sera exécuté sur le serveur chaque fois qu'un push sera terminé avec succès et affichera une liste de fichiers modifiés. Vérifions comment cela fonctionne en ajoutant une ligne au fichier readme.md et en transférant les modifications vers le référentiel distant :
cd /c/git/testdb.local echo "New line" >> readme.md git add . git commit -m "Line added" git push
Vous pouvez voir que lors de l'exécution de la commande git push, la sortie contient désormais des lignes commençant par remote:
- c'est la sortie du script de post-réception qui a été exécuté sur le serveur.
En parlant de changements fondamentaux, les fichiers peuvent être ajoutés, modifiés et supprimés. Vous pouvez adopter différentes approches pour appliquer ces modifications à la base de données :
Supposons que vous ayez choisi la deuxième option et que vous deviez donc exécuter les fichiers qui ont été ajoutés ou modifiés. Nous pouvons filtrer ces fichiers comme décrit ici en ajoutant le paramètre --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
Ajoutez quelques fichiers et modifiez à nouveau le fichier 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
La sortie du script hook contient 4 fichiers :
Nous modifions les fichiers test1.sql et readme.md, supprimons test2.sql et ajoutons un autre fichier :
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
Seuls les fichiers modifiés et ajoutés sont affichés :
Notre objectif est d'exécuter des scripts SQL après chaque push réussi, nous devons donc filtrer les fichiers de ce type uniquement ; dans notre cas, nous fixerons l'exigence qu'ils doivent tous avoir l'extension « .sql ». Pour filtrer, ajoutez le paramètre -- "*.sql"
à la commande 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
Pour exécuter des scripts, il faut également pouvoir se connecter à la base de données. Pour cela, nous allons créer des identifiants et tester la connexion :
mysql_config_editor set --login-path=testdb_remote --host=localhost --port=3306 --user=user_remote --password mysql --login-path=testdb_remote --database=testdb_remote
Modifiez notre script pour parcourir les fichiers « .sql », exécutez chaque fichier et vérifiez le résultat. Nous devons également trier la sortie de la liste pour exécuter les fichiers dans l'ordre requis. Avec la commande git show, nous affichons le contenu du script SQL et le passons via le tube pour exécution par MySQL .
La variable « $ ? » contiendra 0 si le script SQL a été exécuté avec succès et une autre valeur s'il y a eu une erreur :
#!/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
Effectuez un test supplémentaire : supprimez tous les fichiers « .sql » précédemment créés et créez des scripts pour :
Nous devons également ajouter un préfixe (1_, 2_, etc.) à chaque nom de fichier pour garantir l'ordre d'exécution souhaité des fichiers :
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
Nous avons donc quatre fichiers « .sql » qui doivent être exécutés :
Nous apportons des modifications au référentiel :
Et nous voyons que lorsque git push
est exécuté, les fichiers sont exécutés séquentiellement et le résultat de l'exécution (code de sortie de la commande MySQL) de chaque fichier est affiché. Le fichier « 4_error_select.sql » contient une erreur de syntaxe, le résultat de son exécution est donc 1.
Et enfin, vérifions ce que nous avons dans la base de données :
mysql --login-path=testdb_remote --database=testdb_remote show tables; call get_customer(1); call get_customer(2);
Comme vous pouvez le constater, la table et la procédure ont été créées dans la base de données distante. La procédure s'exécute avec succès et renvoie des données.
Pour améliorer la lisibilité de la sortie du script hook, vous pouvez supprimer la sortie de la CLI MySQL ou la rediriger vers un fichier journal. Vous pouvez également analyser le résultat de l'exécution de la commande MySQL et ajouter plus de logique au script hook.
Par exemple, vous pouvez exécuter des scripts SQL sur une base de données de test, puis y exécuter des tests (comme je l'ai décrit ici ). Si les tests sont terminés avec succès sur la base de données de test, exécutez des scripts SQL sur la base de données de production et exécutez probablement également des tests sur celle-ci.
En analysant les résultats de chaque étape, vous pouvez créer des pipelines de n'importe quelle configuration.
Bien entendu, chaque approche présente un certain nombre d’avantages et de limites. Avec la deuxième approche, il faut s'assurer de l'ordre dans lequel les scripts sont exécutés car on ne peut pas, par exemple, insérer des données dans une table tant qu'elle n'est pas créée. Il est également nécessaire de s'assurer que vous pouvez réexécuter les scripts, c'est-à-dire gérer correctement les situations où l'objet en cours de création est déjà dans la base de données ou où la base de données contient déjà les données à ajouter.
Lors de l'utilisation d'un système de versioning, le processus de développement devient un peu plus compliqué car il est nécessaire de formaliser en plus les modifications dans des scripts d'un format prédéterminé. Cependant, vous pouvez obtenir une certaine flexibilité en utilisant un fichier d'installation.
Le principal avantage de la technique décrite est la mise en œuvre du versioning dans la base de données avec très peu d'effort, ainsi que la possibilité de mettre en œuvre des pipelines CI/CD .
Pour évaluer si cela pourrait vous être utile, vous pouvez commencer par vous demander : à quelle fréquence écrivez-vous du code directement en production ?