"Fossies" - the Fresh Open Source Software Archive

Member "buildbot-2.5.1/buildbot/www/hooks/gitlab.py" (24 Nov 2019, 7975 Bytes) of package /linux/misc/buildbot-2.5.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "gitlab.py" see the Fossies "Dox" file reference documentation.

    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 
   16 
   17 import json
   18 import re
   19 
   20 from dateutil.parser import parse as dateparse
   21 
   22 from twisted.internet.defer import inlineCallbacks
   23 from twisted.python import log
   24 
   25 from buildbot.process.properties import Properties
   26 from buildbot.util import bytes2unicode
   27 from buildbot.www.hooks.base import BaseHookHandler
   28 
   29 _HEADER_EVENT = b'X-Gitlab-Event'
   30 _HEADER_GITLAB_TOKEN = b'X-Gitlab-Token'
   31 
   32 
   33 class GitLabHandler(BaseHookHandler):
   34 
   35     def _process_change(self, payload, user, repo, repo_url, event,
   36                         codebase=None):
   37         """
   38         Consumes the JSON as a python object and actually starts the build.
   39 
   40         :arguments:
   41             payload
   42                 Python Object that represents the JSON sent by GitLab Service
   43                 Hook.
   44         """
   45         changes = []
   46         refname = payload['ref']
   47         # project name from http headers is empty for me, so get it from repository/name
   48         project = payload['repository']['name']
   49 
   50         # We only care about regular heads or tags
   51         match = re.match(r"^refs/(heads|tags)/(.+)$", refname)
   52         if not match:
   53             log.msg("Ignoring refname `%s': Not a branch" % refname)
   54             return changes
   55 
   56         branch = match.group(2)
   57         if payload.get('deleted'):
   58             log.msg("Branch `%s' deleted, ignoring" % branch)
   59             return changes
   60 
   61         for commit in payload['commits']:
   62             if not commit.get('distinct', True):
   63                 log.msg('Commit `%s` is a non-distinct commit, ignoring...' %
   64                         (commit['id'],))
   65                 continue
   66 
   67             files = []
   68             for kind in ('added', 'modified', 'removed'):
   69                 files.extend(commit.get(kind, []))
   70 
   71             when_timestamp = dateparse(commit['timestamp'])
   72 
   73             log.msg("New revision: %s" % commit['id'][:8])
   74 
   75             change = {
   76                 'author': '%s <%s>' % (commit['author']['name'],
   77                                        commit['author']['email']),
   78                 'files': files,
   79                 'comments': commit['message'],
   80                 'revision': commit['id'],
   81                 'when_timestamp': when_timestamp,
   82                 'branch': branch,
   83                 'revlink': commit['url'],
   84                 'repository': repo_url,
   85                 'project': project,
   86                 'category': event,
   87                 'properties': {
   88                     'event': event,
   89                 },
   90             }
   91 
   92             if codebase is not None:
   93                 change['codebase'] = codebase
   94 
   95             changes.append(change)
   96 
   97         return changes
   98 
   99     def _process_merge_request_change(self, payload, event, codebase=None):
  100         """
  101         Consumes the merge_request JSON as a python object and turn it into a buildbot change.
  102 
  103         :arguments:
  104             payload
  105                 Python Object that represents the JSON sent by GitLab Service
  106                 Hook.
  107         """
  108         attrs = payload['object_attributes']
  109         commit = attrs['last_commit']
  110         when_timestamp = dateparse(commit['timestamp'])
  111         # @todo provide and document a way to choose between http and ssh url
  112         repo_url = attrs['target']['git_http_url']
  113         # project name from http headers is empty for me, so get it from object_attributes/target/name
  114         project = attrs['target']['name']
  115 
  116         # Filter out uninteresting events
  117         state = attrs['state']
  118         if re.match('^(closed|merged|approved)$', state):
  119             log.msg("GitLab MR#{}: Ignoring because state is {}".format(attrs['iid'], state))
  120             return []
  121         action = attrs['action']
  122         if not re.match('^(open|reopen)$', action) and not (action == "update" and "oldrev" in attrs):
  123             log.msg("GitLab MR#{}: Ignoring because action {} was not open or "
  124                     "reopen or an update that added code".format(attrs['iid'],
  125                                                                  action))
  126             return []
  127 
  128         changes = [{
  129             'author': '%s <%s>' % (commit['author']['name'],
  130                                    commit['author']['email']),
  131             'files': [],  # @todo use rest API
  132             'comments': "MR#{}: {}\n\n{}".format(attrs['iid'], attrs['title'], attrs['description']),
  133             'revision': commit['id'],
  134             'when_timestamp': when_timestamp,
  135             'branch': attrs['target_branch'],
  136             'repository': repo_url,
  137             'project': project,
  138             'category': event,
  139             'revlink': attrs['url'],
  140             'properties': {
  141                 'source_branch': attrs['source_branch'],
  142                 'source_project_id': attrs['source_project_id'],
  143                 'source_repository': attrs['source']['git_http_url'],
  144                 'source_git_ssh_url': attrs['source']['git_ssh_url'],
  145                 'target_branch': attrs['target_branch'],
  146                 'target_project_id': attrs['target_project_id'],
  147                 'target_repository': attrs['target']['git_http_url'],
  148                 'target_git_ssh_url': attrs['target']['git_ssh_url'],
  149                 'event': event,
  150             },
  151         }]
  152         if codebase is not None:
  153             changes[0]['codebase'] = codebase
  154         return changes
  155 
  156     @inlineCallbacks
  157     def getChanges(self, request):
  158         """
  159         Reponds only to POST events and starts the build process
  160 
  161         :arguments:
  162             request
  163                 the http request object
  164         """
  165         expected_secret = isinstance(self.options, dict) and self.options.get('secret')
  166         if expected_secret:
  167             received_secret = request.getHeader(_HEADER_GITLAB_TOKEN)
  168             received_secret = bytes2unicode(received_secret)
  169 
  170             p = Properties()
  171             p.master = self.master
  172             expected_secret_value = yield p.render(expected_secret)
  173 
  174             if received_secret != expected_secret_value:
  175                 raise ValueError("Invalid secret")
  176         try:
  177             content = request.content.read()
  178             payload = json.loads(bytes2unicode(content))
  179         except Exception as e:
  180             raise ValueError("Error loading JSON: " + str(e))
  181         event_type = request.getHeader(_HEADER_EVENT)
  182         event_type = bytes2unicode(event_type)
  183         # newer version of gitlab have a object_kind parameter,
  184         # which allows not to use the http header
  185         event_type = payload.get('object_kind', event_type)
  186         codebase = request.args.get(b'codebase', [None])[0]
  187         codebase = bytes2unicode(codebase)
  188         if event_type in ("push", "tag_push", "Push Hook"):
  189             user = payload['user_name']
  190             repo = payload['repository']['name']
  191             repo_url = payload['repository']['url']
  192             changes = self._process_change(
  193                 payload, user, repo, repo_url, event_type, codebase=codebase)
  194         elif event_type == 'merge_request':
  195             changes = self._process_merge_request_change(
  196                 payload, event_type, codebase=codebase)
  197         else:
  198             changes = []
  199         if changes:
  200             log.msg("Received {} changes from {} gitlab event".format(
  201                 len(changes), event_type))
  202         return (changes, 'git')
  203 
  204 
  205 gitlab = GitLabHandler