Como fazer deploy automático com Bitbucket

Neste artigo, vou mostrar como fazer um deploy automático de um site em um servidor, no meu caso na DreamHost, usando a funcionalidade Webhocks do Bitbucket.

Como funciona

  • Você faz commit e push para seu repositório no Bitbucket
  • O Bitbucket fará um POST na URL que você configurou de acordo com algumas regras
  • Esse POST acionará o script de deploy que fará pull das alterações em seu servidor

Preparando ambiente

Primeiramente você precisará da chave pública do seu servidor para poder se comunicar com o Bitbucket usando SSH sem senha. Esta chave geralmente fica no caminho:

/home/user/.ssh/id_rsa.pub

Caso a pasta .ssh não exista no diretório home do usuário, basta executar o comando ssh-keygen.

Copie o conteúdo do arquivo id_rsa.pub e adicione no seu repositório em Settings > Deployment Keys.

Clonando repositório

Na DreamHost, os arquivos do seu site ficam no diretório /home/user/meudominio.com/. Logo, para clonar oprojeto nesta pasta exitem duas formas: (1) clonando diretamente nesse diretório ou (2) clonando em outro lugar qualquer e fazer um link simbólico para o caminho desejado. Para isso, execute os comandos abaixo:

# Garante que está na raiz do usuário
cd ~

# Remove pasta do site atual
# Isso deletará todos os arquivos do seu domínio, 
#   então tome cuidado
rm -rf meudominio.com

# (1) Para clonar diretamente
git clone [email protected]:usuario/projeto.git meudominio.com

# OU (2) Com link simbólico
git clone [email protected]:usuario/projeto.git
ln -s meuprojeto/caminho/para/arquivos/ meudominio.com

Script de deploy

Para fazer toda a mágica, precisamos de um script que execute os comandos desejados no git. Para isso, usei o seguinte PHP:

<?php
/**
 * Retirado de http://brandonsummers.name/blog/2012/02/10/using-bitbucket-for-automated-deployments/
 */
date_default_timezone_set('America/Sao_Paulo');

class Deploy {

  /**
   * A callback function to call after the deploy has finished.
   *
   * @var callback
   */
  public $post_deploy;

  /**
   * The name of the file that will be used for logging deployments. Set to
   * FALSE to disable logging.
   *
   * @var string
   */
  private $_log = 'deployments.log';

  /**
   * The timestamp format used for logging.
   *
   * @link    http://www.php.net/manual/en/function.date.php
   * @var     string
   */
  private $_date_format = 'd-m-Y H:i:sP';

  /**
   * The name of the branch to pull from.
   *
   * @var string
   */
  private $_branch = 'master';

  /**
   * The name of the remote to pull from.
   *
   * @var string
   */
  private $_remote = 'origin';

  /**
   * The directory where your website and git repository are located, can be
   * a relative or absolute path
   *
   * @var string
   */
  private $_directory;

  /**
   * Sets up defaults.
   *
   * @param  string  $directory  Directory where your website is located
   * @param  array   $data       Information about the deployment
   */
  public function __construct($directory, $options = array())
  {
    // Determine the directory path
    $this->_directory = realpath($directory).DIRECTORY_SEPARATOR;

    $available_options = array('log', 'date_format', 'branch', 'remote');

    foreach ($options as $option => $value)
    {
      if (in_array($option, $available_options))
      {
        $this->{'_'.$option} = $value;
      }
    }

    $this->log('Attempting deployment...');
  }

  /**
   * Writes a message to the log file.
   *
   * @param  string  $message  The message to write
   * @param  string  $type     The type of log message (e.g. INFO, DEBUG, ERROR, etc.)
   */
  public function log($message, $type = 'INFO')
  {
    if ($this->_log)
    {
      // Set the name of the log file
      $filename = $this->_log;

      if ( ! file_exists($filename))
      {
        // Create the log file
        file_put_contents($filename, '');

        // Allow anyone to write to log files
        chmod($filename, 0666);
      }

      // Write the message into the log file
      // Format: time --- type: message
      file_put_contents($filename, date($this->_date_format).' --- '.$type.': '.$message.PHP_EOL, FILE_APPEND);
    }
  }

  /**
   * Executes the necessary commands to deploy the website.
   */
  public function execute()
  {
    try
    {
      // Make sure we're in the right directory
      exec('cd '.$this->_directory, $output);
      $this->log('Changing working directory... '.implode(' ', $output));

      // Discard any changes to tracked files since our last deploy
      exec('git reset --hard HEAD', $output);
      $this->log('Reseting repository... '.implode(' ', $output));

      // Update the local repository
      exec('git pull '.$this->_remote.' '.$this->_branch, $output);
      $this->log('Pulling in changes... '.implode(' ', $output));

      // Secure the .git directory
      exec('chmod -R og-rx .git');
      $this->log('Securing .git directory... ');

      if (is_callable($this->post_deploy))
      {
        call_user_func($this->post_deploy, $this->_data);
      }

      $this->log('Deployment successful.');
    }
    catch (Exception $e)
    {
      $this->log($e, 'ERROR');
    }
  }

}

$deploy = new Deploy('/meuprojeto');
$deploy->execute();

Quando executado, ele faz pull do seu repositório, sendo possível realizar algumas configurações como de qual branch usar.

Adicione esse script em um arquivo PHP, por exemplo deploy.php, e coloque no seu projeto de forma que ele fique disponível de acessar na forma http://meudominio.com/deploy.php.

Configurando webhook

Agora vá até as configurações do seu repositório e configure o Webhook em Settings > Webhook > Add webhook. Defina um título pra ele, na URL insira o caminho do arquivo PHP http://meudominio.com/deploy.php e defina quando ele deve ser invocado. O padrão é ser invocado sempre que houver um push no repositório.

Terminado essa configuração, tudo deve estar funcionando. Para testar, faça push de alguma alteração e verifique se o script foi executando olhando o arquivo deployments.log:

06-05-2016 17:12:24-03:00 --- INFO: Attempting deployment...
06-05-2016 17:12:24-03:00 --- INFO: Changing working directory... 
06-05-2016 17:12:24-03:00 --- INFO: Reseting repository... HEAD is now at e1d5723 Landing page
06-05-2016 17:12:25-03:00 --- INFO: Pulling in changes... HEAD is now at e1d5723 Landing page Already up-to-date.
06-05-2016 17:12:25-03:00 --- INFO: Securing .git directory... 
06-05-2016 17:12:25-03:00 --- INFO: Deployment successful.

Finalizando

Agora tudo está configurado e (deveria estar) funcionando, mas caso tenha algum problema deixe um comentário abaixo que tentarei lhe ajudar na medida do possível.

Este tutorial foi baseado no artigo Using Bitbucket for Automated Deployments.

Deixe uma resposta

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