Puedes usar la API REST para implementar los proyectos hospedados en GitHub en un servidor de su propiedad. Para obtener más información sobre los puntos de conexión para administrar implementaciones y estados, consulta "Puntos de conexión de la API de REST para implementaciones". También puedes usar la API REST podrá coordinar tus implementaciones en el momento en que el código llegue a la rama predeterminada. Para obtener más información, vea «Crear un servidor de IC».
Esta guía utilizará la API REST para demostrar una configuración que puedes utilizar. En nuestro escenario, nosotros:
- Fusionamos una solicitud de cambios.
- Cuando finaliza la IC, configuramos el estado de la solicitud de extracción según corresponda.
- Cuando se fusiona la solicitud de extracción, ejecutamos nuestro despliegue en nuestro servidor.
Nuestro sistema de IC y nuestro servidor host serán imaginarios. Podrían ser Heroku, Amazon, o algo completamente diferente. El meollo de esta guía será configurar y ajustar el servidor que administra la comunicación.
Si todavía no lo has hecho, asegúrate de descargar ngrok
y aprende a usarlo. Consideramos que es una herramienta muy útil para exponer las aplicaciones locales a Internet.
Nota: Como alternativa, puedes usar el reenvío de webhooks para configurar el entorno local para recibir webhooks. Para obtener más información, vea «Uso de la CLI de GitHub para reenviar webhooks para pruebas».
Nota: Puede descargar el código fuente completo de este proyecto desde el repositorio platform-samples.
Escribir tu servidor
Escribiremos una app de Sinatra rápidamente para probar que nuestras conexiones locales estén funcionando. Comencemos con esto:
require 'sinatra'
require 'json'
post '/event_handler' do
payload = JSON.parse(params[:payload])
"Well, it worked!"
end
(Si no está familiarizado con el funcionamiento de Sinatra, le recomendamos leer la guía de Sinatra).
Inicia este servidor. De manera predeterminada, Sinatra comienza en el puerto 4567
, por lo que también tendrás que configurar ngrok
para que empiece a escuchar en este puerto.
Para que este servidor funcione, necesitaremos configurar un repositorio con un webhook. El webhook debe configurarse para que se active cada que se crea o fusiona una solicitud de extracción.
Sigue adelante y crea un repositorio en el que quieras hacer tus experimentos. ¿Podríamos sugerir el repositorio de cucharas y cuchillos de @octocat?
Después de esto, crearás un webhook en el repositorio, le suministrarás la dirección URL que te ha proporcionado ngrok
y seleccionarás application/x-www-form-urlencoded
como el tipo de contenido.
Haga clic en Update Webhook. Debería ver una respuesta del cuerpo de Well, it worked!
.
Magnífico. Haga clic en Let me select individual events (Dejarme seleccionar eventos individuales) y seleccione lo siguiente:
- Implementación
- Estado de implementación
- Solicitud de incorporación de cambios
Estos son los eventos que GitHub enviará al servidor cuando se produzca cualquier acción relevante. Ahora configuraremos nuestro servidor para que solo controle cuándo se fusionan las solicitudes de incorporación de cambios:
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
¿Qué sucede? En cada evento que envía GitHub se adjunta un encabezado HTTP X-GitHub-Event
. Solo nos interesan los eventos de Solicitud de Extracción por el momento. Al fusionar una solicitud de incorporación de cambios (su estado es closed
, y merged
es true
), se iniciará una implementación.
Para probar esta prueba de concepto, realice algunos cambios en una rama del repositorio de pruebas, abra una solicitud de incorporación de cambios y fusiónlea mediante combinación. ¡Tu servidor deberá responder de acuerdo con los casos!
Trabajar con despliegues
Ahora que ya tenemos nuestro servidor configurado, el código se está revisando y nuestras solicitudes de incorporación de cambios se han fusionado, queremos implementar nuestro proyecto.
Comenzaremos modificando nuestro cliente de escucha de eventos para que procese las solicitudes de incorporación de cambios cuando estas se fusionen, y prestaremos atención a las implementaciones:
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
Basándonos en la información de la solicitud de incorporación de cambios, comenzaremos rellenando el 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
Las implementaciones pueden tener algunos metadatos adjuntos en forma de payload
y description
. Aunque estos valores son opcionales, es de gran ayuda usarlos para registrar y representar la información.
Cuando se crea un despliegue nuevo, se activa un evento completamente separado. Por eso tenemos un nuevo caso switch
en el controlador de eventos para deployment
. Puede usar esta información para que se le notifique cuando se desencadene una implementación.
Las implementaciones pueden tardar mucho tiempo, por lo que queremos escuchar varios eventos, como cuándo se crea la implementación y en qué estado se encuentra.
Vamos a simular una implementación que realiza algunas acciones y observaremos el efecto que tiene en la salida. En primer lugar, vamos a completar nuestro 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 último, estimularemos el almacenamiento de la información de los estados como una salida de la consola:
def update_deployment_status
puts "Deployment status for #{@payload['id']} is #{@payload['state']}"
end
Bamos a explicar lo que está pasando. start_deployment
crea una nueva implementación, la cual desencadena el evento deployment
. Desde allí, llamamos a process_deployment
para simular las acciones que están sucediendo. Durante ese procesamiento, también llamamos a create_deployment_status
, que permite a un receptor saber lo que está ocurriendo mientras cambiamos el estado a pending
.
Una vez finalizada la implementación, establecemos el estado como success
.
Conclusión
En GitHub, hemos usado una versión de Heaven para administrar nuestras implementaciones durante años. Un flujo común es esencialmente el mismo que en el servidor que hemos creado anteriormente:
- Espera obtener una respuesta con base en el estado de las verificaciones de IC (éxito o fallo)
- Si las verificaciones requeridas tienen éxito, fusiona la solicitud de cambios
- Heaven toma el código fusionado y lo despliega a los servidores de prueba y producción
- Mientras tanto, Heaven también notifica a todos sobre la compilación mediante el uso de Hubot en nuestras salas de chat.
Eso es todo. No necesitas crear tu propia configuración de despliegue para utilizar este ejemplo. Siempre puede recurrir a las integraciones de GitHub.