Thu, 15 Jan 2015 19:06:14 +0200
- added bridging functionality
import hgapi import time import re import bt as Bt import irc as Irc import os from datetime import datetime from configfile import Config g_needCommitsTxtRebuild = True def color_for_repo (repo_name): if repo_name == 'zandronum': color = 4 elif repo_name == 'zandronum-stable': color = 2 elif repo_name == 'zandronum-sandbox': color = 7 elif repo_name == 'zandronum-sandbox-stable': color = 3 elif repo_name == 'zfc9000': color = 6 else: color = 0 return color def prettify_bookmarks (bookmarks): return "\0036 [\002%s\002]" % bookmarks def make_commits_txt(): global g_needCommitsTxtRebuild if g_needCommitsTxtRebuild == False: return print 'Building commits.txt...' # Update zandronum-everything repo = hgapi.Repo ('zandronum-everything') repo.hg_command ('pull', '../zandronum-sandbox') repo.hg_command ('pull', '../zandronum-sandbox-stable') data = repo.hg_command ('log', '--template', '{node} {date|hgdate}\n') f = open ('commits.txt', 'w') for line in data.split ('\n'): if line == '': continue words = line.split (' ') timestamp = int (words[1]) f.write ('%s %s\n' % (words[0], datetime.utcfromtimestamp (timestamp).strftime ('%y%m%d-%H%M'))) f.close() g_needCommitsTxtRebuild = False ' Check if a repository exists ' def check_repo_exists (repo_name, repo_owner): print 'Checking that %s exists...' % repo_name repo_url = 'https://bitbucket.org/%s/%s' % (repo_owner, repo_name) zanrepo = hgapi.Repo (repo_name) try: zanrepo.hg_command ('id', '.') except hgapi.hgapi.HgException: # If the repo does not exist, clone it. zandronum-everything can be spawned # off other repos though if repo_name == 'zandronum-everything': if not os.path.exists (repo_name): os.makedirs (repo_name) global g_needCommitsTxtRebuild g_needCommitsTxtRebuild = True print 'Init %s' % repo_name zanrepo.hg_command ('init') print 'Cloning zandronum-sandbox into %s' % repo_name zanrepo.hg_command ('pull', '../zandronum-sandbox') print 'Cloning zandronum-sandbox-stable into %s' % repo_name zanrepo.hg_command ('pull', '../zandronum-sandbox-stable') print 'Done' make_commits_txt() return try: print 'Cloning %s...' % repo_name zanrepo.hg_clone (repo_url, repo_name) print 'Cloning done.' except Exception as e: print 'Unable to clone %s from %s: %s' % (repo_name, repo_url, str (`e`)) quit (1) class HgProcessError (Exception): def __init__ (self, value): self.message = value def __str__ (self): return self.message def announce_ticket_resolved (ticket_id, commit_node): ticket_id = int (ticket_id) try: repo_name = 'zandronum-stable' zanrepo = hgapi.Repo (repo_name) zanrepo.hg_command ('log', '-r', commit_node) except hgapi.hgapi.HgException: try: repo_name = 'zandronum' zanrepo = hgapi.Repo (repo_name) zanrepo.hg_command ('log', '-r', commit_node) except hgapi.hgapi.HgException: raise HgProcessError ('unknown commit %s' % commit_node) repo_url = 'https://bitbucket.org/Torr_Samaho/%s' % repo_name # Acquire additional data moredata = get_commit_data (zanrepo, commit_node, '{author|nonempty}\n{date(date, \'%A %d %B %Y %T\')}').split('\n') if len (moredata) != 2: raise HgProcessError ('malformed hg data while processing %s' % commit_node) commit_author = moredata[0] commit_date = moredata[1] commit_email = "" commit_message = zanrepo.hg_command ('log', '-r', commit_node, '--template', '{desc}') try: ticket_data = Bt.get_issue (ticket_id) except Exception as e: raise HgProcessError ("error while processing %s: %s" % (commit_node, e)) # Remove the email address from the author if possible rex = re.compile (r'^(.+) <([^>]+)>$.*') match = rex.match (commit_author) if match: commit_author = match.group (1) commit_email = match.group (2) commit_diffstat = zanrepo.hg_command ('diff', '--change', commit_node, '--stat') if len(commit_diffstat) > 0: # commit_diffstat = 'Changes in files:\n[code]\n' + commit_diffstat + '\n[/code]' commit_diffstat = 'Changes in files:\n' + bbcodify(commit_diffstat) else: commit_diffstat = 'No changes in files.' # Compare the email addresses against known developer usernames commit_trackeruser = Config.find_developer_by_email (commit_email) if commit_trackeruser != '': commit_author += ' [%s]' % commit_trackeruser commit_message = commit_message.replace ("\n", " ") message = 'Issue addressed by commit %s: [b][url=%s/commits/%s]%s[/url][/b]' \ % (commit_node, repo_url, commit_node, commit_message) message += "\nCommitted by %s on %s\n\n%s" \ % (commit_author, commit_date, commit_diffstat) need_update = False # If not already set, set handler if not 'handler' in ticket_data: ticket_data['handler'] = {'name': commit_trackeruser} need_update = True # Find out the status level of the ticket needs_testing_level = 70 if ticket_data['status']['id'] < needs_testing_level: ticket_data.status['id'] = needs_testing_level need_update = True # Set target version if not set if not 'target_version' in ticket_data: ticket_data['target_version'] = '1.4' if repo_name == 'zandronum-stable' else '2.0' need_update = True elif (ticket_data['target_version'] == '2.0' or ticket_data['target_version'] == '2.0-beta') \ and repo_name == 'zandronum-stable': # Target version was 2.0 but this was just committed to zandronum-stable, adjust ticket_data['target_version'] = '1.4' need_update = True elif ticket_data['target_version'] == '2.0-beta': # Fix target version from 2.0-beta to 2.0 ticket_data['target_version'] = '2.0' need_update = True # Announce on IRC for irc_client in Irc.all_clients: for channel in irc_client.channels: if channel.get_value ('btannounce', default=True): irc_client.privmsg (channel.get_value ('name'), "\003%d%s\003: commit\0035 %s\003 addresses issue\002\0032 %d\002" % (color_for_repo (repo_name), repo_name, commit_node, ticket_id)) irc_client.privmsg (channel.get_value ('name'), "Read all about it here: " + Bt.get_ticket_url (ticket_id)) if need_update: # We need to remove the note data, otherwise the ticket notes # will get unnecessary updates. WTF, MantisBT? ticket_data.notes = [] Bt.update_issue (ticket_id, ticket_data) Bt.post_note (ticket_id, message) def init(): check_repo_exists ('zandronum', 'Torr_Samaho') check_repo_exists ('zandronum-stable', 'Torr_Samaho') check_repo_exists ('zandronum-sandbox', 'crimsondusk') check_repo_exists ('zandronum-sandbox-stable', 'crimsondusk') check_repo_exists ('zandronum-everything', '') check_repo_exists ('zfc9000', 'crimsondusk') global repocheck_timeout repocheck_timeout = (time.time()) + 15 def get_commit_data (zanrepo, rev, template): return zanrepo.hg_command ('log', '-l', '1', '-r', rev, '--template', template) def decipher_hgapi_error (e): # Blah, hgapi, why must your error messages be so mangled? try: rawmsg = e.message.replace('\n', '').replace('" +','').replace('\t','') errmsg = re.compile (r'.*: tErr: (.*)Out:.*').match (rawmsg).group (1) return [True, errmsg] except: return [False, ''] def bbcodify (commit_diffstat): result = '' for line in commit_diffstat.split('\n'): rex = re.compile (r'^(.*)\|(.*) (\+*)(-*)(.*)$') match = rex.match (line) if match: line = '%s|%s [color=#5F7]%s[/color][color=#F53]%s[/color]%s\n' \ % (match.group (1), match.group (2), match.group (3), match.group (4), match.group (5)) # Tracker doesn't seem to like empty color tags line = line.replace ('[color=#5F7][/color]', '').replace ('[color=#F53][/color]', '') result += line return result def poll(): global repocheck_timeout if time.time() < repocheck_timeout: return for n in ['zandronum-stable', 'zandronum', 'zandronum-sandbox', 'zandronum-sandbox-stable', 'zfc9000']: poll_one_repo (n) hgns = Config.get_node ('hg') repocheck_timeout = time.time() + hgns.get_value ('checkinterval', default=15) * 60 def poll_one_repo (repo_name): global repocheck_timeout hgns = Config.get_node ('hg') if not hgns.get_value ('track', default=True): return usesandbox = repo_name == 'zandronum-sandbox' or repo_name == 'zandronum-sandbox-stable' repo_owner = 'Torr_Samaho' if (not usesandbox and repo_name != 'zfc9000') else 'crimsondusk' zanrepo = hgapi.Repo (repo_name) commit_data = [] delimeter = '@@@@@@@@@@' # print 'Checking %s for updates' % repo_name try: data = zanrepo.hg_command ('incoming', '--quiet', '--template', '{node|short} {desc}' + delimeter) except hgapi.hgapi.HgException as e: deciphered = decipher_hgapi_error (e) if deciphered[0] and len(deciphered[1]) > 0: Irc.broadcast ("error while using hg import on %s: %s" % (repo_name, deciphered[1])) return except Exception as e: Irc.broadcast ("%s" % `e`) return for line in data.split (delimeter): if line == '': continue rex = re.compile (r'([^ ]+) (.+)') match = rex.match (line) failed = False if not match: Irc.broadcast ('malformed hg data: %s' % line) continue commit_node = match.group (1) commit_message = match.group (2) commit_data.append ([commit_node, commit_message]) process_new_commits (repo_name, commit_data) def process_new_commits (repo_name, commit_data): if len (commit_data) == 0: return usestable = repo_name == 'zandronum-stable' usesandbox = repo_name == 'zandronum-sandbox' or repo_name == 'zandronum-sandbox-stable' repo_owner = 'Torr_Samaho' if (not usesandbox and repo_name != 'zfc9000') else 'crimsondusk' repo_url = 'https://bitbucket.org/%s/%s' % (repo_owner, repo_name) num_commits = 0 zanrepo = hgapi.Repo (repo_name) print '%d new commits on %s' % (len (commit_data), repo_name) # Drop commits that we already have i = 0 while i < len (commit_data): try: zanrepo.hg_command ('log', '-r', commit_data[i][0]) del commit_data[i] except: i += 1 pull_args = []; for commit in commit_data: pull_args.append ('-r'); pull_args.append (commit[0]); print 'Pulling new commits...' try: zanrepo.hg_command ('pull', *pull_args) if repo_name[0:9] == 'zandronum': # Also pull these commits to the zandronum main repository if usestable: devrepo = hgapi.Repo ('zandronum') devrepo.hg_command ('pull', '../zandronum-stable', *pull_args) # Pull everything into sandboxes too if not usesandbox: devrepo = hgapi.Repo ('zandronum-sandbox') devrepo.hg_command ('pull', '../%s' % repo_name, *pull_args) devrepo = hgapi.Repo ('zandronum-sandbox-stable') devrepo.hg_command ('pull', '../%s' % repo_name, *pull_args) devrepo = hgapi.Repo ('zandronum-everything') devrepo.hg_command ('pull', '../%s' % repo_name, *pull_args) global g_needCommitsTxtRebuild g_needCommitsTxtRebuild = True except Exception as e: Irc.broadcast ('Warning: unable to pull: %s' % `e`) return for commit in commit_data: commit_node = commit[0] commit_message = commit[1] print 'Processing new commit %s...' % commit_node try: data = get_commit_data (zanrepo, commit_node, '{author}@@@@@@@@@@{bookmarks}').split ("@@@@@@@@@@") commit_author = data[0] commit_bookmarks = prettify_bookmarks (data[1]) commit_url = '%s/commits/%s' % (repo_url, commit_node) commit_email = '' # Remove the email address from the author if possible rex = re.compile (r'^(.+) <([^>]+)>$.*') match = rex.match (commit_author) if match: commit_author = match.group (1) commit_email = match.group (2) commit_trackeruser = Config.find_developer_by_email (commit_email) committer = commit_trackeruser if commit_trackeruser != '' else commit_author for irc_client in Irc.all_clients: for channel in irc_client.channels: if not channel.get_value ('btannounce', False): continue if (not usesandbox and repo_name != 'zfc9000') or channel.get_value ('btprivate', False): irc_client.privmsg (channel.get_value ('name'), "\003%d%s\003: new commit\0035 %s%s\003 by\0032 %s\003: %s" % (color_for_repo (repo_name), repo_name, commit_node, commit_bookmarks, committer, commit_url)) for line in commit_message.split ('\n'): irc_client.privmsg (channel.get_value ('name'), line) if not usesandbox: rex = re.compile (r'^.*(fixes|resolves|addresses|should fix) ([0-9]+).*$') match = rex.match (commit_message) if match: announce_ticket_resolved (match.group (2), commit_node) num_commits += 1 except Exception as e: Irc.broadcast ('Error while processing %s: %s' % (commit_node, e)) continue def force_poll(): global repocheck_timeout repocheck_timeout = 0 poll()