Deploy automático com GitHub e PHP Artigo

Conheça os cursos gratuitos do WebDevBr! - Inscreva-se!


Este artigo foi publicado a 2 anos, 1 mês, 2 semanas, 3 dias atrás.

Neste artigo vamos aprender como enviar os dados automaticamente do GitHub para o servidor final sempre que atualizarmos o repositório (no GitHub), para isso vamos usar webhooks. O processo é simples e natural e você pode implementar suas próprias regras, nada de Capistrano, Jenkins, Deployer e ferramentas novas, apenas o simples e puro PHP.

O que você precisa

Neste ponto é ideial que você conheça o Git a ponto de conseguir publicar algo no GitHub, se não, acalme-se, é muito simples e rápido:

  • Cadastre-se aqui no WebDevBr
  • Acesse a área de alunos.
  • Faça o curso free de Git
  • Pronto!

O curso é curto, não vai tomar muito tempo da sua vida e você vai conhecer uma ferramenta fantástica.

Gostou deste artigo?

Receba atualizações semanais com novos artigos do WebDevBr e outras dicas!

O que seu servidor precisa

Você também vai precisar que seu servidor tenha:

Eu uso um cloud na Digital Ocean, mas existem outras possibilidades, uma vez ouvi falar que a King Host tem tudo isso, mas como diz a música: "Nunca vi, nem comi, eu só ouço falar!"

Como deve funcionar

Sempre que terminarmos uma etapa do desenvolvimento você vai enviar estes dados para o repositório online (com o comando git push), aqui acaba o seu trabalho.

O GitHub vai avisar o servidor, o servidor vai rodar uma série de comandos automaticamente e atualizar o seu projeto online sem nenhuma interação sua.

Bacana né!

Esse processo é bem mais rápido e seguro que o FTP tradicional por vários motivos, vários mesmo, mas vou deixar aqui alguns que eu priorizo:

  • Você sempre pode voltar para uma versão anterior do projeto com o Git
  • Você tem um backup dos arquivos no GitHub
  • Backup facilitado em outros lugares (um comando git clone [url do repositório] e pronto!)
  • Mais seguro, ele só atualiza os arquivos que você alterou
  • Super simples para automatizar tarefas
  • Histórico de alterações com data, hora, quem alterou e arquivos e linhas alterados em uma interface gráfica na web.
  • Você pode proteger o acesso com uma conta prêmium, ou usar o BitBucket (lá isso é free)
  • Facilita o trabalho em equipe

Automatizando as coisas com o PHP

O arquivo visa automatizar as tarefas de um processo de deploy e isso pode ser um pouco diferente pra você dependento de como trabalha, aqui eu trabalho com Composer e testes, então se você não usa um ou os dois altere tudo para o seu caso.

Sempre que vou atualizar um projeto no servidor usando Git eu faço o seguinte:

  • git pull para puxar as atualizações do GitHub
  • rm -rf vendor para remover o diretório vendor do Composer
  • composer install para instalar tudo com base no composer.lock

Eu não uso o composer update no servidor porque isso atualizaria os pacotes e poderia dar algum erro inesperado por conta das diferentes versões, apagando o diretório vendor e instalando novamente ele vai baixar o que está no composer.lock, ou seja, exatamente o que eu tenho aqui localmente.

Obs.: Note que eu ocultei a etapa de testes com PHPUnit para simplificar o artigo.

Que tal criar um arquivo PHP que executa estes comandos em ordem pra mim, mas eu vou proteger isso contra acessos não autorizados, para isso eu criei um arquivo na raiz do projeto com o nome de deploy.php.

<?php

// se a variável 'k' existir na url eu uso, se não marco como nulo
$key = isset($_GET['k']) ? $_GET['k'] : null;

// se a variável $k for nulo (ou seja, não foi enviada na URL)
// então eu verifico se foi enviada via console, caso contrário
// o valor permanece nulo
if (is_null($key))
    $key = isset($argv[1]) ? $argv[1] : null;

// se o valor de $k for igual a minha "chave de proteção"
if ($key == 'a2cfe19e8065517b655f1360ff4625de') {
    //executo os comandos de Deploy
}

echo 'Deploy finalizado!'.PHP_EOL;

Claro que os Webhooks do GitHub podem enviar este valor de forma 'obscura' sem ser via GET, mas isso vai ficar pra outro artigo.

A string a2cfe19e8065517b655f1360ff4625de foi gerada usando echo md5(uniqid(rand()));, você pode fazer como preferir, da até salvar em banco de dados ou criar usuários, vou me manter simples e imaginar que você pode enriquecer este processo sozinho.

Obs.: O PHP_EOL insere uma quebra de linha, ok.

Agora vamos ver os comandos que serão executados.

// Este cabeçalho informa ao navegador que
// isso é um arquivo de texto e quebra
// as linhas sem eu usar <br>
if (php_sapi_name() != "cli") {
    header('Content-Type:text/plain');
}

echo 'INICIANDO DEPLOY'.PHP_EOL;
echo '======='.PHP_EOL;

// uso o passthru para rodar um
// git pull no console do Linux
echo 'Atualizando o repositório'.PHP_EOL;
passthru('git pull');
echo 'OK'.PHP_EOL;
echo '======='.PHP_EOL;

// uso o passthru para rodar um
// rm -rf vendor, removendo meu
// diretório  vendor
echo 'Removendo diretório vendor'.PHP_EOL;
passthru('rm -rf vendor');
echo 'OK'.PHP_EOL;
echo '======='.PHP_EOL;

// instalo as novas atualizações
echo 'Instalando as dependências'.PHP_EOL;
echo passthru('composer install');
echo 'OK'.PHP_EOL;
echo '======='.PHP_EOL;

O comando passthru() executa comandos no terminal do Linux e automatiza as tarefas que precisamos.

Para muitos, o primeiro comando já resolveria (git pull), ou seja, sem remover diretórios vendor e instalar tudo com o Composer, se você não usar isso, não coloque os comandos.

No final teremos algo assim:

<?php

$key = isset($_GET['k']) ? $_GET['k'] : null;
if (is_null($key))
    $key = isset($argv[1]) ? $argv[1] : null;

if ($key == 'a2cfe19e8065517b655f1360ff4625de') {

    if (php_sapi_name() != "cli") {
        header('Content-Type:text/plain');
    }

    echo 'INICIANDO DEPLOY'.PHP_EOL;
    echo '======='.PHP_EOL;

    echo 'Atualizando o repositório'.PHP_EOL;
    passthru('git pull');
    echo 'OK'.PHP_EOL;
    echo '======='.PHP_EOL;

    echo 'Removendo diretório vendor'.PHP_EOL;
    passthru('rm -rf vendor');
    echo 'OK'.PHP_EOL;
    echo '======='.PHP_EOL;

    echo 'Instalando as dependências'.PHP_EOL;
    echo passthru('composer install');
    echo 'OK'.PHP_EOL;
    echo '======='.PHP_EOL;
}

echo 'Deploy finalizado!'.PHP_EOL;

Para usar este arquivo no terminal é muito simples:

php deploy.php a2cfe19e8065517b655f1360ff4625de

Ou via navegador:

http://[url-do-servidor]/deploy.php?k=a2cfe19e8065517b655f1360ff4625de

Mas dependendo da demora no processo você deve receber um erro de timeout no navegador, no console isso não deve acontecer, outra coisa, a maioria dos projetos hoje em dia tem um diretório public (ou webroot, qualquer que seja o nome, vou usar public) com a função de evitar o acesso direto aos arquivos, inclusive ao nosso deploy.php, vamos corrigir estes dois problemas de uma única vez.

Primeiro vamos criar um arquivo dentro de public com o mesmo nome, deploy com o seguinte conteúdo:

<?php

$key = isset($_GET['k']) ? $_GET['k'] : null;

chdir(__DIR__.'/..');

exec('php deploy.php '.$key);

O chdir() diz que tudo o que acontece a partir dele, deve ser interpretado como um diretório a baixo, ou seja, o exec(), o exec() é como o passthru(), porém não precisamos de nenhuma saída de binário, então ele vai funcionar melhor. Mas ainda não resolveu o problema de timeout, preciso que o navegador execute o comando e encerre suas tarefas, assim o servidor vai cuidar de terminar a atualização enquanto o usuário que acessou o arquivo está livre para seguir sua vida e fechar a janela.

Vou alterar a última linha para:

exec('php deploy.php '.$key.' > deploy.log &');

Agora eu também criei um arquivo de log para dizer se algo deu errado ou não e usei um & (e comercial) no final para dizer ao exec() que o navegador não precisa esperar terminar a tarefa, ele pode seguir em frente.

Obs.: Essa dica também é válida quando você precisa executar processos pesados, como exportar relatórios, da até pra voltar no primeiro deploy.php e enviar um email no final, não faça o navegador esperar infinitamente usando "hacks" de aumentar o timeout.

E tem mais, você ainda pode alterar e incrementar este script, tentei deixar o mais simples possível, você pode e deve adaptar para as suas necessidades.

Testado com Laravel 5 e CakePHP 3.

Tem alguma sugestão que fique bacana no nosso deploy? Manda nos comentários.

Fazendo o GitHub avisar o servidor

Para este tipo de tarefa existem os Webhooks, estes caras acessam URLs automaticamente para dizer que tem coisa nova sempre que eu enviar um pacote com git push, vamos configurar?

  • Acesse o repositório do seu projeto e clique em Settings (desenho da roda dentada na lateral direita).
  • Clique no menu Webhooks & Services na nova tela que apareceu (menu esquerdo)
  • Na direção do título Webhooks tem um botão Add webhook, clique e confirme sua senha.
  • Em Payload URL insira a URL para o deploy.php com a chave de segurança, exemplo http://[url-do-servidor]/deploy.php?k=a2cfe19e8065517b655f1360ff4625de

Clique no botão verde com título Add webhook.

Prontinho, ele vai carregar a tela anterior com o seu Webhook inserido e um indicador cinza, atualize (f5 na tela) para ele ficar verde, se ficar vermelhor abra e identifique o problema, você sempre pode dar clicar em um item em Recent Deliveries para ver o erro e clicar em Redeliver para tentar enviar novamente.

Não esqueça que você tem um arquivo de log também (que é criado graças ao nosso DeployHiperBlasterMagicoDaHoraPontoPhp).

Conclusão

Este processo é mais simples e menos custoso para o seu rendimento durante o trabalho que o FTP tradicional, vale investir um tempo para aperfeiçoar o que você viu aqui e aprender Git, teste em outros servidores, como o BitBucket por exemplo. Com mais tempo, conheça Capistrano ou outra ferramenta mais incrementada.

Atualmente eu uso o Capistrano, mas vejo muitas possibilidades para isso, quem sabe uma interface que me ajude a por projetos online sem precisar acessar o servidor!


Cursos relacionados


* Parcelamento apenas cartão de crédito! Pode haver uma pequena variação no parcelamento em relação a simulações apresentadas!