Rails: Páginas de manutenção com Capistrano


English Version (Google Translate)

Já tem um tempinho que quero falar sobre Capistrano em um material mais completo, o problema é que ando muito ocupado então só sobrou tempo para pequenas dicas.

Na semana passada capistrano 2.5 foi lançado e Jamis Buck anúncio que não será mais o mantenedor deste projeto que praticamente se tornou a forma padrão de colocar sistemas e websites Rails online. Nem por isso o projeto está morto e você precisa achar que deve encontrar outra ferramenta, por ser OpenSource e no github já temos gente a frente do projeto para continuar o trabalho de Jamis.

A dica de hoje será sobre as formas de utilizar uma feature muito bacana do Capistrano, cap deploy:web:disable. Em várias situações você vai precisar tirar do ar seu sistema ou website e não é interessante ter nenhum outro link acessível, só remover a index não resolve pois pode gerar problemas enormes se um cliente acessar o sistema quando ele estiver em manutenção e depois perder os dados.

Para isso o Capistrano tem o comando que pode ser chamado pelo terminal cap deploy:web:disable que gera um página maintenance.html e a copia para a public do seu projeto. Mas o Capistrano não pode fazer mágica, seu servidor web precisa saber que caso existe esta página ele deve rotear todas as requisições para esta página.

Então vamos ao passo a passo, considerando que você já está com sua receita capistrano configurada devidadamente e o deploy do seu projeto é feito com Capistrano mas um cliente te liga e manda tirar o site do ar por alguns dias, você vai ao terminal e digita:

1
2

macbook:meu_projeto daniellopes$ cap:deploy:web:disable
Agora na pasta shared/system do seu projeto Rails no seu host terá uma arquivo de manutenção com o conteúdo semelhante a imagem abaixo:

Agora você precisa avisar a seu webserver para rotear tudo para a maintenance.html quando a página existir. Vamos usar Apache, abra o .htacess do seu projeto e então:

RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /system/maintenance.html [L]

Usamos as regras acima pois o Capistrano por padrão cria o arquivo maintenance na pasta compartilhada system dentro da shared no seu servidor. Feito isto tudo já deve estar funcionando e ao acessar a url do projeto você verá uma tela de manutenção.

Mas nós somos brasileiros e precisamos traduzir a coisa. Abra o arquivo deploy.rb dentro da pasta config do seu projeto e então reescreva a task disable, como abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

namespace :deploy do
  namespace :web do
    task :disable, :roles => :web do
      on_rollback { rm "#{shared_path}/system/maintenance.html" }

      require 'erb'
      deadline, reason = ENV['UNTIL'], ENV['REASON']
      maintenance = ERB.new(File.read("./app/views/layouts/maintenance.html.erb")).result(binding)

      put maintenance, "#{shared_path}/system/maintenance.html", :mode => 0644
    end
  end
end

O que estamos fazendo acima é dizer que agora a task disable ( que está dentro dos namespaces web e deploy ) irá gerar o maintenance.html com base em um arquivo maintenance.html.erb dentro da pasta layout do nosso projeto. Desta forma podemos criar um arquivo erb com o nosso layout além de traduzido.

Também repare que temos duas variáveis, na linha:
deadline, reason = ENV['UNTIL'], ENV['REASON']

Por padrão a task deploy:web:disable aceita duas variáveis de ambiente, UNTIL e REASON que é o horário e o motivo do site estar off-line. Entendido isso, podemos fazer nosso template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>Site em manuten&ccedil;&atilde;o</title>

  <style type="text/css">
    div.outer {
      position: absolute;
      left: 50%;
      top: 50%;
      width: 500px;
      height: 300px;
      margin-left: -260px; 
      margin-top: -150px;
    }

    .DialogBody {
      margin: 0;
      padding: 10px;
      text-align: left;
      border: 1px solid #ccc;
      border-right: 1px solid #999;
      border-bottom: 1px solid #999;
      background-color: #fff;
    }

    body { background-color: #fff; }
  </style>
</head>

<body>

  <div class="outer">
     <div class="DialogBody" style="text-align: center;">
       <div style="text-align: center; width: 200px; margin: 0 auto;">
         <p style="color: red; font-size: 16px; line-height: 20px;">
           Este website est&aacute; fora do ar <%= reason ? reason : "maintenance" %>
           a partir de <%= Time.now.strftime("%H:%M %Z") %>.
         </p>
         <p style="color: #666;">
           N&oacute;s voltaremos <%= deadline ? deadline : "o quanto antes poss&iacute;vel" %>.
         </p>
       </div>
     </div>
  </div>

</body>
</html>

Agora basta executar:

1
2

macbook:meu_projeto daniellopes$ cap:deploy:web:disable REASON="atualizando o estoque" UNTIL="15:00"

E quando estiver tudo ok com o sistema basta fazer o inverson:

1
2

macbook:meu_projeto daniellopes$ cap:deploy:web:enable

Simples, não? Agora temos nossa tela de manutenção personalizada e traduzida. Também seria possível utilizar a API de internacionalização do Rails mas como é uma tarefa razoavelmente simples não vou explicar aqui.

Mais uma vez, meus agradecimentos ao Jamis Buck por esta excelente ferramenta.


Comentário