buildbot-transifex/buildbot_transifex/webhook.py

218 lines
8.1 KiB
Python

import sys
import os
import base64
import time
import json
import re
import hmac
import pprint
import hashlib
import requests
from subprocess import call
from base64 import b64encode
from buildbot.process.properties import Properties
from buildbot.util import bytes2unicode, unicode2bytes
from buildbot.www.hooks.base import BaseHookHandler
from twisted.internet import defer
from twisted.python import log
from dateutil.parser import parse as dateparse
_HEADER_USER_AGENT = 'User-Agent'
_HEADER_SIGNATURE = 'X-TX-Signature'
_EVENT_KEY = 'event'
transifexSecret = ""
transifexUsername = ""
transifexPassword = ""
transifex_dict = {}
secret = ""
master = ""
gitHubToken = os.environ('gitHubToken')
class TransifexHandler(BaseHookHandler):
def __init__(self, master, secret, transifex_dict):
self.secret = secret
self.master = master
self.transifex_dict = transifex_dict
def returnMessage(self, status = False, message = "Unimplemented"):
output = json.dumps({"status": "OK" if status else "FAIL", "message": message})
return [output, [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))
]]
def verifyTransifexSignature(
self, request, content, rendered_secret, signature, header_signature
):
http_verb = 'POST'
http_url_path = request.headers('X-TX-Url')
http_gmt_date = request.headers('Date')
content_md5 = hashlib.md5(content).hexdigest()
msg = b'\n'.join([
http_verb, http_url_path, http_gmt_date, content_md5
])
tx_signature = base64.b64encode(
hmac.new(
key=rendered_secret,
msg=msg,
digestmod=hashlib.sha256
).digest()
)
if tx_signature() != header_signature:
raise ValueError('Invalid secret')
try:
if signature != os.environ.get('HTTP_X_TX_SIGNATURE'):
return False
return True
except:
return False
def downloadTranslatedLanguage(self, ts, lang):
headers = {"Authorization": "Basic " + b64encode(transifexUsername + ":" + transifexPassword)}
fname = "bitmessage_" + lang.lower() + ".po"
with open("src/translations/" + fname, "wt") as handle:
response = requests.get("https://www.transifex.com/api/2/project/pybitmessage/resource/pybitmessage/translation/" + lang + "/",
headers=headers)
if response.ok:
content = json.loads(response.content)["content"]
handle.write(content.encode("utf-8"))
return response
def commitTranslatedLanguage(self, ts, lang):
call(["kivy", "src/translations/messages.pro"])
call(["git", "add", "src/translations/bitmessage_" + lang + ".ts", "src/translations/bitmessage_" + lang + ".qm"])
call(["git", "commit", "-q", "-S", "-m", "Auto-updated language %s from transifex" % (lang)])
newbranch = "translate_" + lang + "_" + str(ts)
call(["git", "push", "-q", "translations", newbranch + ":" + newbranch])
branch = transifex_dict['branch']
request = {
"title": "Translation update " + lang,
"body": "Auto-updated from transifex",
"head": "PyBitmessageTranslations:" + newbranch,
"base": branch
}
headers = {"Authorization": "token " + gitHubToken}
response = requests.post("https://api.github.com/repos/Bitmessage/PyBitmessage/pulls",
headers=headers, data=json.dumps(request))
return response
def process_translation_completed(self, payload, transifex_dict, event_type, codebase):
changes = []
transifex_response = self._transform_variables(payload, transifex_dict)
if 'pybitmessage-test' in transifex_response['project'] and 'messagespot' in transifex_response['resource']:
if 'translation_completed' in transifex_response['event'] and 100 in transifex_response['translated']:
ts = int(time.time())
lang = transifex_response['language']
branch = transifex_dict['branch']
self.downloadTranslatedLanguage(ts, lang.lower())
response = self.commitTranslatedLanguage(ts, lang.lower())
if response.ok:
output, responseHeaders = self.returnMessage(True, "Processed.")
else:
output, responseHeaders = self.returnMessage(False, "Error: %i." % (response.status_code))
else:
output, responseHeaders = self.returnMessage(False, "Nothing to do")
else:
output, responseHeaders = self.returnMessage(False, "Nothing to do")
# if isinstance(self.options, dict) and self.options.get('onlyIncludePushCommit', False):
# commits = commits[:1]
# for commit in commits:
# files = []
# for kind in ('added', 'modified', 'removed'):
# files.extend(commit.get(kind, []) or [])
# timestamp = dateparse(commit['timestamp'])
# change = {
# 'author': '{} <{}>'.format(commit['author']['name'],
# commit['author']['email']),
# 'files': files,
# 'comments': commit['message'],
# 'revision': commit['id'],
# 'when_timestamp': timestamp,
# 'branch': branch,
# 'revlink': commit['url'],
# 'repository': repo_url,
# 'project': project,
# 'category': event_type,
# 'properties': {
# 'event': event_type,
# 'repository_name': repository['name'],
# 'owner': repository["owner"]["username"]
# },
# }
# if codebase is not None:
# change['codebase'] = codebase
# changes.insert(0, change)
return changes
def process_review_completed(self, payload, transifex_data):
pass
def _transform_variables(self, payload, transifex_dict):
transifex_variables = {
'project': payload['project'],
"translated": payload['translated'],
"resource": payload['resource'],
"event": payload['event'],
"language": payload['language']
}
return transifex_variables
@defer.inlineCallbacks
def getChanges(self, request):
self.secret = None
if isinstance(self.options, dict):
self.secret = self.options.get('secret')
try:
content = request.content.read()
content_text = bytes2unicode(content)
payload = json.loads(content_text)
except Exception as exception:
raise ValueError('Error loading JSON: ' + str(exception))
if self.secret is not None:
p = Properties()
p.master = self.master
rendered_secret = yield p.render(self.secret)
signature = hmac.new(
unicode2bytes(rendered_secret),
unicode2bytes(content_text.strip()),
digestmod=hashlib.sha256)
header_signature = bytes2unicode(
request.getHeader(_HEADER_SIGNATURE))
self.verifyTransifexSignature(
request, content, rendered_secret,
signature, header_signature
)
event_type = bytes2unicode(payload.get(_EVENT_KEY), "None")
log.msg("Received event '{}' from transifex".format(event_type))
codebase = ""
changes = []
handler_function = getattr(self, 'process_{}'.format(event_type), None)
if not handler_function:
log.msg("Ignoring transifex event '{}'".format(event_type))
else:
changes = handler_function(payload, event_type, codebase)
return (changes, 'transifex')
transifex = TransifexHandler(transifex_dict)