Você pode usar a API REST para unir os commits a um serviço de teste, de modo que cada push que você efetuar possa ser testado e representado em uma solicitação de pull do GitHub Enterprise Cloud. Para obter mais informações sobre pontos de extremidade relevantes, confira Pontos de extremidade da API REST para status de commits.
Este guia usará a API para demonstrar uma configuração que você pode usar. No nosso cenário, iremos:
- Executar o nosso conjunto de CI quando um pull request for aberto (iremos definir o status de CI como pendente).
- Quando o CI terminar, definiremos o status do pull request.
O nosso sistema de CI e servidor de hospedagem serão imaginários. Eles podem ser o Travis, o Jenkins 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 aprenda como usá-lo. Ele é uma ferramenta muito útil para expor aplicativos locais à Internet.
Note
Como alternativa, é possível usar o encaminhamento de webhook a fim de configurar o ambiente local para receber webhooks. Para saber mais, 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:
- Status
- Pull Request
Esses são os eventos que o GitHub Enterprise Cloud enviará ao nosso servidor sempre que ocorrer a ação relevante. Vamos atualizar nosso servidor para apenas lidar com o cenário de solicitação de pull agora:
post '/event_handler' do
@payload = JSON.parse(params[:payload])
case request.env['HTTP_X_GITHUB_EVENT']
when "pull_request"
if @payload["action"] == "opened"
process_pull_request(@payload["pull_request"])
end
end
end
helpers do
def process_pull_request(pull_request)
puts "It's #{pull_request['title']}"
end
end
O que está havendo? Cada evento que o GitHub Enterprise Cloud envia anexa um cabeçalho HTTP X-GitHub-Event
. Por enquanto, nos importaremos apenas com os eventos do PR. Daí em diante, usaremos o conteúdo das informações e retornaremos o campo de título. Em um cenário ideal, nosso servidor ficará preocupado com a atualização de cada solicitação de pull, não apenas quando ela é aberta. Isso asseguraria que todos os novos pushes passassem pelos testes de CI.
Mas, para essa demonstração, nós nos preocuparemos quando ela for aberta.
Para testar esta prova de conceito, faça algumas alterações em um branch no repositório de teste e abra uma solicitação de pull. Seu servidor deve responder de acordo!
Trabalhar com status
Com o servidor implementado, estamos prontos para iniciar nosso primeiro requisito, que é configurar (e atualizar) os status de CI. Observe que, a qualquer momento, você pode clicar em Entregar novamente para enviar o mesmo conteúdo. Não há necessidade de fazer uma nova solicitação de pull toda vez que você faz uma alteração.
Como estamos interagindo com os dados da API do GitHub, usaremos o Octokit.rb para gerenciar as interações. Esse cliente será configurado com um personal access token:
# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
# Instead, set and test environment variables, like below
ACCESS_TOKEN = ENV['MY_PERSONAL_TOKEN']
before do
@client ||= Octokit::Client.new(:access_token => ACCESS_TOKEN)
end
Em seguida, apenas precisaremos atualizar a solicitação de pull no GitHub Enterprise Cloud para deixar claro que estamos fazendo o processamento na CI:
def process_pull_request(pull_request)
puts "Processing pull request..."
@client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'pending')
end
Aqui, estamos fazendo três coisas muito básicas:
- Estamos procurando o nome completo do repositório
- Estamos procurando o último SHA da pull request
- Estamos definindo o status como "pendente"
É isso! Daí em diante, você pode executar qualquer processo de que precise para executar o conjunto de testes. Talvez você transmita seu código para o Jenkins ou ligue para outro serviço Web por meio da API, como o Travis. Em seguida, lembre-se de atualizar o status novamente. Em nosso exemplo, vamos apenas defini-lo como "success"
:
def process_pull_request(pull_request)
@client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'pending')
sleep 2 # do busy work...
@client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'success')
puts "Pull request processed!"
end
Conclusão
No GitHub, usamos uma versão do Janky para gerenciar a CI por vários anos. O fluxo básico é essencialmente o mesmo que o servidor que construímos acima. No GitHub, nós:
- Notificamos tudo ao Jenkins quando um pull request é criado ou atualizado (via Janky)
- Esperamos por uma resposta no estado da CI
- Se o código for verde, fazemos o merge do pull request
Toda esta comunicação é canalizada de volta para nossas salas de bate-papo. Você não precisa criar sua configuração de CI para usar este exemplo. Você sempre pode contar com as integrações do GitHub.