paint-brush
Usando Git Hooks com MySQLpor@yuridanilov
4,692 leituras
4,692 leituras

Usando Git Hooks com MySQL

por Yuri Danilov7m2023/09/30
Read on Terminal Reader

Muito longo; Para ler

Criar banco de dados de teste Criar git hook pós-recebimento Executar scripts SQL por push
featured image - Usando Git Hooks com MySQL
Yuri Danilov HackerNoon profile picture
0-item
1-item

Os sistemas de controle de versão, e o Git em particular, são ferramentas essenciais para rastrear alterações de código, colaborar com sua equipe e garantir a estabilidade de sua base de código. Embora o Git seja projetado principalmente para código-fonte, você também pode usá-lo em combinação com bancos de dados MySQL para controle de versão e gerenciamento de alterações de esquema.


Neste artigo, exploraremos como integrar Git com MySQL para controle de versão usando ganchos Git, com exemplos específicos em formato de guia. Todos os scripts fornecidos nas listagens são totalmente funcionais e completos. Você pode reproduzi-los sequencialmente em seu ambiente de teste.


Primeiro de tudo, vamos criar um banco de dados de teste e um usuário:

 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'; 


Criando um banco de dados de teste


A seguir, criaremos um repositório remoto. Este pode ser um repositório em qualquer servidor remoto, mas para simplificar, iremos criá-lo localmente. Para facilitar a execução de comandos, eu uso o git bash. Minha máquina local já possui uma pasta git, então eu a uso:

 cd /c/git mkdir testdb.remote cd testdb.remote git init --bare 


Criando um repositório remoto

E crie um repositório local como um clone do remoto:

 cd /c/git git clone /c/git/testdb.remote testdb.local cd testdb.local git ls-files 


Criando um repositório local


Não há arquivos no repositório; vamos criar um e enviar nossas alterações para o repositório remoto:

 echo "Test DB repo" > readme.md git status git add . git commit -m "1st commit" git push 


Primeiro empurrão


Vamos verificar o conteúdo do repositório remoto:

 cd /c/git/testdb.remote git ls-tree --full-tree -r HEAD 


Arquivo em repositório remoto


Existe uma pasta hooks no repositório remoto, que contém vários arquivos com exemplos:

 ls -1 /c/git/testdb.remote/hooks 


Amostra de ganchos


Hooks são scripts executados quando ocorrem eventos específicos. O Git possui ganchos do lado do cliente e do lado do servidor. Os ganchos do lado do cliente são acionados por operações como confirmação e mesclagem. Os ganchos do lado do servidor são executados em operações de rede, como o recebimento de confirmações enviadas. Os ganchos são descritos em detalhes aqui . Existem diferentes opções para implementar a lógica; Darei um exemplo de uso do gancho pós-recebimento do lado do servidor.


Na pasta hooks, precisamos criar um arquivo chamado “post-receive”, este é um 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


O script acima será executado no servidor sempre que um push for concluído com sucesso e gerará uma lista de arquivos modificados. Vamos verificar como funciona adicionando uma linha ao readme.md e enviando as alterações para o repositório remoto:

 cd /c/git/testdb.local echo "New line" >> readme.md git add . git commit -m "Line added" git push 


Testando o script de gancho pós-recebimento


Você pode ver que ao executar o comando git push, a saída agora contém linhas começando com remote: - esta é a saída do script pós-recebimento que foi executado no servidor.

Falando em alterações básicas, os arquivos podem ser adicionados, modificados e excluídos. Você pode adotar diferentes abordagens sobre como aplicar essas alterações ao banco de dados:


  1. Faça alterações no banco de dados somente ao adicionar novos arquivos. Nesse caso, você pode salvar todas as alterações de uma tarefa específica em um único arquivo. Este arquivo pode conter o nome da tarefa do sistema de rastreamento de bugs. Isso pode não ser muito conveniente durante o desenvolvimento, mas essas alterações são mais fáceis de lidar.
  2. Mantenha um arquivo separado no repositório para cada objeto (tabelas, procedimentos) e aplique alterações no banco de dados ao alterar ou adicionar arquivos.
  3. Uma abordagem mais abrangente é criar uma pasta separada para cada tarefa (solicitação de alteração) e colocar nela os arquivos que devem ser executados como parte da instalação de uma determinada versão. Ao mesmo tempo, crie um arquivo adicional descrevendo a lista de arquivos e a ordem em que devem ser instalados. Esta abordagem é mais flexível mas, ao mesmo tempo, mais complexa tanto para o desenvolvimento como para a implementação do pipeline.


Suponha que você escolheu a segunda opção e precisa executar os arquivos que foram adicionados ou alterados. Podemos filtrar esses arquivos conforme descrito aqui adicionando o 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


Adicione alguns arquivos e altere também o readme.md novamente:

 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


A saída do script de gancho contém 4 arquivos:

Lista de todos os arquivos alterados


Editamos os arquivos test1.sql e readme.md, excluímos test2.sql e adicionamos outro arquivo:

 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


Somente arquivos modificados e adicionados são exibidos:

Apenas arquivos adicionados ou alterados


Nosso objetivo é executar scripts SQL após cada push bem-sucedido, portanto, precisamos filtrar apenas arquivos desse tipo; no nosso caso, definiremos o requisito de que todos eles tenham a extensão “.sql”. Para filtrar, adicione o parâmetro -- "*.sql" ao 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 executar scripts, também devemos ser capazes de nos conectar ao banco de dados. Para isso, criaremos credenciais e testaremos a conexão:

 mysql_config_editor set --login-path=testdb_remote --host=localhost --port=3306 --user=user_remote --password mysql --login-path=testdb_remote --database=testdb_remote 


Criando credenciais para MySQL


Modifique nosso script para iterar pelos arquivos “.sql”, execute cada arquivo e verifique o resultado. Também precisamos classificar a saída da lista para executar os arquivos na ordem necessária. Com o comando git show, exibimos o conteúdo do script SQL e o passamos pelo pipe para execução pelo MySQL .

A variável “$?” conterá 0 se o script SQL foi executado com sucesso e outro valor se houve um erro:

 #!/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


Faça mais um teste - exclua todos os arquivos “.sql” criados anteriormente e crie scripts para:


  • criação de tabela
  • inserindo dados na tabela
  • criando um procedimento para receber dados da tabela
  • script contendo um erro


Também precisamos adicionar um prefixo (1_, 2_, etc.) a cada nome de arquivo para garantir a ordem de execução desejada dos arquivos:

 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


Portanto temos quatro arquivos “.sql” que precisam ser executados:

Lista de arquivos

Fazemos alterações no repositório:

Apenas arquivos SQL são executados


E vemos que quando git push é executado, os arquivos são executados sequencialmente e o resultado da execução (código de saída do comando MySQL) de cada arquivo é exibido. O arquivo “4_error_select.sql” contém um erro de sintaxe, portanto o resultado de sua execução é 1.


E por fim, vamos verificar o que temos no banco de dados:

 mysql --login-path=testdb_remote --database=testdb_remote show tables; call get_customer(1); call get_customer(2); 


Testando objetos criados no banco de dados


Como você pode ver, a tabela e o procedimento foram criados no banco de dados remoto. O procedimento é executado com sucesso e retorna dados.


Para melhorar a legibilidade da saída do script de gancho, você pode suprimir a saída da CLI do MySQL ou redirecioná-la para um arquivo de log. Você também pode analisar o resultado da execução do comando MySQL e adicionar mais lógica ao script de gancho.


Por exemplo, você pode executar scripts SQL em um banco de dados de teste e depois executar alguns testes nele (como descrevi aqui ). Se os testes forem concluídos com êxito no banco de dados de teste, execute scripts SQL no banco de dados de produção e provavelmente execute alguns testes nele também.

Ao analisar os resultados de cada etapa, você pode criar pipelines de qualquer configuração.


É claro que cada abordagem tem uma série de vantagens e limitações. Com a segunda abordagem, é necessário garantir a ordem em que os scripts são executados, pois não podemos, por exemplo, inserir dados em uma tabela até que ela seja criada. Também é necessário garantir que você possa executar novamente os scripts, ou seja, lidar corretamente com situações em que o objeto que está sendo criado já está no banco de dados, ou o banco de dados já contém os dados a serem adicionados.


Ao utilizar um sistema de versionamento, o processo de desenvolvimento torna-se um pouco mais complicado, pois é necessário formalizar adicionalmente alterações em scripts de formato pré-determinado. No entanto, você pode obter alguma flexibilidade usando um arquivo de instalação.


A principal vantagem da técnica descrita é a implementação de versionamento no banco de dados com pouquíssimo esforço, bem como a capacidade de implementar pipelines de CI/CD .

Para avaliar se isso pode ser útil para você, comece perguntando-se: com que frequência você escreve código diretamente na produção?