Merge lp:~3v1n0/ubuntu-bots/gitlab-support into lp:ubuntu-bots

Proposed by Marco Trevisan (Treviño)
Status: Needs review
Proposed branch: lp:~3v1n0/ubuntu-bots/gitlab-support
Merge into: lp:ubuntu-bots
Prerequisite: lp:~3v1n0/ubuntu-bots/github-support
Diff against target: 199 lines (+122/-11)
2 files modified
Bugtracker/README.txt (+1/-0)
Bugtracker/plugin.py (+121/-11)
To merge this branch: bzr merge lp:~3v1n0/ubuntu-bots/gitlab-support
Reviewer Review Type Date Requested Status
Ubuntu IRC Bots Pending
Review via email: mp+342164@code.launchpad.net

Commit message

Bugtracker: add support for Gitlab issues and merge requests

To post a comment you must log in.
lp:~3v1n0/ubuntu-bots/gitlab-support updated
324. By Marco Trevisan (Treviño)

Bugtracker: add support for Gitlab issues and merge requests

325. By Marco Trevisan (Treviño)

Bugtracker: use cleaner code in generating bug status text

326. By Marco Trevisan (Treviño)

Bugtracker, gitlab: allow to user longer paths for projects

Unmerged revisions

326. By Marco Trevisan (Treviño)

Bugtracker, gitlab: allow to user longer paths for projects

325. By Marco Trevisan (Treviño)

Bugtracker: use cleaner code in generating bug status text

324. By Marco Trevisan (Treviño)

Bugtracker: add support for Gitlab issues and merge requests

323. By Marco Trevisan (Treviño)

Bugtracker: add ext info for Github

322. By Marco Trevisan (Treviño)

Bugtracker: add support for trackers with multiple sub-trackers

Properly log pull requests in Github

321. By Marco Trevisan (Treviño)

Bugtracker: Add github support

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Bugtracker/README.txt'
2--- Bugtracker/README.txt 2018-03-27 04:09:52 +0000
3+++ Bugtracker/README.txt 2018-03-27 04:09:52 +0000
4@@ -20,6 +20,7 @@
5 * str.php from the CUPS project
6 * Mantis (http://www.mantisbt.org)
7 * Github
8+* Gitlab
9
10 A notable exception is Sourceforge. Unfortunatly, it has no API or data export
11 feature to output bug information in a well-formed way.
12
13=== modified file 'Bugtracker/plugin.py'
14--- Bugtracker/plugin.py 2018-03-27 04:09:52 +0000
15+++ Bugtracker/plugin.py 2018-03-27 04:09:52 +0000
16@@ -419,7 +419,7 @@
17 irc.reply(makeClean(r), prefixNick=False)
18
19 def turlSnarfer(self, irc, msg, match):
20- r"(?P<tracker>https?://\S*?)/(?:Bugs/0*|str.php\?L|show_bug.cgi\?id=|bugreport.cgi\?bug=|(?:bugs|\+bug)/|ticket/|tracker/|\S*aid=|bug=|issues/|pull/)?(?P<bug>\d+)(?P<sfurl>&group_id=\d+&at_id=\d+)?"
21+ r"(?P<tracker>https?://\S*?)/(?:Bugs/0*|str.php\?L|show_bug.cgi\?id=|bugreport.cgi\?bug=|(?:bugs|\+bug)/|ticket/|tracker/|\S*aid=|bug=|issues/|pull/|merge_requests/)?(?P<bug>\d+)(?P<sfurl>&group_id=\d+&at_id=\d+)?"
22 channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None
23 if not self.registryValue('bugSnarfer', channel):
24 return
25@@ -543,8 +543,7 @@
26 else:
27 (bid, product, title, severity, status, assignee, url) = r
28
29- severity = severity[0].upper() + severity[1:].lower() if severity else ''
30- status = status[0].upper() + status[1:].lower()
31+ state_infos = ', '.join(filter(None, [severity.title(), status.title()]))
32 tracker_name = tracker.description + ' '
33 if not do_url:
34 url = ''
35@@ -552,16 +551,16 @@
36 tracker_name = ''
37 if product:
38 if showext:
39- reports.append("%sbug %s in %s \"%s\" %s [%s,%s] %s" % (tracker_name, bid, product,
40- title, extinfo, severity, status, url))
41+ reports.append("%sbug %s in %s \"%s\" %s [%s] %s" % (tracker_name, bid, product,
42+ title, extinfo, state_infos, url))
43 else:
44- reports.append("%sbug %s in %s \"%s\" [%s,%s] %s" % (tracker_name, bid, product,
45- title, severity, status, url))
46+ reports.append("%sbug %s in %s \"%s\" [%s] %s" % (tracker_name, bid, product,
47+ title, state_infos, url))
48 else:
49 if showext:
50- reports.append("%sbug %s \"%s\" %s [%s,%s] %s" % (tracker_name, bid, title, extinfo, severity, status, url))
51+ reports.append("%sbug %s \"%s\" %s [%s] %s" % (tracker_name, bid, title, extinfo, state_infos, url))
52 else:
53- reports.append("%sbug %s \"%s\" [%s,%s] %s" % (tracker_name, bid, title, severity, status, url))
54+ reports.append("%sbug %s \"%s\" [%s] %s" % (tracker_name, bid, title, state_infos, url))
55 if do_assignee and assignee:
56 reports[-1] = reports[-1] + (" - Assigned to %s" % assignee)
57 return reports
58@@ -1128,8 +1127,8 @@
59 if is_pull_request:
60 id = u'(Pull request) {}'.format(id)
61
62- # extinfo = "(comments: {}, reactions: {})" % (issue.comments, len(issue.get_reactions()))
63- extinfo = "(comments: {})".format(issue.comments)
64+ # extinfo = '(comments: {}, reactions: {})'.format(issue.comments, len(issue.get_reactions()))
65+ extinfo = u'(comments: {})'.format(issue.comments)
66
67 return [(id, self.repo.name, issue.title, labels, issue.state, assignee, issue.html_url, extinfo)]
68 except Exception as e:
69@@ -1139,6 +1138,113 @@
70 raise BugtrackerError, s
71
72
73+class Gitlab(IBugtracker):
74+ _GL_TOKENS = { 'gitlab.com': None,
75+ 'gitlab.gnome.org': None,
76+ 'salsa.debian.org': None }
77+ _gl = {}
78+
79+ def __init__(self, *args, **kwargs):
80+ IBugtracker.__init__(self, *args, **kwargs)
81+
82+ self.gl = None
83+ self.project = None
84+ self.merge_requests = False
85+ self.hostname = self.url.split('://', 1)[1].split('/', 1)[0]
86+
87+ if not self.hostname in Gitlab._gl.keys():
88+ if not self.hostname in Gitlab._GL_TOKENS.keys() or not Gitlab._GL_TOKENS[self.hostname]:
89+ self.log.error("No access token for gitlab host {}".format(self.hostname))
90+ return
91+
92+ try: # We need python-gitlab
93+ import gitlab
94+ from distutils.version import LooseVersion
95+
96+ if LooseVersion(gitlab.__version__) < LooseVersion('1.0.0'):
97+ self.log.exception("gitlab version must be 1.0.0 or newer")
98+
99+ Gitlab._gl[self.hostname] = gitlab.Gitlab(self.url, private_token=Gitlab._GL_TOKENS[self.hostname])
100+ except ImportError:
101+ self.log.error("Please install python-gitlab (=> 1.0.0)")
102+
103+ self.gl = Gitlab._gl[self.hostname]
104+
105+ def has_multiple_trackers(self):
106+ return True
107+
108+ def get_tracker(self, url):
109+ if not self.gl:
110+ return
111+ if not url:
112+ self.log.info("Impossible to get tracker for an empty url")
113+ return
114+
115+ try:
116+ url = url.replace('http://', 'https://', 1)
117+ site_prefix = self.url if self.url[-1] != '/' else self.url[:-1]
118+
119+ if not url.startswith(site_prefix+'/'):
120+ return
121+
122+ projectmatch = re.search(re.escape(self.hostname)+"\/(.+)\/(issues|merge_requests)\/\d+", url)
123+
124+ if projectmatch:
125+ project_full_name = projectmatch.group(1)
126+ project = self.gl.projects.get(project_full_name)
127+ merge_request = projectmatch.group(2) == u'merge_requests'
128+
129+ if not merge_request and not project.issues_enabled:
130+ s = u'Project {} has no issues'.format(project_full_name)
131+ self.log.error(s)
132+ return
133+ elif merge_request and not project.merge_requests_enabled:
134+ s = u'Project {} has no merge requests'.format(project_full_name)
135+ self.log.error(s)
136+ return
137+
138+ description = project.name
139+ if project.namespace['kind'] != u'user':
140+ description = project.namespace['name']
141+
142+ gl = Gitlab(project_full_name.replace('/', '-'), project.web_url, description)
143+ gl.project = project
144+ gl.merge_requests = merge_request
145+ return gl
146+
147+ except Exception as e:
148+ s = u'{}: Impossible to get tracker for {}: {}'.format(
149+ self.description, url, e)
150+ self.log.exception(s)
151+
152+ def get_bug(self, id):
153+ if not self.gl or not self.project:
154+ self.log.info("Gitlab or project not initialized")
155+ return []
156+
157+ try:
158+ if self.merge_requests:
159+ issue = self.project.mergerequests.get(id)
160+ id = u'(Merge request) {}'.format(id)
161+ else:
162+ issue = self.project.issues.get(id)
163+
164+ assignee = None
165+ labels = u', '.join([l for l in issue.labels])
166+
167+ if issue.assignee:
168+ assignee = u'{} ({})'.format(issue.assignee.username, issue.assignee['name'])
169+
170+ extinfo = u'(comments: {})'.format(issue.user_notes_count)
171+
172+ return [(id, self.project.path, issue.title, labels, issue.state, assignee, issue.web_url, extinfo)]
173+ except Exception as e:
174+ s = u'{}: Impossible to get infos for {} issue {}: {}'.format(
175+ self.description, self.project.path_with_namespace, id, e)
176+ self.log.exception(s)
177+ raise BugtrackerError, s
178+
179+
180 # Introspection is quite cool
181 defined_bugtrackers = {}
182 v = vars()
183@@ -1150,6 +1256,7 @@
184 registerBugtracker('ubuntu', 'https://launchpad.net', 'Ubuntu', 'launchpad')
185 registerBugtracker('gnome', 'http://bugzilla.gnome.org', 'Gnome', 'bugzilla')
186 registerBugtracker('gnome2', 'http://bugs.gnome.org', 'Gnome', 'bugzilla')
187+registerBugtracker('gnome3', 'https://gitlab.gnome.org', 'Gnome', 'gitlab')
188 registerBugtracker('kde', 'http://bugs.kde.org', 'KDE', 'bugzilla')
189 registerBugtracker('ximian', 'http://bugzilla.ximian.com', 'Ximian', 'bugzilla')
190 registerBugtracker('freedesktop', 'http://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla')
191@@ -1168,5 +1275,8 @@
192 registerBugtracker('ubottu', 'https://launchpad.net', 'Ubottu', 'launchpad')
193 registerBugtracker('sourceforge', 'http://sourceforge.net/tracker/', 'Sourceforge', 'sourceforge')
194 registerBugtracker('github', 'https://github.com', 'Github', 'github')
195+registerBugtracker('gitlab', 'https://gitlab.com', 'Gitlab', 'gitlab')
196+registerBugtracker('salsa', 'https://salsa.debian.org', 'Debian Salsa', 'gitlab')
197+
198 # Don't delete this one
199 Class = Bugtracker

Subscribers

People subscribed via source and target branches