203 |
203 |
204 # from http://www.daniweb.com/software-development/python/code/260268/restart-your-python-program |
204 # from http://www.daniweb.com/software-development/python/code/260268/restart-your-python-program |
205 def restart_self(): |
205 def restart_self(): |
206 python = sys.executable |
206 python = sys.executable |
207 os.execl (python, python, * sys.argv) |
207 os.execl (python, python, * sys.argv) |
|
208 |
|
209 ' Retrieves hg incoming from zandronum repository ' |
|
210 def get_incoming_data (zanrepo, rev, template): |
|
211 try: |
|
212 if rev != '': |
|
213 return zanrepo.hg_command ('incoming', '../zanstablecopy', '--quiet', '-r', rev, '--template', template) |
|
214 else: |
|
215 return zanrepo.hg_command ('incoming', '../zanstablecopy', '--quiet', '--template', template) |
|
216 #fi |
|
217 except: |
|
218 pass |
|
219 return "" |
|
220 #enddef |
|
221 |
|
222 ' Check if a repository exists ' |
|
223 def check_repo_exists (repo_name): |
|
224 print 'Checking that %s exists...' % repo_name |
|
225 repo_url = 'https://bitbucket.org/Torr_Samaho/' + repo_name |
|
226 zanrepo = hgapi.Repo (repo_name) |
|
227 |
|
228 try: |
|
229 zanrepo.hg_command ('id', '.') |
|
230 except hgapi.hgapi.HgException: |
|
231 # If the repo does not exist, clone it. After cloning, there obviously |
|
232 # are no more updates, so we'll be done. |
|
233 try: |
|
234 print 'Cloning %s...' % repo_name |
|
235 zanrepo.hg_clone (repo_url, repo_name) |
|
236 print 'Cloning done. No update checking needed.' |
|
237 except Exception as e: |
|
238 print 'Unable to clone %s from %s: %s' % (repo_name, repo_url, str (`e`)) |
|
239 quit(1) |
|
240 #tried |
|
241 #enddef |
|
242 |
|
243 check_repo_exists ('zandronum') |
|
244 check_repo_exists ('zandronum-stable') |
|
245 |
|
246 repocheck_timeout = {'zandronum':(time.time()) + 15, 'zandronum-stable':(time.time() + 15)} |
|
247 |
|
248 ' Retrieves and processes commits for zandronum repositories ' |
|
249 ' Ensure both repositories are OK before using this! ' |
|
250 def process_zan_repo_updates (usestable): |
|
251 global repocheck_timeout |
|
252 global suds_client |
|
253 global g_config |
|
254 global g_clients |
|
255 |
|
256 repo_name = 'zandronum' if usestable == False else 'zandronum-stable' |
|
257 repo_url = 'https://bitbucket.org/Torr_Samaho/' + repo_name |
|
258 |
|
259 if time.time() < repocheck_timeout[repo_name]: |
|
260 return |
|
261 |
|
262 repocheck_timeout[repo_name] = time.time() + (cfg ('hg_checkinterval', 15) * 60) |
|
263 zanrepo = hgapi.Repo (repo_name) |
|
264 data = get_incoming_data (zanrepo, '', '{node|short} {desc}\n') |
|
265 commits_to_pull = []; |
|
266 |
|
267 for line in data.split ('\n'): |
|
268 if line == '': |
|
269 continue |
|
270 #fi |
|
271 |
|
272 rex = re.compile (r'^([^ ]+) (.+)$') |
|
273 match = rex.match (line) |
|
274 failed = False |
|
275 |
|
276 if not match: |
|
277 chanlog ('malformed hg data: %s' % line) |
|
278 continue |
|
279 #fi |
|
280 |
|
281 try: |
|
282 commit_node = match.group (1) |
|
283 commit_message = match.group (2) |
|
284 |
|
285 rex = re.compile (r'^.*(fixes|resolves|addresses) ([0-9]+).*$') |
|
286 match = rex.match (commit_message) |
|
287 |
|
288 if not match: |
|
289 continue # no "fixes" message in the commit |
|
290 #fi |
|
291 |
|
292 ticket_id = int (match.group (2)) |
|
293 |
|
294 # Acquire additional data |
|
295 moredata = get_incoming_data (zanrepo, commit_node, |
|
296 '{author|nonempty}\n{date(date, \'%A %d %B %Y %T\')}\n{diffstat|nonempty}').split('\n') |
|
297 |
|
298 if len (moredata) != 3: |
|
299 chanlog ('commit %s: malformed hg data' % commit_node) |
|
300 #fi |
|
301 |
|
302 commit_author = moredata[0] |
|
303 commit_date = moredata[1] |
|
304 commit_diffstat = moredata[2] |
|
305 commit_email = "" |
|
306 |
|
307 ticket_data = suds_client.service.mc_issue_get (g_config['trackeruser'], g_config['trackerpassword'], ticket_id) |
|
308 if not ticket_data: |
|
309 chanlog ("error: commit %s: ticket %s not found" % (commit_node, ticket_id)) |
|
310 continue |
|
311 #fi |
|
312 |
|
313 # Remove the email address from the author if possible |
|
314 rex = re.compile (r'^(.+) <([^>]+)>$.*') |
|
315 match = rex.match (commit_author) |
|
316 if match: |
|
317 commit_author = match.group (1) |
|
318 commit_email = match.group (2) |
|
319 #fi |
|
320 |
|
321 rex = re.compile (r'([0-9]+): \+([0-9]+)/-([0-9]+)') |
|
322 match = rex.match (commit_diffstat) |
|
323 |
|
324 if match: |
|
325 modded = int (match.group (1)) |
|
326 added = int (match.group (2)) |
|
327 deleted = int (match.group (3)) |
|
328 commit_diffstat = "%s file%s modified, %s line%s added, %s line%s removed" % \ |
|
329 (modded if modded != 0 else 'no', |
|
330 's' if modded != 1 else '', |
|
331 added if added != 0 else 'no', |
|
332 's' if added != 1 else '', |
|
333 deleted if deleted != 0 else 'no', |
|
334 's' if deleted != 1 else '') |
|
335 #fi |
|
336 |
|
337 files_added = filter (None, get_incoming_data (zanrepo, commit_node, '{file_adds}').split ('\n')) |
|
338 files_removed = filter (None, get_incoming_data (zanrepo, commit_node, '{file_dels}').split ('\n')) |
|
339 files_changed = filter (None, get_incoming_data (zanrepo, commit_node, '{file_mods}').split ('\n')) |
|
340 |
|
341 # Compare the email addresses against known developer usernames |
|
342 commit_trackeruser = '' |
|
343 |
|
344 for developer, emails in g_config['developer_emails'].iteritems(): |
|
345 for email in emails: |
|
346 if commit_email == email: |
|
347 commit_trackeruser = developer |
|
348 break; |
|
349 #fi |
|
350 else: |
|
351 continue |
|
352 #done |
|
353 break |
|
354 #done |
|
355 |
|
356 if commit_trackeruser != '': |
|
357 commit_author += ' [%s]' % commit_trackeruser |
|
358 #fi |
|
359 |
|
360 message = 'Issue addressed by commit %s: [b][url="%s/commits/%s"]%s[/url][/b]' \ |
|
361 % (commit_node, repo_url, commit_node, commit_message) |
|
362 message += "\nCommitted by %s on %s\n\n%s" \ |
|
363 % (commit_author, commit_date, commit_diffstat) |
|
364 |
|
365 if len (files_added) > 0: |
|
366 message += "\nFiles added: %s" % ', '.join (files_added) |
|
367 #fi |
|
368 |
|
369 if len (files_removed) > 0: |
|
370 message += "\nFiles removed: %s" % ', '.join (files_removed) |
|
371 #fi |
|
372 |
|
373 if len (files_changed) > 0: |
|
374 message += "\nFiles changed: %s" % ', '.join (files_changed) |
|
375 #fi |
|
376 |
|
377 need_update = False |
|
378 |
|
379 # If not already set, set handler |
|
380 if not 'handler' in ticket_data: |
|
381 ticket_data['handler'] = {'name': commit_trackeruser} |
|
382 need_update = True |
|
383 #fi |
|
384 |
|
385 # Find out the status level of the ticket |
|
386 needs_testing_level = 70 |
|
387 |
|
388 if ticket_data['status']['id'] < needs_testing_level: |
|
389 ticket_data.status['id'] = needs_testing_level |
|
390 need_update = True |
|
391 #fi |
|
392 |
|
393 # Announce on IRC |
|
394 for irc_client in g_clients: |
|
395 for channel in irc_client.cfg['channels']: |
|
396 if 'btannounce' in channel and channel['btannounce'] == True: |
|
397 irc_client.privmsg (channel['name'], |
|
398 "%s: commit %s fixes issue %d: %s" |
|
399 % (repo_name, commit_node, ticket_id, commit_message)) |
|
400 irc_client.privmsg (channel['name'], |
|
401 "Read all about it here: " + irc_client.get_ticket_url (ticket_id)) |
|
402 #fi |
|
403 #done |
|
404 #done |
|
405 |
|
406 if need_update: |
|
407 suds_client.service.mc_issue_update (g_config['trackeruser'], g_config['trackerpassword'], ticket_id, ticket_data) |
|
408 #fi |
|
409 |
|
410 suds_client.service.mc_issue_note_add (g_config['trackeruser'], g_config['trackerpassword'], ticket_id, { 'text': message }) |
|
411 except Exception as e: |
|
412 chanlog ('Error: %s' % `e`) |
|
413 failed = True |
|
414 #tried |
|
415 |
|
416 if not failed: |
|
417 commits_to_pull.append (commit_node) |
|
418 #fi |
|
419 #done |
|
420 |
|
421 if len (commits_to_pull) > 0: |
|
422 pull_args = ['pull', '../zanstablecopy'] |
|
423 |
|
424 for commit in commits_to_pull: |
|
425 pull_args.append ('-r'); |
|
426 pull_args.append (commit); |
|
427 #done |
|
428 |
|
429 try: |
|
430 zanrepo.hg_command (*pull_args) |
|
431 |
|
432 # Also pull these commits to the zandronum main repository |
|
433 if usestable: |
|
434 devrepo = hgapi.Repo ('zandronum') |
|
435 devrepo.hg_command (*pull_args) |
|
436 #fi |
|
437 except Exception as e: |
|
438 chanlog ('Warning: unable to pull: %s' % `e`) |
|
439 #tried |
|
440 #fi |
|
441 #enddef |
208 |
442 |
209 # |
443 # |
210 # Main IRC client class |
444 # Main IRC client class |
211 # |
445 # |
212 class irc_client (asyncore.dispatcher): |
446 class irc_client (asyncore.dispatcher): |
550 raise logical_exception ('unknown key ' + key) |
789 raise logical_exception ('unknown key ' + key) |
551 |
790 |
552 self.privmsg (replyto, '%s is now %s' % (key, channel[key])) |
791 self.privmsg (replyto, '%s is now %s' % (key, channel[key])) |
553 |
792 |
554 save_config() |
793 save_config() |
|
794 elif command == 'devemail': |
|
795 check_admin (sender, ident, host, command) |
|
796 |
|
797 if len(args) < 2: |
|
798 raise logical_exception ("usage: .%s <user> <email>" % command) |
|
799 #fi |
|
800 |
|
801 if not 'developer_emails' in g_config: |
|
802 g_config['developer_emails'] = {} |
|
803 #fi |
|
804 |
|
805 user = ' '.join (args[0:-1]) |
|
806 |
|
807 if args[0] in g_config['developer_emails']: |
|
808 g_config['developer_emails'][user].append (args[-1]) |
|
809 else: |
|
810 g_config['developer_emails'][user] = [args[-1]] |
|
811 #fi |
|
812 |
|
813 self.privmsg (replyto, 'Developer emails for %s are now %s' % |
|
814 (user, ', '.join (g_config['developer_emails'][user]))) |
|
815 save_config() |
|
816 elif command == 'deldevemail': |
|
817 check_admin (sender, ident, host, command) |
|
818 |
|
819 if len(args) < 2: |
|
820 raise logical_exception ("usage: .%s <user> <email>" % command) |
|
821 #fi |
|
822 |
|
823 if not 'developer_emails' in g_config: |
|
824 g_config['developer_emails'] = {} |
|
825 #fi |
|
826 |
|
827 user = ' '.join (args[0:-1]) |
|
828 |
|
829 if user in g_config['developer_emails']: |
|
830 try: |
|
831 g_config['developer_emails'][user].remove (args[-1]) |
|
832 except: |
|
833 pass |
|
834 #tried |
|
835 |
|
836 if len (g_config['developer_emails'][user]) == 0: |
|
837 g_config['developer_emails'].pop (user) |
|
838 self.privmsg (replyto, 'No more developer emails for %s' % user) |
|
839 else: |
|
840 self.privmsg (replyto, 'Developer emails for %s are now %s' % |
|
841 (user, ', '.join (g_config['developer_emails'][user]))) |
|
842 #fi |
|
843 save_config() |
|
844 else: |
|
845 self.privmsg (replyto, 'There is no developer \'%s\'' % user) |
|
846 #fi |
|
847 elif command == 'listdevemails': |
|
848 check_admin (sender, ident, host, command) |
|
849 |
|
850 for dev, emails in g_config['developer_emails'].iteritems(): |
|
851 self.privmsg (replyto, 'Emails for %s: %s' % (dev, ', '.join (emails))) |
|
852 #done |
|
853 elif command == 'checkhg': |
|
854 check_admin (sender, ident, host, command) |
|
855 global repocheck_timeout |
|
856 repocheck_timeout = {'zandronum':0, 'zandronum-stable':0} |
|
857 process_zan_repo_updates (True) |
|
858 process_zan_repo_updates (False) |
555 elif command == 'die': |
859 elif command == 'die': |
556 check_admin (sender, ident, host, command) |
860 check_admin (sender, ident, host, command) |
557 quit() |
861 quit() |
558 elif command == 'convert': |
862 elif command == 'convert': |
559 if len(args) != 3 or args[1] != 'as': |
863 if len(args) != 3 or args[1] != 'as': |