github.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. # This file is part of Buildbot. Buildbot is free software: you can
  2. # redistribute it and/or modify it under the terms of the GNU General Public
  3. # License as published by the Free Software Foundation, version 2.
  4. #
  5. # This program is distributed in the hope that it will be useful, but WITHOUT
  6. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  7. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  8. # details.
  9. #
  10. # You should have received a copy of the GNU General Public License along with
  11. # this program; if not, write to the Free Software Foundation, Inc., 51
  12. # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  13. #
  14. # Copyright Buildbot Team Members
  15. #!/usr/bin/env python
  16. """
  17. github_buildbot.py is based on git_buildbot.py
  18. github_buildbot.py will determine the repository information from the JSON
  19. HTTP POST it receives from github.com and build the appropriate repository.
  20. If your github repository is private, you must add a ssh key to the github
  21. repository for the user who initiated the build on the buildslave.
  22. """
  23. import re
  24. import datetime
  25. from twisted.python import log
  26. import calendar
  27. try:
  28. import json
  29. assert json
  30. except ImportError:
  31. import simplejson as json
  32. # python is silly about how it handles timezones
  33. class fixedOffset(datetime.tzinfo):
  34. """
  35. fixed offset timezone
  36. """
  37. def __init__(self, minutes, hours, offsetSign = 1):
  38. self.minutes = int(minutes) * offsetSign
  39. self.hours = int(hours) * offsetSign
  40. self.offset = datetime.timedelta(minutes = self.minutes,
  41. hours = self.hours)
  42. def utcoffset(self, dt):
  43. return self.offset
  44. def dst(self, dt):
  45. return datetime.timedelta(0)
  46. def convertTime(myTestTimestamp):
  47. #"1970-01-01T00:00:00+00:00"
  48. # Normalize myTestTimestamp
  49. if myTestTimestamp[-1] == 'Z':
  50. myTestTimestamp = myTestTimestamp[:-1] + '-00:00'
  51. matcher = re.compile(r'(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)([-+])(\d\d):(\d\d)')
  52. result = matcher.match(myTestTimestamp)
  53. (year, month, day, hour, minute, second, offsetsign, houroffset, minoffset) = \
  54. result.groups()
  55. if offsetsign == '+':
  56. offsetsign = 1
  57. else:
  58. offsetsign = -1
  59. offsetTimezone = fixedOffset( minoffset, houroffset, offsetsign )
  60. myDatetime = datetime.datetime( int(year),
  61. int(month),
  62. int(day),
  63. int(hour),
  64. int(minute),
  65. int(second),
  66. 0,
  67. offsetTimezone)
  68. return calendar.timegm( myDatetime.utctimetuple() )
  69. def getChanges(request, options = None):
  70. """
  71. Reponds only to POST events and starts the build process
  72. :arguments:
  73. request
  74. the http request object
  75. """
  76. payload = json.loads(request.args['payload'][0])
  77. import urllib,datetime
  78. fname = str(datetime.datetime.now()).replace(' ','_').replace(':','-')[:19]
  79. open('github_{0}.json'.format(fname),'w').write(json.dumps(json.loads(urllib.unquote(request.args['payload'][0])), sort_keys = True, indent = 2))
  80. if 'pull_request' in payload:
  81. user = payload['pull_request']['user']['login']
  82. repo = payload['pull_request']['head']['repo']['name']
  83. repo_url = payload['pull_request']['head']['repo']['html_url']
  84. else:
  85. user = payload['repository']['owner']['name']
  86. repo = payload['repository']['name']
  87. repo_url = payload['repository']['url']
  88. project = request.args.get('project', None)
  89. if project:
  90. project = project[0]
  91. elif project is None:
  92. project = ''
  93. # This field is unused:
  94. #private = payload['repository']['private']
  95. changes = process_change(payload, user, repo, repo_url, project)
  96. log.msg("Received %s changes from github" % len(changes))
  97. return (changes, 'git')
  98. def process_change(payload, user, repo, repo_url, project):
  99. """
  100. Consumes the JSON as a python object and actually starts the build.
  101. :arguments:
  102. payload
  103. Python Object that represents the JSON sent by GitHub Service
  104. Hook.
  105. """
  106. changes = []
  107. newrev = payload['after'] if 'after' in payload else payload['pull_request']['head']['sha']
  108. refname = payload['ref'] if 'ref' in payload else payload['pull_request']['head']['ref']
  109. # We only care about regular heads, i.e. branches
  110. match = re.match(r"^(refs\/heads\/|)([^/]+)$", refname)
  111. if not match:
  112. log.msg("Ignoring refname `%s': Not a branch" % refname)
  113. return []
  114. branch = match.groups()[1]
  115. if re.match(r"^0*$", newrev):
  116. log.msg("Branch `%s' deleted, ignoring" % branch)
  117. return []
  118. else:
  119. if 'pull_request' in payload:
  120. changes = [{
  121. 'category' : 'github_pullrequest',
  122. 'who' : '{0} - PR#{1}'.format(user,payload['number']),
  123. 'files' : [],
  124. 'comments' : payload['pull_request']['title'],
  125. 'revision' : newrev,
  126. 'when' : convertTime(payload['pull_request']['updated_at']),
  127. 'branch' : branch,
  128. 'revlink' : '{0}/commit/{1}'.format(repo_url,newrev),
  129. 'repository' : repo_url,
  130. 'project' : project }]
  131. return changes
  132. for commit in payload['commits']:
  133. files = []
  134. if 'added' in commit:
  135. files.extend(commit['added'])
  136. if 'modified' in commit:
  137. files.extend(commit['modified'])
  138. if 'removed' in commit:
  139. files.extend(commit['removed'])
  140. when = convertTime( commit['timestamp'])
  141. log.msg("New revision: %s" % commit['id'][:8])
  142. chdict = dict(
  143. who = commit['author']['name']
  144. + " <" + commit['author']['email'] + ">",
  145. files = files,
  146. comments = commit['message'],
  147. revision = commit['id'],
  148. when = when,
  149. branch = branch,
  150. revlink = commit['url'],
  151. repository = repo_url,
  152. project = project)
  153. changes.append(chdict)
  154. return changes