Realizando deploy com hooks do git

Tenha um processo de deploy simples, rápido e confiável com hooks do git.

Deploy

Trabalho com integração continua visando entregas de melhorias de software rápidas e em curto espaço de tempo. Nos meus projetos pessoais e no trabalho (Templum) fazemos vários deploys por dia. Para nós é essencial ter controle sobre o que entra e sai, assim como um processo rápido de restauração da última versão estável em caso de versão com falhas.

Há projetos que possuem vários servidores de produção com um load balancer distribuindo a carga entre eles (olá Amazon AWS ;)). Imagina subir via SFTP para cada um deles? Seria o caos.

O git já nos dava confiança para modificar e refatorar coisas no ambiente de desenvolvimento (branches, histórico de modificações do arquivo + quem fez e quando). Levamos isso para o processo de deploy.

Para isso usamos uma funcionalidade interessante do git chamada hooks. Ela permite que o git execute scripts seus quando algumas ações acontecem no repositório, como um commit.

Configuramos um hook no git para que a cada novo commit no repositório remoto fosse acionado um script callback nosso em cada um dos servidores. Esse script encarrega-se de baixar a última versão do código diretamente do repositório.

Usamos uma versão modificada do script PHP a seguir. Para usá-lo devemos ter o git instalado no servidor de produção e ter o repositório do projeto devidamente clonado.

<?php
	/**
	 * GIT DEPLOYMENT SCRIPT
	 *
	 * Used for automatically deploying websites via github or bitbucket, more deets here:
	 *
	 *		https://gist.github.com/1809044
	 */

	// The commands
	$commands = array(
		'echo $PWD',
		'whoami',
		'git pull',
		'git status',
		'git submodule sync',
		'git submodule update',
		'git submodule status',
	);

	// Run the commands for output
	$output = '';
	foreach($commands AS $command){
		// Run it
		$tmp = shell_exec($command);
		// Output
		$output .= "$ {$command}n";
		$output .= htmlentities(trim($tmp)) . "n";
	}

	// Make it pretty for manual user access (and why not?)
?>



	
	GIT DEPLOYMENT SCRIPT



 .  ____  .    ____________________________
 |/      |   |                            |
[| ♥    ♥ |]  | Git Deployment Script v0.1 |
 |___==___|  /              © oodavid 2012 |
              |____________________________|


 

O script deve estar acessível via servidor web. Se você usar o script exatamente como acima ele deve estar dentro do repositório (mas você pode alterar os comandos do script para poder colocar em outro lugar). Para mais detalhes de como configurar o repositório e o hook, veja esse gist.

Nos meus repositórios e no trabalho sempre temos um branch ‘prod’. Tudo o que é mesclado lá em menos de 10 segundos está em produção.

A transferência é muito rápida porque o servidor só faz o download do que fora modificado.

Arquivos de configuração que possuem diferentes versões em ‘prod’ e ‘dev’ entram no .gitignore para não acontecer acidentes nas mesclagens. No repositório fica uma versão com sufixo “.dev.dist” ou “.prod.dist”. Quando faço a configuração inicial do repositório ou quando ocorre alterações no servidor de produção preciso simplesmente copiar o arquivo removendo o sufixo do nome. Como hoje só lido com dois arquivos desse tipo, faço manualmente quando preciso, mas poderia automatizar colocando isso dentro do script que trata os hooks do git. Deixar informações sensíveis fora do arquivo do repositório é uma boa (ex: senhas).

Para obter a última versão em produção é muito simples: “git pull origin prod”.

Geralmente trabalhamos em branches separados. Quando chega a hora de enviar para produção mesclamos com o branch prod e enviamos para o repositório.

Exemplo de fluxo de trabalho e deploy

git pull origin prod # pego a última versão em produção
git checkout -b novafeature # crio um branch separado para começar desenvolver a nova feature
# crio/edito os arquivos normalmente
git add . # adiciono os arquivos que foram modificados a stage area do git.
git commit -m "descricao do commit" # realizo o commit
# depois de um ou mais commits locais, quando o código fica estável, chega a hora de fazer deploy.
git checkout prod # entramos no branch prod
git merge novafeature # mesclamos com o branch 'novafeature'
git push origin prod # enviamos para o repositório remoto, que adionará o hook do git e dará inicio ao deploy.

Esse exemplo é simples, mas eu poderia ter enviado o branch “novafeature” para o repositório remoto para que outros desenvolvedores trabalhassem simultaneamente nele.

Considerações finais

Utilizo o git para deploy há 1 ano e hooks há 6 meses e atesto sua confiabilidade, eficiência e simplicidade. Quase todo desenvolvedor que usa git vai adorar. Quem não conhece git, em pouco tempo pega o jeito.

Particularmente gosto da linha de comando, mas tem gente que arrepia. Para essas pessoas existem programinhas front-end para git (mas estarão traindo o movimento! haha).

Quanto ao script, dá para fazer o que você quiser, como registrar a saída dos comandos no log e executar comandos da aplicação.

As dependências da aplicação (bundles do Symfony2, bibliotecas PHP, bibliotecas JS, Twitter Bootstrap, etc) não estão no repositório. Para tal utilizamos o fantástico composer. Ele cuida disso para nós, mas isso é assunto para outro texto.

Fiquem à vontade para comentar. Isso é tudo, pessoal.

Créditos da imagem: Florida National Guardsmen march at Ft. Hood after completing training to deploy to Kuwait, Iraq this week, por The National Guard, licenciado sob Creative Commons (CC BY 2.0).

5 Comments

  1. Rubens

    Muito bom! A Templum não podia ter feito contratação melhor! hahaha =)

    Se um dia eu voltar a programar, vou lembrar de fazer o que você indicou aqui. Sem dúvidas que a vida é outra com esse esquema de deploy.

    Grande abraço, meu amigo! Ótimo trabalho. 

  2. Omar Alves

    Excelente artigo, Thiago!
    Realmente este esquema de deploy é um achado! E certamente vem nos livrando de várias dores de cabeça.
    Abraço!

    1. Thiago Rodrigues

      Valeu Omar!

      E vale lembrar que você que veio com a idéia dos hooks (até então tinha que entrar no server e dar um “git pull origin prod”). A partir dai pesquisamos e implementamos esse esquema. Uma ótima sacada.

      Abraço!

  3. juniel

    Olá Thiago,

    Muito bom o artigo.
    Tenho utilizado git e gostaria de automatizar o trabalho com os hooks.
    Utilizo o gitlab organizar os projetos e usuários.

    De que forma poderia utilizar o seu script para aplicar o deploy no server de produção assim que recebece um merger do develop no master?

    As alterações são realizadas na maquina local em seguida enviada ao gitlab e posteriormente é feito o pull no server de prod.

    Abraços.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *