Lo primero que tengo que dear claro sobre el tema es que odio el desarrollo web. Solo hay que ver el estado de esta página web, si fuese más viejuna tendría que meterle gifs de bebes satánicos bailando. Supongo que es por que las veces que me he tenido que pelear con el tema, he tenido que tocar mucho de la parte visual, lo que seria el diseño y el CSS. Y yo tengo un gusto horrible.
Por ello, cuando quise hacer uso de los webhooks que ofrece gitea pensé que odiaría hacerlo posible. Por suerte me encontré con Flask, que viene a ser un django usable por gente que no quiere dedicarle mucho tiempo al desarrollo. Una vez instalado el paquete mediante un pip install flask
, hacer un “Hola Mundo!” es tan simple como esto:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hola Mundo!"
Cuando se ejecute el anterior script, dirá que hay un servidor web activo en el puerto 5000, si le hacemos un curl nos devolverá el querido “Hola Mundo”.
La aplicación que hice es la siguiente. Está en python 2 por que quería usar xmpppy, que es más simple de usar que Sleekxmpp o Slixmpp. Estas librerías son mucho más robustas, pero mantener la conexión asíncrona era complicarse más de lo que quería. Así que una conexión por visita y ale:
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import xmpp
import json
from flask import Flask, request
import ConfigParser
app = Flask(__name__)
@app.route('/')
def root():
return redirect("/webhook", code=302)
@app.route("/webhook", methods=['POST'])
def webhook():
# read config file
config = ConfigParser.ConfigParser()
config.read('config.ini')
username = config.get('xmpp', 'username', 0)
password = config.get('xmpp', 'password', 0)
server = config.get('xmpp', 'server', 0)
room = config.get('xmpp', 'room', 0)
nick = config.get('xmpp', 'nick', 0)
secret = config.get('gitea', 'secret', 0)
# TODO: comprobar si funciona sin secret
if not secret:
secret = ''
if request.json['secret'] == secret:
data = json.loads(request.data.decode('utf-8'))
message = ''
print(data)
# commit
if 'compare_url' in data.keys():
message = data['pusher']['username'] + ' has pushed some changes:'\
+ ' ' + data['compare_url']
# pull request open
elif data['action'] == 'opened':
message = data['sender']['username'] + ' opened a new pull reques'\
+ 't: ' + data['pull_request']['html_url']
# close pull request
elif data['action'] == 'closed':
message = data['sender']['username'] + ' closed a pull request: ' \
+ data['pull_request']['html_url']
# reopen pull request
elif data['action'] == 'reopened':
message = data['sender']['username'] + ' reopened a pull request:'\
+ ' ' + data['pull_request']['html_url']
# add label
elif data['action'] == 'label_updated':
f_tag = ""
for tag in data['pull_request']['labels']:
f_tag += '[' + tag['name'] + '] '
message = data['sender']['username'] + ' changed the labels ' \
'of a pull request: ' + f_tag + \
data['pull_request']['html_url']
# delete label
elif data['action'] == 'label_cleared':
message = data['sender']['username'] + ' deleted all the labels ' \
'of a pull request: ' + data['pull_request']['html_url']
if message:
client = xmpp.Client(server, debug=[])
client.connect()
client.auth(username, password, 'gitea')
# send join
client.send(xmpp.Presence(to="%s/%s" % (room, nick)))
msg = xmpp.protocol.Message(body=message)
msg.setTo(room)
msg.setType('groupchat')
client.send(msg)
presence = xmpp.Presence(to=room)
presence.setAttr('type', 'unjavailable')
client.send(presence)
return ":)"
if __name__ == "__main__":
app.run()
Este fichero debería guardarse como gitea-sendxmpp.py
. No hay mucho que comentar. Las peticiones que se hagan a la raíz se re-envían a /webhook
, por lo que se puede apuntar directamente ahí. Leerá el archivo de configuración (que veremos a continuación), y si el secreto cuadra con el de la configuración pardeará el mensaje y enviará un mensaje a la sala de jabber que se le diga.
El archivo de configuración a rellenar es el siguiente:
[xmpp]
username =
password =
server =
room =
nick =
[gitea]
secret =
Y por último, la forma correcta de ejecutar aplicaciones web en python es usar uwsgi
. Un ejemplo simple de configuración es el siguiente:
[uwsgi]
chdir = /var/www/gitea-sendxmpp/
module = gitea-sendxmpp:app
master = true
processes = 1
threads = 2
uid = www-data
gid = www-data
socket = /tmp/gitea-sendxmpp.sock
chmod-socket = 777
die-on-term = true
Ya solo queda configurar nginx para que actué como un proxy inverso (algo sumamente recomendable por temas de codificación, eficiencia, seguridad, …) del siguiente modo:
server {
listen 80;
server_name daemons.it;
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/gitea-sendxmpp.sock;
}
}
Lo más importante es que el socket uwsgi_pass
sea el mismo tanto en la configuración de uwsgi
como de nginx.
Para ver la versión más reciente, en caso de que la modifique, se puede visitar el repositorio.