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.