- now is able to track zandronum mercurial repositories and react to 'fixes 0001234' trigger messages in them

Mon, 29 Sep 2014 03:26:45 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Mon, 29 Sep 2014 03:26:45 +0300
changeset 29
5767ee263b12
parent 28
30257c78904f
child 30
114afc110afd

- now is able to track zandronum mercurial repositories and react to 'fixes 0001234' trigger messages in them

cobalt.py file | annotate | diff | comparison | revisions
--- a/cobalt.py	Sat Sep 06 14:03:38 2014 +0300
+++ b/cobalt.py	Mon Sep 29 03:26:45 2014 +0300
@@ -103,7 +103,7 @@
 if suds_active:
 	try:
 		sys.stdout.write ('Retrieving latest tracker ticket... ')
-		btannounce_id = suds_client.service.mc_issue_get_biggest_id (g_config['trackeruser'], g_config ['trackerpassword'], 0)
+		btannounce_id = suds_client.service.mc_issue_get_biggest_id (g_config['trackeruser'], g_config['trackerpassword'], 0)
 		btannounce_active = True
 		bt_updatechecktimeout()
 		print btannounce_id
@@ -123,7 +123,7 @@
 		bt_updatechecktimeout()
 		newid = btannounce_id
 		try:
-			newid = suds_client.service.mc_issue_get_biggest_id (g_config['trackeruser'], g_config ['trackerpassword'], 0)
+			newid = suds_client.service.mc_issue_get_biggest_id (g_config['trackeruser'], g_config['trackerpassword'], 0)
 		except Exception as e:
 			pass
 
@@ -206,6 +206,240 @@
 	python = sys.executable
 	os.execl (python, python, * sys.argv)
 
+' Retrieves hg incoming from zandronum repository '
+def get_incoming_data (zanrepo, rev, template):
+	try:
+		if rev != '':
+			return zanrepo.hg_command ('incoming', '../zanstablecopy', '--quiet', '-r', rev, '--template', template)
+		else:
+			return zanrepo.hg_command ('incoming', '../zanstablecopy', '--quiet', '--template', template)
+		#fi
+	except:
+		pass
+	return ""
+#enddef
+
+' Check if a repository exists '
+def check_repo_exists (repo_name):
+	print 'Checking that %s exists...' % repo_name
+	repo_url = 'https://bitbucket.org/Torr_Samaho/' + repo_name
+	zanrepo = hgapi.Repo (repo_name)
+
+	try:
+		zanrepo.hg_command ('id', '.')
+	except hgapi.hgapi.HgException:
+		# If the repo does not exist, clone it. After cloning, there obviously
+		# are no more updates, so we'll be done.
+		try:
+			print 'Cloning %s...' % repo_name
+			zanrepo.hg_clone (repo_url, repo_name)
+			print 'Cloning done. No update checking needed.'
+		except Exception as e:
+			print 'Unable to clone %s from %s: %s' % (repo_name, repo_url, str (`e`))
+			quit(1)
+	#tried
+#enddef
+
+check_repo_exists ('zandronum')
+check_repo_exists ('zandronum-stable')
+
+repocheck_timeout = {'zandronum':(time.time()) + 15, 'zandronum-stable':(time.time() + 15)}
+
+' Retrieves and processes commits for zandronum repositories '
+' Ensure both repositories are OK before using this! '
+def process_zan_repo_updates (usestable):
+	global repocheck_timeout
+	global suds_client
+	global g_config
+	global g_clients
+
+	repo_name = 'zandronum' if usestable == False else 'zandronum-stable'
+	repo_url = 'https://bitbucket.org/Torr_Samaho/' + repo_name
+
+	if time.time() < repocheck_timeout[repo_name]:
+		return
+
+	repocheck_timeout[repo_name] = time.time() + (cfg ('hg_checkinterval', 15) * 60)
+	zanrepo = hgapi.Repo (repo_name)
+	data = get_incoming_data (zanrepo, '', '{node|short} {desc}\n')
+	commits_to_pull = [];
+
+	for line in data.split ('\n'):
+		if line == '':
+			continue
+		#fi
+
+		rex = re.compile (r'^([^ ]+) (.+)$')
+		match = rex.match (line)
+		failed = False
+
+		if not match:
+			chanlog ('malformed hg data: %s' % line)
+			continue
+		#fi
+
+		try:
+			commit_node = match.group (1)
+			commit_message = match.group (2)
+
+			rex = re.compile (r'^.*(fixes|resolves|addresses) ([0-9]+).*$')
+			match = rex.match (commit_message)
+
+			if not match:
+				continue # no "fixes" message in the commit
+			#fi
+
+			ticket_id = int (match.group (2))
+
+			# Acquire additional data
+			moredata = get_incoming_data (zanrepo, commit_node,
+				'{author|nonempty}\n{date(date, \'%A %d %B %Y %T\')}\n{diffstat|nonempty}').split('\n')
+
+			if len (moredata) != 3:
+				chanlog ('commit %s: malformed hg data' % commit_node)
+			#fi
+
+			commit_author = moredata[0]
+			commit_date = moredata[1]
+			commit_diffstat = moredata[2]
+			commit_email = ""
+
+			ticket_data = suds_client.service.mc_issue_get (g_config['trackeruser'], g_config['trackerpassword'], ticket_id)
+			if not ticket_data:
+				chanlog ("error: commit %s: ticket %s not found" % (commit_node, ticket_id))
+				continue
+			#fi
+
+			# 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)
+			#fi
+
+			rex = re.compile (r'([0-9]+): \+([0-9]+)/-([0-9]+)')
+			match = rex.match (commit_diffstat)
+
+			if match:
+				modded = int (match.group (1))
+				added = int (match.group (2))
+				deleted = int (match.group (3))
+				commit_diffstat = "%s file%s modified, %s line%s added, %s line%s removed" % \
+					(modded if modded != 0 else 'no',
+					's' if modded != 1 else '',
+					added if added != 0 else 'no',
+					's' if added != 1 else '',
+					deleted if deleted != 0 else 'no',
+					's' if deleted != 1 else '')
+			#fi
+
+			files_added = filter (None, get_incoming_data (zanrepo, commit_node, '{file_adds}').split ('\n'))
+			files_removed = filter (None, get_incoming_data (zanrepo, commit_node, '{file_dels}').split ('\n'))
+			files_changed = filter (None, get_incoming_data (zanrepo, commit_node, '{file_mods}').split ('\n'))
+
+			# Compare the email addresses against known developer usernames
+			commit_trackeruser = ''
+
+			for developer, emails in g_config['developer_emails'].iteritems():
+				for email in emails:
+					if commit_email == email:
+						commit_trackeruser = developer
+						break;
+					#fi
+				else:
+					continue
+				#done
+				break
+			#done
+
+			if commit_trackeruser != '':
+				commit_author += ' [%s]' % commit_trackeruser
+			#fi
+
+			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)
+
+			if len (files_added) > 0:
+				message += "\nFiles added: %s" % ', '.join (files_added)
+			#fi
+
+			if len (files_removed) > 0:
+				message += "\nFiles removed: %s" % ', '.join (files_removed)
+			#fi
+
+			if len (files_changed) > 0:
+				message += "\nFiles changed: %s" % ', '.join (files_changed)
+			#fi
+
+			need_update = False
+
+			# If not already set, set handler
+			if not 'handler' in ticket_data:
+				ticket_data['handler'] = {'name': commit_trackeruser}
+				need_update = True
+			#fi
+
+			# 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
+			#fi
+
+			# Announce on IRC
+			for irc_client in g_clients:
+				for channel in irc_client.cfg['channels']:
+					if 'btannounce' in channel and channel['btannounce'] == True:
+						irc_client.privmsg (channel['name'],
+							"%s: commit %s fixes issue %d: %s"
+							% (repo_name, commit_node, ticket_id, commit_message))
+						irc_client.privmsg (channel['name'],
+							"Read all about it here: " + irc_client.get_ticket_url (ticket_id))
+					#fi
+				#done
+			#done
+
+			if need_update:
+				suds_client.service.mc_issue_update (g_config['trackeruser'], g_config['trackerpassword'], ticket_id, ticket_data)
+			#fi
+
+			suds_client.service.mc_issue_note_add (g_config['trackeruser'], g_config['trackerpassword'], ticket_id, { 'text': message })
+		except Exception as e:
+			chanlog ('Error: %s' % `e`)
+			failed = True
+		#tried
+
+		if not failed:
+			commits_to_pull.append (commit_node)
+		#fi
+	#done
+
+	if len (commits_to_pull) > 0:
+		pull_args = ['pull', '../zanstablecopy']
+
+		for commit in commits_to_pull:
+			pull_args.append ('-r');
+			pull_args.append (commit);
+		#done
+
+		try:
+			zanrepo.hg_command (*pull_args)
+
+			# Also pull these commits to the zandronum main repository
+			if usestable:
+				devrepo = hgapi.Repo ('zandronum')
+				devrepo.hg_command (*pull_args)
+			#fi
+		except Exception as e:
+			chanlog ('Warning: unable to pull: %s' % `e`)
+		#tried
+	#fi
+#enddef
+
 #
 # Main IRC client class
 #
@@ -330,6 +564,11 @@
 			# Check for new issues on the bugtracker
 			bt_checklatest()
 
+			# Check for new commits in the repositories
+			# note: stable (i.e. True) goes first!
+			for n in [True, False]:
+				process_zan_repo_updates (n)
+
 	def channel_by_name (self, name):
 		for channel in self.channels:
 			if channel['name'].upper() == args[0].upper():
@@ -552,6 +791,71 @@
 				self.privmsg (replyto, '%s is now %s' % (key, channel[key]))
 
 			save_config()
+		elif command == 'devemail':
+			check_admin (sender, ident, host, command)
+
+			if len(args) < 2:
+				raise logical_exception ("usage: .%s <user> <email>" % command)
+			#fi
+
+			if not 'developer_emails' in g_config:
+				g_config['developer_emails'] = {}
+			#fi
+
+			user = ' '.join (args[0:-1])
+
+			if args[0] in g_config['developer_emails']:
+				g_config['developer_emails'][user].append (args[-1])
+			else:
+				g_config['developer_emails'][user] = [args[-1]]
+			#fi
+
+			self.privmsg (replyto, 'Developer emails for %s are now %s' %
+				(user, ', '.join (g_config['developer_emails'][user])))
+			save_config()
+		elif command == 'deldevemail':
+			check_admin (sender, ident, host, command)
+
+			if len(args) < 2:
+				raise logical_exception ("usage: .%s <user> <email>" % command)
+			#fi
+
+			if not 'developer_emails' in g_config:
+				g_config['developer_emails'] = {}
+			#fi
+
+			user = ' '.join (args[0:-1])
+
+			if user in g_config['developer_emails']:
+				try:
+					g_config['developer_emails'][user].remove (args[-1])
+				except:
+					pass
+				#tried
+
+				if len (g_config['developer_emails'][user]) == 0:
+					g_config['developer_emails'].pop (user)
+					self.privmsg (replyto, 'No more developer emails for %s' % user)
+				else:
+					self.privmsg (replyto, 'Developer emails for %s are now %s' %
+						(user, ', '.join (g_config['developer_emails'][user])))
+				#fi
+				save_config()
+			else:
+				self.privmsg (replyto, 'There is no developer \'%s\'' % user)
+			#fi
+		elif command == 'listdevemails':
+			check_admin (sender, ident, host, command)
+
+			for dev, emails in g_config['developer_emails'].iteritems():
+				self.privmsg (replyto, 'Emails for %s: %s' % (dev, ', '.join (emails)))
+			#done
+		elif command == 'checkhg':
+			check_admin (sender, ident, host, command)
+			global repocheck_timeout
+			repocheck_timeout = {'zandronum':0, 'zandronum-stable':0}
+			process_zan_repo_updates (True)
+			process_zan_repo_updates (False)
 		elif command == 'die':
 			check_admin (sender, ident, host, command)
 			quit()
@@ -654,7 +958,7 @@
 				break
 		else:
 			raise logical_exception ("unknown autoconnect entry %s" % (aconn))
-	
+
 	g_BotActive = True
 	asyncore.loop()
 except KeyboardInterrupt:

mercurial