default_hook.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. #!/usr/bin/env python
  2. """Pagure specific hook to be added to all projects in pagure by default.
  3. """
  4. from __future__ import print_function, unicode_literals
  5. import os
  6. import logging
  7. import sys
  8. if 'PAGURE_CONFIG' not in os.environ \
  9. and os.path.exists('/etc/pagure/pagure.cfg'):
  10. os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'
  11. import pygit2 # noqa: E402
  12. import six # noqa: E402
  13. import pagure # noqa: E402
  14. import pagure.flask_app # noqa: E402
  15. import pagure.exceptions # noqa: E402
  16. import pagure.lib.link # noqa: E402
  17. import pagure.lib.tasks # noqa: E402
  18. import pagure.lib.tasks_services # noqa: E402
  19. _config = pagure.config.reload_config()
  20. _log = logging.getLogger(__name__)
  21. abspath = os.path.abspath(os.environ['GIT_DIR'])
  22. def send_fedmsg_notifications(project, topic, msg):
  23. ''' If the user asked for fedmsg notifications on commit, this will
  24. do it.
  25. '''
  26. import fedmsg
  27. config = fedmsg.config.load_config([], None)
  28. config['active'] = True
  29. config['endpoints']['relay_inbound'] = config['relay_inbound']
  30. fedmsg.init(name='relay_inbound', **config)
  31. pagure.lib.notify.log(
  32. project=project,
  33. topic=topic,
  34. msg=msg,
  35. redis=None, # web-hook notification are handled separately
  36. )
  37. def send_webhook_notifications(project, topic, msg):
  38. ''' If the user asked for webhook notifications on commit, this will
  39. do it.
  40. '''
  41. pagure.lib.tasks_services.webhook_notification.delay(
  42. topic=topic,
  43. msg=msg,
  44. namespace=project.namespace,
  45. name=project.name,
  46. user=project.user.username if project.is_fork else None,
  47. )
  48. def send_notifications(session, project, refname, revs, forced):
  49. ''' Send out-going notifications about the commits that have just been
  50. pushed.
  51. '''
  52. auths = set()
  53. for rev in revs:
  54. email = pagure.lib.git.get_author_email(rev, abspath)
  55. name = pagure.lib.git.get_author(rev, abspath)
  56. author = pagure.lib.search_user(session, email=email) or name
  57. auths.add(author)
  58. authors = []
  59. for author in auths:
  60. if not isinstance(author, six.string_types):
  61. author = author.to_json(public=True)
  62. authors.append(author)
  63. if revs:
  64. revs.reverse()
  65. print("* Publishing information for %i commits" % len(revs))
  66. topic = 'git.receive'
  67. msg = dict(
  68. total_commits=len(revs),
  69. start_commit=revs[0],
  70. end_commit=revs[-1],
  71. branch=refname,
  72. forced=forced,
  73. authors=list(authors),
  74. agent=os.environ['GL_USER'],
  75. repo=project.to_json(public=True)
  76. if not isinstance(project, six.string_types) else project,
  77. )
  78. fedmsg_hook = pagure.lib.plugins.get_plugin('Fedmsg')
  79. fedmsg_hook.db_object()
  80. always_fedmsg = _config.get('ALWAYS_FEDMSG_ON_COMMITS') or None
  81. if always_fedmsg \
  82. or (project.fedmsg_hook and project.fedmsg_hook.active):
  83. try:
  84. print(" - to fedmsg")
  85. send_fedmsg_notifications(project, topic, msg)
  86. except Exception:
  87. _log.exception(
  88. 'Error sending fedmsg notifications on commit push')
  89. if project.settings.get('Web-hooks') and not project.private:
  90. try:
  91. print(" - to web-hooks")
  92. send_webhook_notifications(project, topic, msg)
  93. except Exception:
  94. _log.exception(
  95. 'Error sending web-hook notifications on commit push')
  96. if _config.get('PAGURE_CI_SERVICES') \
  97. and project.ci_hook \
  98. and project.ci_hook.active_commit \
  99. and not project.private:
  100. pagure.lib.tasks_services.trigger_ci_build.delay(
  101. project_name=project.fullname,
  102. cause=revs[-1],
  103. branch=refname,
  104. ci_type=project.ci_hook.ci_type
  105. )
  106. def inform_pull_request_urls(
  107. session, project, commits, refname, default_branch):
  108. ''' Inform the user about the URLs to open a new pull-request or visit
  109. the existing one.
  110. '''
  111. target_repo = project
  112. if project.is_fork:
  113. target_repo = project.parent
  114. if commits and refname != default_branch\
  115. and target_repo.settings.get('pull_requests', True):
  116. print()
  117. prs = pagure.lib.search_pull_requests(
  118. session,
  119. project_id_from=project.id,
  120. status='Open',
  121. branch_from=refname,
  122. )
  123. # Link to existing PRs if there are any
  124. seen = len(prs) != 0
  125. for pr in prs:
  126. # Link tickets with pull-requests if the commit mentions it
  127. pagure.lib.tasks.link_pr_to_ticket.delay(pr.uid)
  128. # Inform the user about the PR
  129. print('View pull-request for %s' % refname)
  130. print(' %s/%s/pull-request/%s' % (
  131. _config['APP_URL'].rstrip('/'),
  132. pr.project.url_path,
  133. pr.id)
  134. )
  135. # If no existing PRs, provide the link to open one
  136. if not seen:
  137. print('Create a pull-request for %s' % refname)
  138. print(' %s/%s/diff/%s..%s' % (
  139. _config['APP_URL'].rstrip('/'),
  140. project.url_path,
  141. default_branch,
  142. refname)
  143. )
  144. print()
  145. def run_as_post_receive_hook():
  146. repo = pagure.lib.git.get_repo_name(abspath)
  147. username = pagure.lib.git.get_username(abspath)
  148. namespace = pagure.lib.git.get_repo_namespace(abspath)
  149. if _config.get('HOOK_DEBUG', False):
  150. print('repo:', repo)
  151. print('user:', username)
  152. print('namespace:', namespace)
  153. session = pagure.lib.create_session(_config['DB_URL'])
  154. project = pagure.lib._get_project(
  155. session, repo, user=username, namespace=namespace)
  156. for line in sys.stdin:
  157. if _config.get('HOOK_DEBUG', False):
  158. print(line)
  159. (oldrev, newrev, refname) = line.strip().split(' ', 2)
  160. if _config.get('HOOK_DEBUG', False):
  161. print(' -- Old rev')
  162. print(oldrev)
  163. print(' -- New rev')
  164. print(newrev)
  165. print(' -- Ref name')
  166. print(refname)
  167. # Retrieve the default branch
  168. repo_obj = pygit2.Repository(abspath)
  169. default_branch = None
  170. if not repo_obj.is_empty and not repo_obj.head_is_unborn:
  171. default_branch = repo_obj.head.shorthand
  172. forced = False
  173. if set(newrev) == set(['0']):
  174. print("Deleting a reference/branch, so we won't run the "
  175. "pagure hook")
  176. return
  177. elif set(oldrev) == set(['0']):
  178. oldrev = '^%s' % oldrev
  179. elif pagure.lib.git.is_forced_push(oldrev, newrev, abspath):
  180. forced = True
  181. base = pagure.lib.git.get_base_revision(oldrev, newrev, abspath)
  182. if base:
  183. oldrev = base[0]
  184. refname = refname.replace('refs/heads/', '')
  185. commits = pagure.lib.git.get_revs_between(
  186. oldrev, newrev, abspath, refname)
  187. if refname == default_branch:
  188. print('Sending to redis to log activity and send commit '
  189. 'notification emails')
  190. else:
  191. print('Sending to redis to send commit notification emails')
  192. # This is logging the commit to the log table in the DB so we can
  193. # render commits in the calendar heatmap.
  194. # It is also sending emails about commits to people using the
  195. # 'watch' feature to be made aware of new commits.
  196. pagure.lib.tasks_services.log_commit_send_notifications.delay(
  197. name=repo,
  198. commits=commits,
  199. abspath=abspath,
  200. branch=refname,
  201. default_branch=default_branch,
  202. namespace=namespace,
  203. username=username,
  204. )
  205. # This one is sending fedmsg and web-hook notifications for project
  206. # that set them up
  207. send_notifications(session, project, refname, commits, forced)
  208. # Now display to the user if this isn't the default branch links to
  209. # open a new pr or review the existing one
  210. inform_pull_request_urls(
  211. session, project, commits, refname, default_branch)
  212. # Schedule refresh of all opened PRs
  213. parent = project.parent or project
  214. pagure.lib.tasks.refresh_pr_cache.delay(
  215. parent.name,
  216. parent.namespace,
  217. parent.user.user if parent.is_fork else None
  218. )
  219. session.remove()
  220. def main(args):
  221. run_as_post_receive_hook()
  222. if __name__ == '__main__':
  223. main(sys.argv[1:])