Skip to content

Instantly share code, notes, and snippets.

@peterfroehlich
Last active June 2, 2022 19:47
Show Gist options
  • Save peterfroehlich/6163017 to your computer and use it in GitHub Desktop.
Save peterfroehlich/6163017 to your computer and use it in GitHub Desktop.
Mercurial hook to parse commit messages of just pushed commits for Youtrack IDs and post the commits and a link to the mercurial web interface as comments into the matching Youtrack ticket.
#!/usr/bin/env python
'''Get mercurial hook and post commit message with link to hg page to youtrack issue as comment.
Installation:
Add hook entry in your hgrc:
[hooks]
incoming = python:/<path>/commit-to-youtrack.py:hook
[youtrack-hook]
ID = EXPL,EG,OPS
AGGRO = False
Usage:
Add the appropriate Youtrack ID to your commit message:
hg commit -m "some stuff EXPL-21"
This works with multiple IDs as well:
hg commit -m "more stuff EXPL-21 EG-3"
Ticket enforcement:
Set config option AGGRO to True if you want to display a reminder
not to push commits without a ticket tag in the commit comment.
Peter Froehlich 08/2013
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
from urllib import urlencode
import pycurl
from sys import exit
# BaseURL of Youtrack
BASE="http://youtrack.meh.com/youtrack"
# BaseURL of HG
HGPAGE="http://hg.meh.com/"
# The Youtrack user. Need rights to "update projects" and "read users"
YTUSER="api"
YTPW="api"
# Should sending mails about the comments be disabled?
DISABLENOTIFICATIONS=True
# Ignore
CFILE='cookies.txt'
def hook(ui, repo, hooktype, node=None, **kwargs):
'''Entrypoint for hg hook'''
if node is None:
raise util.Abort(_('hook type %s does not pass a changeset id') % hooktype)
REV=str(repo.changectx(node).rev())
CTX=repo[REV]
YTIDLIST = ui.config('youtrack-hook', 'ID', None)
AGGROERROR = ui.config('youtrack-hook', 'AGGRO', False)
if YTIDLIST == None:
print '''\nNo youtrack ID defined in hgrc. Please define IDs like this:
[youtrack-hook]
ID = EXPL,EG,OPS
Breaking up.\n'''
exit()
YTIDS=YTIDLIST.split(",")
ISSUES=[]
MSG=CTX.description()
for YTID in YTIDS:
for WORD in MSG.split(" "):
WORD = WORD.rstrip(":").strip()
if WORD.startswith(YTID) and "-" in WORD and WORD.split("-")[1].isdigit():
print "Youtrack ID "+WORD+" found."
ISSUES+=[WORD]
if len(ISSUES) > 0:
REPO=str(repo.root).split("/")[-1]
BRANCH=CTX.branch()
NODE=CTX.node()
DATE=CTX.date()
USER=CTX.user()
ID=str(CTX)
URL=HGPAGE+REPO+"/rev/"+ID
MSG="Commit "+REV+" in "+REPO+"/"+BRANCH+"\nUser: "+USER+"\nMessage: "+MSG+"\n"+URL
for ISSUE in ISSUES:
postToYoutrack(ISSUE,MSG,USER)
else:
if AGGROERROR:
print ''' /) ,-^ ^-.
// / \\
.-------| |--------------/ __ __ \-------------------.__
|WTF!WTF| |>>>>>>>>>>>>> | />>\ />>\ |>>>>>>>>>>>>>>>>>>>>>>:>
`-------| |--------------| \__/ \__/ |-------------------'^^
\\\\ \ /|\ /
\) \ \_/ /
| |
|+H+H+H+|
\ /
^-----^
MISSING YOUTRACK-ID IN COMMIT MSG. WHAT. ARE. YOU. DOING?!
'''
class ContentHandler:
'''Simple content handler for curl'''
def __init__(self):
self.CONTENT=""
def SaveContent(self,IN):
self.CONTENT=self.CONTENT+str(IN)
def ReturnContent(self):
return self.CONTENT
def xPost(CURLWORKER,URL,DATA):
'''http POST methode'''
ch=ContentHandler()
cw=CURLWORKER
post = urlencode(DATA)
cw.setopt(cw.URL, URL)
cw.setopt(pycurl.POST, 1)
cw.setopt(pycurl.POSTFIELDS, post)
cw.setopt(pycurl.WRITEFUNCTION, ch.SaveContent)
cw.perform()
cw.setopt(pycurl.POST, 0)
return ch.CONTENT
def login(CURLWORKER):
'''Login to Youtrack'''
cw=CURLWORKER
cw.setopt(pycurl.COOKIEFILE, CFILE)
CRED={'login' : YTUSER , 'password' : YTPW}
return xPost(cw,BASE+"/rest/user/login",CRED)
def xGet(CURLWORKER,URL):
'''http GET methode'''
ch=ContentHandler()
cw=CURLWORKER
cw.setopt(cw.URL, URL)
cw.setopt(cw.WRITEFUNCTION, ch.SaveContent)
cw.perform()
return ch.CONTENT
def getUser(CURLWORKER,USER):
'''Look if Youtrack finds the user from his email address'''
for WORD in USER.split(" "):
if "@" in WORD:
ACCOUNT=WORD.strip("<").strip(">")
OUT=xGet(CURLWORKER,BASE+"/rest/admin/user?q="+ACCOUNT)
for WORD in OUT.split(" "):
if WORD.startswith("login="):
return WORD.split("=")[1].strip('"')
return YTUSER
def postToYoutrack(YTID,MSG,USER):
'''Post a comment to a given Youtrack ID as a User'''
# Possible options in data (all lower caps)
# issue: Youtrack ID
# command: a command to apply. Vaild values: bug, task, exception, .. see: http://confluence.jetbrains.net/display/YTD4/Search+and+Command+Attributes
# comment: add comment
# group: Usergroup to whom this should be visible
# disableNotifications: If set 'true' then no notifications about changes made with the specified command will be send. By default, is 'false'.
# runAs: userlogin to use
# init curl
cw=pycurl.Curl()
if ">ok<" in login(cw):
# Build data
ACCOUNT=getUser(cw,USER)
data={"comment" : MSG , "disableNotifications" : DISABLENOTIFICATIONS , "runAs" : ACCOUNT }
OUT=xPost(cw,BASE+"/rest/issue/"+YTID+"/execute",data)
if OUT != "":
print "Failed to transmit to youtrack:\n"+OUT
else:
print "Error connecting to Youtrack"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment