Los sistemas de control de versiones, y Git en particular, son herramientas esenciales para rastrear los cambios de código, colaborar con su equipo y garantizar la estabilidad de su base de código. Si bien Git está diseñado principalmente para código fuente, también puedes usarlo en combinación con bases de datos MySQL para control de versiones y gestión de cambios de esquema.
En este artículo, exploraremos cómo integrar Git con MySQL para el control de versiones usando enlaces de Git, con ejemplos específicos en un formato de guía. Todos los scripts que figuran en los listados son completamente funcionales y completos. Puede reproducirlos secuencialmente en su entorno de prueba.
En primer lugar, creemos una base de datos y un usuario de prueba:
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';
A continuación, crearemos un repositorio remoto. Este puede ser un repositorio en cualquier servidor remoto, pero para simplificar, lo crearemos localmente. Para facilitar la ejecución de comandos, utilizo git bash. Mi máquina local ya tiene una carpeta git, así que la uso:
cd /c/git mkdir testdb.remote cd testdb.remote git init --bare
Y crea un repositorio local como un clon del remoto:
cd /c/git git clone /c/git/testdb.remote testdb.local cd testdb.local git ls-files
No hay archivos en el repositorio; creemos uno y enviemos nuestros cambios al repositorio remoto:
echo "Test DB repo" > readme.md git status git add . git commit -m "1st commit" git push
Comprobemos el contenido del repositorio remoto:
cd /c/git/testdb.remote git ls-tree --full-tree -r HEAD
Hay una carpeta de ganchos en el repositorio remoto, que contiene varios archivos con ejemplos:
ls -1 /c/git/testdb.remote/hooks
Los ganchos son scripts que se ejecutan cuando ocurren eventos específicos. Git tiene ganchos del lado del cliente y del lado del servidor. Los enlaces del lado del cliente se activan mediante operaciones como la confirmación y la fusión. Los enlaces del lado del servidor se ejecutan en operaciones de red, como la recepción de confirmaciones enviadas. Los ganchos se describen en detalle aquí . Existen diferentes opciones para implementar la lógica; Daré un ejemplo del uso del gancho del lado del servidor posterior a la recepción .
En la carpeta de ganchos, necesitamos crear un archivo llamado "post-receive", este es un script bash normal:
#!/bin/sh while read oval nval ref do echo List of files changed in the commit: git diff --name-only $oval $nval done
El script anterior se ejecutará en el servidor cada vez que se complete con éxito una inserción y generará una lista de archivos modificados. Comprobemos cómo funciona agregando una línea al archivo readme.md y enviando los cambios al repositorio remoto:
cd /c/git/testdb.local echo "New line" >> readme.md git add . git commit -m "Line added" git push
Puede ver que al ejecutar el comando git push, la salida ahora contiene líneas que comienzan con remote:
esta es la salida del script posterior a la recepción que se ejecutó en el servidor.
Hablando de cambios básicos, los archivos se pueden agregar, modificar y eliminar. Puede adoptar diferentes enfoques sobre cómo aplicar estos cambios a la base de datos:
Supongamos que eligió la segunda opción, por lo que necesita ejecutar archivos que se agregaron o modificaron. Podemos filtrar dichos archivos como se describe aquí agregando el parámetro --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
Agregue algunos archivos y cambie también el archivo readme.md nuevamente:
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 salida del script de enlace contiene 4 archivos:
Editamos los archivos test1.sql y readme.md, eliminamos test2.sql y agregamos otro archivo:
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
Sólo se muestran los archivos modificados y agregados:
Nuestro objetivo es ejecutar scripts SQL después de cada envío exitoso, por lo que necesitamos filtrar archivos solo de este tipo; en nuestro caso pondremos como requisito que todos tengan la extensión “.sql”. Para filtrar, agregue el parámetro -- "*.sql"
al comando 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
Para ejecutar scripts, también debemos poder conectarnos a la base de datos. Para ello crearemos credenciales y probaremos la conexión:
mysql_config_editor set --login-path=testdb_remote --host=localhost --port=3306 --user=user_remote --password mysql --login-path=testdb_remote --database=testdb_remote
Modifique nuestro script para recorrer los archivos “.sql”, ejecute cada archivo y verifique el resultado. También necesitamos ordenar la salida de la lista para ejecutar los archivos en el orden requerido. Con el comando git show, mostramos el contenido del script SQL y lo pasamos a través de la tubería para que MySQL lo ejecute .
La variable “$?” contendrá 0 si el script SQL se ejecutó exitosamente y otro valor si hubo un error:
#!/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
Realice una prueba más: elimine todos los archivos “.sql” creados anteriormente y cree scripts para:
También necesitamos agregar un prefijo (1_, 2_, etc.) a cada nombre de archivo para garantizar el orden de ejecución deseado de los archivos:
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
Entonces tenemos cuatro archivos “.sql” que deben ejecutarse:
Realizamos cambios en el repositorio:
Y vemos que cuando se realiza git push
, los archivos se ejecutan secuencialmente y se muestra el resultado de la ejecución (código de salida del comando MySQL) de cada archivo. El archivo “4_error_select.sql” contiene un error de sintaxis, por lo que el resultado de su ejecución es 1.
Y por último, revisemos lo que tenemos en la base de datos:
mysql --login-path=testdb_remote --database=testdb_remote show tables; call get_customer(1); call get_customer(2);
Como puede ver, la tabla y el procedimiento se crearon en la base de datos remota. El procedimiento se ejecuta exitosamente y devuelve datos.
Para mejorar la legibilidad de la salida del script de enlace, puede suprimir la salida de la CLI de MySQL o redirigirla a un archivo de registro. También puede analizar el resultado de la ejecución del comando MySQL y agregar más lógica al script de enlace.
Por ejemplo, puede ejecutar scripts SQL en una base de datos de prueba y luego ejecutar algunas pruebas en ella (como lo describí aquí ). Si las pruebas se completan con éxito en la base de datos de prueba, ejecute scripts SQL en la base de datos de producción y probablemente también ejecute algunas pruebas en ella.
Al analizar los resultados de cada paso, puede crear canalizaciones de cualquier configuración.
Por supuesto, cada enfoque tiene una serie de ventajas y limitaciones. Con el segundo enfoque, es necesario asegurar el orden en el que se ejecutan los scripts porque no podemos, por ejemplo, insertar datos en una tabla hasta que se cree. También es necesario asegurarse de poder volver a ejecutar scripts, es decir, manejar correctamente situaciones en las que el objeto que se está creando ya está en la base de datos o la base de datos ya contiene los datos que se van a agregar.
Cuando se utiliza un sistema de control de versiones, el proceso de desarrollo se vuelve un poco más complicado porque es necesario formalizar adicionalmente los cambios en scripts de un formato predeterminado. Sin embargo, puede lograr cierta flexibilidad utilizando un archivo de instalación.
La principal ventaja de la técnica descrita es la implementación del control de versiones en la base de datos con muy poco esfuerzo, así como la capacidad de implementar pipelines CI/CD .
Para evaluar si esto podría resultarle útil, puede comenzar preguntándose: ¿Con qué frecuencia escribe código directamente en producción?