Creating Hook Script
When you install Subversion it also installs sample hook scripts. These sample scripts are located in REPOS_PATH\hooks directory. For example, the pre-commit template is in PATH_TO_REPOS/hooks/pre-commit.tmpl. These templates contain instructions on what the hook script does and what parameters it can expect.Tip
The hook file must be an executable. Windows uses file extensions to determine whether or not a program is executable, so you would need to supply a program whose basename is the name of the hook, and whose extension is one of the special extensions recognized by Windows for executable programs, such as .exe or .com for programs, and .bat for batch files.
The hook file must be an executable. Windows uses file extensions to determine whether or not a program is executable, so you would need to supply a program whose basename is the name of the hook, and whose extension is one of the special extensions recognized by Windows for executable programs, such as .exe or .com for programs, and .bat for batch files.
The pre-commit Hook
The pre-commit hook gives you an opportunity to catch the transaction just before it becomes a revision. Typically, this hook is used to protect against commits that are disallowed due to content or location (for example, your organisation might require that all commits to a certain branch include a ticket number from the bug tracker, or that the incoming log message is non-empty). Subversion passes this hook two parameters:- The path to the root of the repository
- The transaction identifier
The Hook Script
The hook script described here enforces following rules.- Non empty commit message.
- Commit message must begin with an Issue Id followed by a space and hyphen
- The Issue id specified must be a valid issue in Issue System(JIRA)
- The Issue must have been assigned to the committer
<package> <job id="hPreCommit"> <reference object="WScript.Shell"/> <reference object="Microsoft.XMLHTTP"/> <reference object="Scripting.Dictionary"/> <script language="VBScript"> '******************************************************************************* '* NAME : pre-commit.vbs '* AUTHOR : Prasad P. Khandekar '* CREATED ON : April 09, 2008 16:02 '* COPYRIGHT : © 2008, Prasad P. Khandekar '* PURPOSE : Script to check whether the commit is accompanied by a commit '* message and that the message contains the valid open JIRA issue '* id. The script is passed two parameters namely '* '* [1] REPOS-PATH (the path to this repository) '* [2] TXN-NAME (the name of the txn about to be committed) '******************************************************************************* Option Explicit Public Const vbSpace = " " Public Const SVNLOOK_PATH = "<PATH TO SVNLOOK.EXE FILE>" Public Const JIRA_USER = "<JIR_AUSER>" Public Const JIRA_PASS = "<JIRA_PASSOWRD>" Public Const JIRA_HOST = "<JIRA_HOSTNAME_OR_IP>" Public Const JIRA_ENDPOINT = "http://JIRA_HOSTNAME_OR_IP/JIRA/rpc/soap/jirasoapservice-v2?wsdl" Private retVal Private strRepoPath Private strRevision Private colArgs Public wshShell Set wshShell = WScript.CreateObject("WScript.Shell") Set colArgs = WScript.Arguments If (colArgs.Count < 2) Then wshShell.LogEvent 4, "Repository path and txn name are missing!" WScript.StdErr.WriteLine("Repository path and commit transaction id are missing!") Set colArgs = Nothing Set wshShell = Nothing WScript.Quit(1) End If strRepoPath = colArgs(0) strRevision = colArgs(1) Set colArgs = Nothing wshShell.LogEvent 4, "Repository : " & strRepoPath & ", Revision : " & strRevision retVal = doMain(strRepoPath, strRevision) WScript.Quit(retVal) Function doMain(strPath, strRev) Dim strLog, strAuthor, strJiraId Dim intPos Dim char strLog = runCMD(SVNLOOK_PATH & " log " & strPath & " --transaction " & strRev) strAuthor = runCMD(SVNLOOK_PATH & " author " & strPath & " --transaction " & strRev) If (IsNull(strAuthor) Or IsEmpty(strAuthor)) Then wshShell.LogEvent 4, "Unable to find author of the commit transaction [" & _ strRev & "] in repository '" & strPath & "'!" WScript.StdErr.WriteLine("Unable to find author of the commit transaction [" & _ strRev & "] in repository '" & strPath & "'!") doMain = 2 Exit Function End If If (IsNull(strLog) Or IsEmpty(strLog)) Then wshShell.LogEvent 4, "Commit message not specified for commit transaction [" & _ strRev & "] in repository '" & strPath & "'!" WScript.StdErr.WriteLine("Commit message not specified for commit transaction [" & _ strRev & "] in repository '" & strPath & "'!") doMain = 3 Exit Function End If strJiraId = "" For intPos = 1 To Len(strLog) char = Mid(strLog, intPos, 1) If (char = vbSpace) Then Exit For strJiraId = strJiraId & char Next If (IsNull(strJiraId) Or IsEmpty(strJiraId)) Then wshShell.LogEvent 4, "Jira Issue id is not specifed in commit message!" WScript.StdErr.WriteLine("Jira Issue id is not specifed in commit message!") doMain = 4 Exit Function End If If (checkJIRA(strJiraId, strAuthor)) Then doMain = 0 Else wshShell.LogEvent 4, "Issue with Id '" & strJiraId & "' does not exists or is not open!" doMain = 5 End If End Function Function runCMD(strRunCmd) Dim strOut Dim objExec wshShell.LogEvent 4, "Running Command : " & strRunCmd Set objExec = wshShell.Exec(strRunCmd) Do While objExec.Status = 0 WScript.Sleep 100 Loop strOut = objExec.StdOut.ReadAll() Set objExec = Nothing wshShell.LogEvent 4, "Output of run command : " & strOut strOut = Trim(strOut) If (Len(strOut) = 0) Then runCMD = Empty Else runCMD = strOut End If End Function Function checkJIRA(strId, strAuthor) Dim ws Dim strTok Set ws = new WebService ws.Url = JIRA_ENDPOINT strTok = doLogin(ws) If (IsNull(strTok) Or IsEmpty(strTok)) Then wshShell.LogEvent 4, "Unable to login to JIRA!" WScript.StdErr.WriteLine("Unable to login to JIRA!") checkJIRA = False Else ws.Parameters.Clear checkJIRA = getIssue(ws, strTok, strId, strAuthor) doLogout ws, strTok End If Set ws = Nothing End Function Function doLogin(ws) Dim xmlDoc Dim domNode ' Login and get the token. ws.Method = "login" ws.Parameters.Add "in0", JIRA_USER ws.Parameters.Add "in1", JIRA_PASS ws.execute Set xmlDoc = ws.Response Set domNode = xmlDoc.selectSingleNode("/soapenv:Envelope/soapenv:Body/ns1:loginResponse/loginReturn") If Not (domNode Is Nothing) Then doLogin = domNode.text Set domNode = Nothing End If Set xmlDoc = Nothing End Function Sub doLogout(ws, strTok) ws.Method = "logout" ws.Parameters.Add "in0", strTok ws.execute End Sub Function getIssue(ws, strTok, strId, strAuthor) Dim domNode Dim child Dim xmlDoc Dim buff ws.Method = "getIssue" ws.Parameters.Add "in0", strTok ws.Parameters.Add "in1", strId ws.execute Set xmlDoc = ws.Response Set domNode = xmlDoc.selectSingleNode("/soapenv:Envelope/soapenv:Body/ns1:getIssueResponse") If Not (domNode Is Nothing) Then Set domNode = xmlDoc.selectSingleNode("/soapenv:Envelope/soapenv:Body/multiRef[@id='id0']/assignee") If (domNode.text = strAuthor) Then getIssue = True Else WScript.StdErr.WriteLine("Issue " & strId & " is not assigned to " & strAuthor) getIssue = False End If Else buff = "" Set domNode = xmlDoc.selectSingleNode("/soapenv:Envelope/soapenv:Body/soapenv:Fault") For Each child In domNode.childNodes If (child.baseName = "faultcode") Then buff = buff & child.text & " - " ElseIf (child.baseName = "faultstring") Then buff = buff & child.text End If Next WScript.StdErr.WriteLine(buff) getIssue = False End If Set domNode = Nothing Set xmlDoc = Nothing End Function Class WebService Private m_strUrl Private m_strMethod Private m_xmlResponse Private m_objParams Public Property Get Url Url = m_strUrl End Property Public Property Let Url(strUrl) m_strUrl = strUrl End Property Public Property Get Method Method = m_strMethod End Property Public Property Let Method(strMethod) m_strMethod = strMethod End Property Public Property Get Response Set Response = m_xmlResponse End Property Public Property Get Parameters Set Parameters = m_objParams End Property Public Function execute() Dim xmlhttp Set xmlhttp = CreateObject("Microsoft.XMLHTTP") xmlhttp.open "POST", m_strUrl & "/" & m_strMethod, False xmlhttp.setRequestHeader "Content-Type", "text/xml; charset=utf-8" xmlhttp.setRequestHeader "SOAPAction", m_strUrl & "/" & m_strMethod xmlhttp.send m_objParams.toString(m_strUrl, m_strMethod) Set m_xmlResponse = xmlhttp.responseXML Set xmlhttp = Nothing End Function Private Sub Class_Initialize() Set m_objParams = New wsParameters End Sub Private Sub Class_Terminate() Set m_objParams = Nothing End Sub End Class Class wsParameters Private m_colParams Public Function toString(strUrl, strMethod) Dim param Dim buffer buffer = "<?xml version=""1.0"" encoding=""utf-8""?>" & _ "<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""" & _ " xmlns:xsd=""http://www.w3.org/2001/XMLSchema""" & _ " xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" & _ " <soap:Body>" & _ " <" & strMethod & " xmlns=""" & strMethod & """>" For Each param In m_colParams.Items buffer = buffer & param.toString Next buffer = buffer & "</" & strMethod & "></soap:Body></soap:Envelope>" toString = buffer End Function Public Sub Clear Set m_colParams = Nothing Set m_colParams = CreateObject("Scripting.Dictionary") End Sub Public Sub Add(pKey, pValue) Dim param Set param = New wsParameter param.Key = pKey param.Value = pValue m_colParams.Add m_colParams.count + 1, param Set param = Nothing End Sub Public Function Item(nKey) Set Item = m_colParams.Item(nKey) End Function Public Function ExistsXKey(pKey) Dim param For Each param In m_colParams.Items If param.Key = pKey Then ExistsXKeyword = True Exit For End If Next End Function Public Sub Remove(nKey) m_colParams.Remove(nKey) End Sub Public Function Count() Count = m_colParams.count End Function Private Sub Class_Initialize() Set m_colParams = CreateObject("Scripting.Dictionary") End Sub Private Sub Class_Terminate() Set m_colParams = Nothing End Sub End Class Class wsParameter Private m_strKey Private m_strValue Public Property Get Key Key = m_strKey End Property Public Property Let Key(strKey) m_strKey = strKey End Property Public Property Get Value Value = m_strValue End Property Public Property Let Value(strVal) m_strValue = strVal End Property Public Function toString() toString = "<" & m_strKey & ">" & m_strValue & "</" & m_strKey & ">" End Function End Class </script> </job> </package>
JIRA® Integration
The integration with JIRA is done via web service using SOAP messages. This ofcourse requires that the xmlrpc plugin be enabled on JIRA server. The script makes use of MSXML.HTTP object to invoke the JIRA web service. The commit log message and the committer are found using the svnlook.exe program which comes with Subversion software. The web service invokation is done by class named WebService which is actually a adapatation of code published at CDYNE Wiki. (The code found on this site is good enough to make an POST request but fails to invoke a SOAP enabled web service.) The code calls following methods in JIRA web service.- login - This method must be called to ensure that we get an authentication token. The token returned by this method is required in subsequent calls. This method takes following two parameters and upon successful logon returns the authentication token.
- in0 - The user name
- in1 - The plain text user password
- getIssue - This method is used to retrieve the issue details given it's id. This method takes following two parameters and returns issue details if the requesting user has view permissions and the issue with the supplied id exists.
- in0 - The authentication token
- in1 - The issue id
- logout - The method is used to logout the user. This method takes only one parameter namely the authentication token.
pre-commit.bat
As mentioned earlier the Subversion server is capable of calling the executable scripts only. Since the pre-commit hook is written in VBScript we need a driving script. So here is the driving script.@ECHOSETLOCAL
SET PATH=C:\Windows;C:\Windows\system32;D:\SCC\SVN146\bin;
cscript.exe //NoLogo H:\SVNRepo\docman\hooks\pre-commit.wsf %1 %2
IF ERRORLEVEL 1 GOTO fail
:success
EXIT 0
:fail
EXIT 1
4 comments:
Great script overall!
Found a bug. A fault is always raised during doLogout(ws, strTok).
The webservice parameters need to be reset since doLogout accepts only one parameter.
Sub doLogout(ws, strTok)
ws.Parameters.Clear
ws.Method = "logout"
ws.Parameters.Add "in0", strTok
ws.Execute
End Sub
Hey thanks for the post. I am having a problem communicating over https and have found the error I am getting is it is due to https. Any advice?
Thanks,
Rob
Hello there,
Very interesting article. Could you please help me with my issues.
I need to check prior the directory size and informaing the user and also file extensions.
Give me some hints please
thanks for sharing
Post a Comment