##// END OF EJS Templates
events: improve the error message on missing event commits
marcink -
r2643:c2891b47 default
parent child Browse files
Show More
@@ -1,355 +1,356 b''
1 1 # Copyright (C) 2016-2018 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import collections
20 20 import logging
21 21 import datetime
22 22
23 23 from rhodecode.translation import lazy_ugettext
24 24 from rhodecode.model.db import User, Repository, Session
25 25 from rhodecode.events.base import RhodecodeEvent
26 26 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
27 27
28 28 log = logging.getLogger(__name__)
29 29
30 30
31 31 def _commits_as_dict(event, commit_ids, repos):
32 32 """
33 33 Helper function to serialize commit_ids
34 34
35 35 :param event: class calling this method
36 36 :param commit_ids: commits to get
37 37 :param repos: list of repos to check
38 38 """
39 39 from rhodecode.lib.utils2 import extract_mentioned_users
40 40 from rhodecode.lib.helpers import (
41 41 urlify_commit_message, process_patterns, chop_at_smart)
42 42 from rhodecode.model.repo import RepoModel
43 43
44 44 if not repos:
45 45 raise Exception('no repo defined')
46 46
47 47 if not isinstance(repos, (tuple, list)):
48 48 repos = [repos]
49 49
50 50 if not commit_ids:
51 51 return []
52 52
53 53 needed_commits = list(commit_ids)
54 54
55 55 commits = []
56 56 reviewers = []
57 57 for repo in repos:
58 58 if not needed_commits:
59 59 return commits # return early if we have the commits we need
60 60
61 61 vcs_repo = repo.scm_instance(cache=False)
62 62
63 63 try:
64 64 # use copy of needed_commits since we modify it while iterating
65 65 for commit_id in list(needed_commits):
66 66 if commit_id.startswith('tag=>'):
67 67 raw_id = commit_id[5:]
68 68 cs_data = {
69 69 'raw_id': commit_id, 'short_id': commit_id,
70 70 'branch': None,
71 71 'git_ref_change': 'tag_add',
72 72 'message': 'Added new tag {}'.format(raw_id),
73 73 'author': event.actor.full_contact,
74 74 'date': datetime.datetime.now(),
75 75 'refs': {
76 76 'branches': [],
77 77 'bookmarks': [],
78 78 'tags': []
79 79 }
80 80 }
81 81 commits.append(cs_data)
82 82
83 83 elif commit_id.startswith('delete_branch=>'):
84 84 raw_id = commit_id[15:]
85 85 cs_data = {
86 86 'raw_id': commit_id, 'short_id': commit_id,
87 87 'branch': None,
88 88 'git_ref_change': 'branch_delete',
89 89 'message': 'Deleted branch {}'.format(raw_id),
90 90 'author': event.actor.full_contact,
91 91 'date': datetime.datetime.now(),
92 92 'refs': {
93 93 'branches': [],
94 94 'bookmarks': [],
95 95 'tags': []
96 96 }
97 97 }
98 98 commits.append(cs_data)
99 99
100 100 else:
101 101 try:
102 102 cs = vcs_repo.get_changeset(commit_id)
103 103 except CommitDoesNotExistError:
104 104 continue # maybe its in next repo
105 105
106 106 cs_data = cs.__json__()
107 107 cs_data['refs'] = cs._get_refs()
108 108
109 109 cs_data['mentions'] = extract_mentioned_users(cs_data['message'])
110 110 cs_data['reviewers'] = reviewers
111 111 cs_data['url'] = RepoModel().get_commit_url(
112 112 repo, cs_data['raw_id'], request=event.request)
113 113 cs_data['permalink_url'] = RepoModel().get_commit_url(
114 114 repo, cs_data['raw_id'], request=event.request,
115 115 permalink=True)
116 116 urlified_message, issues_data = process_patterns(
117 117 cs_data['message'], repo.repo_name)
118 118 cs_data['issues'] = issues_data
119 119 cs_data['message_html'] = urlify_commit_message(
120 120 cs_data['message'], repo.repo_name)
121 121 cs_data['message_html_title'] = chop_at_smart(
122 122 cs_data['message'], '\n', suffix_if_chopped='...')
123 123 commits.append(cs_data)
124 124
125 125 needed_commits.remove(commit_id)
126 126
127 127 except Exception:
128 128 log.exception('Failed to extract commits data')
129 129 # we don't send any commits when crash happens, only full list
130 130 # matters we short circuit then.
131 131 return []
132 132
133 133 missing_commits = set(commit_ids) - set(c['raw_id'] for c in commits)
134 134 if missing_commits:
135 log.error('missing commits: %s' % ', '.join(missing_commits))
135 log.error('Inconsistent repository state. '
136 'Missing commits: %s' % ', '.join(missing_commits))
136 137
137 138 return commits
138 139
139 140
140 141 def _issues_as_dict(commits):
141 142 """ Helper function to serialize issues from commits """
142 143 issues = {}
143 144 for commit in commits:
144 145 for issue in commit['issues']:
145 146 issues[issue['id']] = issue
146 147 return issues
147 148
148 149
149 150 class RepoEvent(RhodecodeEvent):
150 151 """
151 152 Base class for events acting on a repository.
152 153
153 154 :param repo: a :class:`Repository` instance
154 155 """
155 156
156 157 def __init__(self, repo):
157 158 super(RepoEvent, self).__init__()
158 159 self.repo = repo
159 160
160 161 def as_dict(self):
161 162 from rhodecode.model.repo import RepoModel
162 163 data = super(RepoEvent, self).as_dict()
163 164
164 165 extra_fields = collections.OrderedDict()
165 166 for field in self.repo.extra_fields:
166 167 extra_fields[field.field_key] = field.field_value
167 168
168 169 data.update({
169 170 'repo': {
170 171 'repo_id': self.repo.repo_id,
171 172 'repo_name': self.repo.repo_name,
172 173 'repo_type': self.repo.repo_type,
173 174 'url': RepoModel().get_url(
174 175 self.repo, request=self.request),
175 176 'permalink_url': RepoModel().get_url(
176 177 self.repo, request=self.request, permalink=True),
177 178 'extra_fields': extra_fields
178 179 }
179 180 })
180 181 return data
181 182
182 183
183 184 class RepoPreCreateEvent(RepoEvent):
184 185 """
185 186 An instance of this class is emitted as an :term:`event` before a repo is
186 187 created.
187 188 """
188 189 name = 'repo-pre-create'
189 190 display_name = lazy_ugettext('repository pre create')
190 191
191 192
192 193 class RepoCreateEvent(RepoEvent):
193 194 """
194 195 An instance of this class is emitted as an :term:`event` whenever a repo is
195 196 created.
196 197 """
197 198 name = 'repo-create'
198 199 display_name = lazy_ugettext('repository created')
199 200
200 201
201 202 class RepoPreDeleteEvent(RepoEvent):
202 203 """
203 204 An instance of this class is emitted as an :term:`event` whenever a repo is
204 205 created.
205 206 """
206 207 name = 'repo-pre-delete'
207 208 display_name = lazy_ugettext('repository pre delete')
208 209
209 210
210 211 class RepoDeleteEvent(RepoEvent):
211 212 """
212 213 An instance of this class is emitted as an :term:`event` whenever a repo is
213 214 created.
214 215 """
215 216 name = 'repo-delete'
216 217 display_name = lazy_ugettext('repository deleted')
217 218
218 219
219 220 class RepoVCSEvent(RepoEvent):
220 221 """
221 222 Base class for events triggered by the VCS
222 223 """
223 224 def __init__(self, repo_name, extras):
224 225 self.repo = Repository.get_by_repo_name(repo_name)
225 226 if not self.repo:
226 227 raise Exception('repo by this name %s does not exist' % repo_name)
227 228 self.extras = extras
228 229 super(RepoVCSEvent, self).__init__(self.repo)
229 230
230 231 @property
231 232 def actor(self):
232 233 if self.extras.get('username'):
233 234 return User.get_by_username(self.extras['username'])
234 235
235 236 @property
236 237 def actor_ip(self):
237 238 if self.extras.get('ip'):
238 239 return self.extras['ip']
239 240
240 241 @property
241 242 def server_url(self):
242 243 if self.extras.get('server_url'):
243 244 return self.extras['server_url']
244 245
245 246 @property
246 247 def request(self):
247 248 return self.extras.get('request') or self.get_request()
248 249
249 250
250 251 class RepoPrePullEvent(RepoVCSEvent):
251 252 """
252 253 An instance of this class is emitted as an :term:`event` before commits
253 254 are pulled from a repo.
254 255 """
255 256 name = 'repo-pre-pull'
256 257 display_name = lazy_ugettext('repository pre pull')
257 258
258 259
259 260 class RepoPullEvent(RepoVCSEvent):
260 261 """
261 262 An instance of this class is emitted as an :term:`event` after commits
262 263 are pulled from a repo.
263 264 """
264 265 name = 'repo-pull'
265 266 display_name = lazy_ugettext('repository pull')
266 267
267 268
268 269 class RepoPrePushEvent(RepoVCSEvent):
269 270 """
270 271 An instance of this class is emitted as an :term:`event` before commits
271 272 are pushed to a repo.
272 273 """
273 274 name = 'repo-pre-push'
274 275 display_name = lazy_ugettext('repository pre push')
275 276
276 277
277 278 class RepoPushEvent(RepoVCSEvent):
278 279 """
279 280 An instance of this class is emitted as an :term:`event` after commits
280 281 are pushed to a repo.
281 282
282 283 :param extras: (optional) dict of data from proxied VCS actions
283 284 """
284 285 name = 'repo-push'
285 286 display_name = lazy_ugettext('repository push')
286 287
287 288 def __init__(self, repo_name, pushed_commit_ids, extras):
288 289 super(RepoPushEvent, self).__init__(repo_name, extras)
289 290 self.pushed_commit_ids = pushed_commit_ids
290 291 self.new_refs = extras.new_refs
291 292
292 293 def as_dict(self):
293 294 data = super(RepoPushEvent, self).as_dict()
294 295
295 296 def branch_url(branch_name):
296 297 return '{}/changelog?branch={}'.format(
297 298 data['repo']['url'], branch_name)
298 299
299 300 def tag_url(tag_name):
300 301 return '{}/files/{}/'.format(
301 302 data['repo']['url'], tag_name)
302 303
303 304 commits = _commits_as_dict(
304 305 self, commit_ids=self.pushed_commit_ids, repos=[self.repo])
305 306
306 307 last_branch = None
307 308 for commit in reversed(commits):
308 309 commit['branch'] = commit['branch'] or last_branch
309 310 last_branch = commit['branch']
310 311 issues = _issues_as_dict(commits)
311 312
312 313 branches = set()
313 314 tags = set()
314 315 for commit in commits:
315 316 if commit['refs']['tags']:
316 317 for tag in commit['refs']['tags']:
317 318 tags.add(tag)
318 319 if commit['branch']:
319 320 branches.add(commit['branch'])
320 321
321 322 # maybe we have branches in new_refs ?
322 323 try:
323 324 branches = branches.union(set(self.new_refs['branches']))
324 325 except Exception:
325 326 pass
326 327
327 328 branches = [
328 329 {
329 330 'name': branch,
330 331 'url': branch_url(branch)
331 332 }
332 333 for branch in branches
333 334 ]
334 335
335 336 # maybe we have branches in new_refs ?
336 337 try:
337 338 tags = tags.union(set(self.new_refs['tags']))
338 339 except Exception:
339 340 pass
340 341
341 342 tags = [
342 343 {
343 344 'name': tag,
344 345 'url': tag_url(tag)
345 346 }
346 347 for tag in tags
347 348 ]
348 349
349 350 data['push'] = {
350 351 'commits': commits,
351 352 'issues': issues,
352 353 'branches': branches,
353 354 'tags': tags,
354 355 }
355 356 return data
General Comments 0
You need to be logged in to leave comments. Login now