Wed, 31 Dec 2014 11:40:46 -0500
- added page system to prevent commands from printing too much output
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 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()