Skip to main content

Entregar implantações

Ao usar a API RESt de implantações, você pode criar ferramentas personalizadas que interagem com seu servidor e um aplicativo de terceiros.

Você pode usar a API REST para implantar seus projetos hospedados no GitHub em um servidor do qual é o proprietário. Para obter mais informações sobre os pontos de extremidade usados para gerenciar as implantações e os status, confira "Pontos de extremidade da API REST para implantações". Use também a API REST para coordenar suas implantações no momento em que o código chega ao branch padrão. Para obter mais informações, confira "Criar um servidor de CI".

Este guia usará a API REST para demonstrar uma configuração que você pode usar. No nosso cenário, iremos:

  • Fazer merge de um pull request.
  • Quando a CI terminar, definiremos o status do pull request.
  • Quando o pull request for mesclado, executaremos a nossa implantação no nosso servidor.

O nosso sistema de CI e servidor de hospedagem serão imaginários. Eles podem ser o Heroku, o Amazon ou qualquer outro completamente diferente. O aspecto fundamental deste guia será configurar o servidor que gerencia a comunicação.

Caso ainda não tenha feito isso, baixe o ngrok e saiba como usá-lo. Ele é uma ferramenta muito útil para expor aplicativos locais à Internet.

Observação: como alternativa, é possível usar o encaminhamento de webhook a fim de configurar o ambiente local para receber webhooks. Para obter mais informações, confira "Usando a CLI do GitHub para encaminhar webhooks para teste".

Observação: baixe o código-fonte completo deste projeto no repositório platform-samples.

Escrever o seu servidor

Vamos escrever um aplicativo rápido do Sinatra para provar que nossas conexões locais estão funcionando. Vamos começar com isso:

require 'sinatra'
require 'json'

post '/event_handler' do
  payload = JSON.parse(params[:payload])
  "Well, it worked!"
end

(Se você não estiver familiarizado com o funcionamento do Sinatra, recomendamos ler o guia do Sinatra).

Inicie este servidor. Como o Sinatra é iniciado na porta 4567 por padrão, é recomendado configurar o ngrok para começar a ouvir nessa porta também.

Para que esse servidor funcione, precisamos configurar um repositório com um webhook. O webhook deve ser configurado para ser acionado sempre que um pull request for criado ou mesclado.

Vá em frente e crie um repositório com o qual você esteja confortável para fazer testes. Podemos sugerir o repositório do Spoon/do Knife de @octocat?

Depois disso, você criará um webhook no repositório, alimentando-o com a URL que foi fornecida por ngrok e escolhendo application/x-www-form-urlencoded como o tipo de conteúdo.

Clique em Atualizar webhook. Você verá a resposta de corpo Well, it worked!. Ótimo! Clique em Deixe-me selecionar eventos individuais e selecione o seguinte:

  • Implantação
  • Status da Implantação
  • Pull Request

Esses são os eventos que o GitHub enviará ao nosso servidor sempre que ocorrer a ação relevante. Vamos configurar nosso servidor para apenas tratar as solicitações de pull quando elas forem mescladas agora:

post '/event_handler' do
  @payload = JSON.parse(params[:payload])

  case request.env['HTTP_X_GITHUB_EVENT']
  when "pull_request"
    if @payload["action"] == "closed" && @payload["pull_request"]["merged"]
      puts "A pull request was merged! A deployment should start now..."
    end
  end
end

O que está havendo? Cada evento que o GitHub envia anexa um cabeçalho HTTP X-GitHub-Event. Por enquanto, nos importaremos apenas com os eventos do PR. Quando uma solicitação de pull for mesclada (o estado dela é closed, e merged é true), iniciaremos uma implantação.

Para testar esta prova de conceito, faça algumas alterações em um branch no repositório de teste, abra uma solicitação de pull e mescle-a. Seu servidor deve responder de acordo!

Trabalhando com implantações

Como o servidor implementado, o código sendo revisado e a solicitação de pull mesclada, queremos implantar nosso projeto.

Vamos começar modificando o ouvinte de evento para processar as solicitações de pull quando elas forem mescladas e começar a prestar atenção às implantações:

when "pull_request"
  if @payload["action"] == "closed" && @payload["pull_request"]["merged"]
    start_deployment(@payload["pull_request"])
  end
when "deployment"
  process_deployment(@payload)
when "deployment_status"
  update_deployment_status
end

Com base nas informações da solicitação de pull, começaremos preenchendo o método start_deployment:

def start_deployment(pull_request)
  user = pull_request['user']['login']
  payload = JSON.generate(:environment => 'production', :deploy_user => user)
  @client.create_deployment(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], {:payload => payload, :description => "Deploying my sweet branch"})
end

As implantações podem ter alguns metadados anexados na forma de um payload e uma description. Embora esses valores sejam opcionais, é útil usá-los para registrar em log e representar informações.

Quando uma nova implantação é criada, um evento completamente separado é acionado. É por isso que temos um novo caso switch no manipulador de eventos para deployment. Você pode usar essas informações para receber uma notificação de quando uma implantação é disparada.

As implantações podem demorar um pouco. Portanto, vamos precisar ouvir vários eventos, como quando a implantação foi criada e em que estado se ela encontra.

Vamos simular uma implantação que realiza um trabalho e observar o efeito que ela tem na saída. Primeiro, vamos concluir o método process_deployment:

def process_deployment
  payload = JSON.parse(@payload['payload'])
  # you can send this information to your chat room, monitor, pager, etc.
  puts "Processing '#{@payload['description']}' for #{payload['deploy_user']} to #{payload['environment']}"
  sleep 2 # simulate work
  @client.create_deployment_status("repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}", 'pending')
  sleep 2 # simulate work
  @client.create_deployment_status("repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}", 'success')
end

Por fim, vamos fazer a simulação do armazenamento da informação de status como a saída do console:

def update_deployment_status
  puts "Deployment status for #{@payload['id']} is #{@payload['state']}"
end

Vamos dividir o que está acontecendo. Uma nova implantação é criada por start_deployment, que dispara o evento deployment. Nele, chamamos process_deployment para simular o trabalho que está sendo feito. Durante esse processamento, também fazemos uma chamada a create_deployment_status, que permite que um destinatário saiba o que está acontecendo, à medida que alternamos o status para pending.

Após a conclusão da implantação, definimos o status como success.

Conclusão

No GitHub, há vários anos, usamos uma versão do Heaven para gerenciar nossas implantações. Um fluxo comum é essencialmente o mesmo que o servidor que criamos acima:

  • Aguarde uma resposta sobre o estado das verificações de CI (sucesso ou falha)
  • Se as verificações forem bem-sucedidas, faça o merge do pull request
  • Heaven toma o código mesclado e o implementa nos servidores de teste e produção
  • Enquanto isso, o Heaven também notifica todos sobre o build por meio do Hubot sentado nas salas de chat

É isso! Você não precisa criar sua própria configuração de implantação para usar este exemplo. Você sempre pode contar com as integrações do GitHub.