Tuesday, December 18, 2012
Amazing Battery
Friday, August 22, 2008
SVN-JIRA Pre-Commit Hook
Earlier I had posted a VBScript version of the same integration Hook. This time I have come up with the PHP version. I know there is already a plugin available for JIRA, which does exactly the same thing. However that plugin needs to be installed on both the svn server and the JIRA server. It adds new methods to the JIRA web service.
The PHP version and the VBScript version which I presented earlier tends to be much more simpler, in that both of these hooks needs to be installed on svn server only and configuring them is a matter of changing one file, the hook script it-self. Here is the PHP code of the Hook script.
Linux Installation
Simply create a file named "pre-commit" in "hooks" directory of your svn repository and paste the following code in it. Change the values for following constants.
If you have multiple svn repositories then you will have to copy this file into those many directories.
- JIRA_SERVER - This is the address of the JIRA web service. You need to change the IP address and port.
- SVN_USER - The id of the JIRA user. This user must have the read access to all projects defined in JIRA and have corresponding svn repository for which the control needs to be enforced.
- SVN_PASSWORD - The password for the JIRA user.
- SVNLOOK_PATH - The full path to the "svnlook" executable.
After saving do not forget to change the permissions of the file. (chmod 755 pre-commit).
Pre-requisites
- Subversion 1.4.6 or higher.
- PHP 5.2.6 or higher.
- PHP soap extension.
- JIRA 3.x
- JIRA rpc plugin enabled.
Source Code
1: #!/usr/bin/php2: <?php3: //------------------------------------------------------------------------------4: // A php schell script to authorize commit only if it is accompanied by a log5: // message in <code>^([A-Z]*)\-(\d*)\s?\-\s?(.*)</code> format and that the issue6: // id present in the log message is valid issue (Open, In Progress or Re-Opened)7: // in JIRA and is assigned to the committer.8: //9: // e.g. ABC-1345 - Changed the implementation of login metod.10: //11: // AUTHOR : Prasad P. Khandekar12: // CREATED: August 22, 200813: // VERSION: 1.014: //------------------------------------------------------------------------------15: define("JIRA_SERVER", "http://<JIRA HOST>:<PORT>/JIRA/rpc/soap/jirasoapservice-v2?wsdl");16: define("SVN_USER", "ENTER JIRA USER");17: define("SVN_PASS", "ENTER PASSOWRD");18: define("SVNLOOK_PATH", "/usr/bin/svnlook");19: define("LOGMSG_REGX", "/^([A-Z]*)\-(\d*)\s?\-\s?(.*)/");20:21: if ($argc < 3)22: trigger_error("Insufficient command line arguments!", E_USER_ERROR);23:24: $repoPath = $argv[1];25: $txnId = $argv[2];26:27: $cmd = SVNLOOK_PATH . " info -r " . $txnId . " " . $repoPath;28: //$cmd = SVNLOOK_PATH . " info -t " .$txnId . " " . $repoPath;29:30: $result = runCommand($cmd);31: if (!is_array($result))32: trigger_error("Unable to retrieve commit info for commit transaction " .33: $txnId . " repository " . $repoPath, E_USER_ERROR);34: if (count($result) < 4)35: trigger_error("Log message not supplied for commit transaction " . $txnId .36: " repository " . $repoPath, E_USER_ERROR);37:38: $author = $result[0];39: $line = NULL;40: $issueId = NULL;41: $matches = NULL;42: for ($cntr = 3; $cntr < count($result); $cntr++)43: {44: if (is_array($matches))45: unset($matches);46:47: $line = $result[$cntr];48: if (preg_match(LOGMSG_REGX, $line, $matches))49: {50: $issueId = $matches[1] . "-" . $matches[2];51: break 1;52: }53: }54: if (!isset($issueId) || is_null($issueId))55: trigger_error("Unable to retrieve JIRA issue id for commit transaction " .56: $txnId, E_USER_ERROR);57:58: if (!acceptCommit($author, $issueId))59: trigger_error($author . " is not allowed to commit against issue (" .60: $issueId . ")!", E_USER_ERROR);61: else62: exit(0);63:64: //------------------------------------------------------------------------------65: // HELPER Functions66: //------------------------------------------------------------------------------67: /**68: * Function acceptCommit calls the JIRA web service and checks following conditions69: * 1. The Issue id represents a valid JIRA issue with one of the following status70: * 1. Open71: * 2. In Progress72: * 3. Re-Opened73: * 2. The issue is assigned to the committer.74: *75: * If both of these conditions are met then the commit is allowed.76: * @param string - the user id of the committer.77: * @param string - The JIRA issue id used in the commit log message.78:79: * @return boolean80: */81: function acceptCommit($pAuthor, $pIssueId)82: {83: $client = new SoapClient(JIRA_SERVER);84: $authId = $client->login(SVN_USER, SVN_PASS);85: if (is_soap_fault($authId))86: trigger_error("JIRA Authentication failed!", E_USER_ERROR);87:88: $issue = $client->getIssue($authId, $pIssueId);89: if (is_soap_fault($issue))90: trigger_error("Usnable to retrieve details for issue " . $issueId,91: E_USER_ERROR);92:93: $status = intval($issue->status);94: $author = $issue->assignee;95: if (($status == 1 || $status == 3 || $status == 5) &&96: strcasecmp($author, $pAuthor))97: return true;98:99: return false;100: }101:102: /**103: * Function runCommand is used to launch a external program and capture it's104: * output.105: * @param string The shell command to be launched.106: * @param boolean A flag which allows running shell commands which do not return107: * anything.108: * @return array109: */110: function runCommand($cmd, $mayReturnNothing = false)111: {112: $output = array ();113: $err = false;114:115: $c = quoteCommand($cmd);116:117: $descriptorspec = array (0 => array('pipe', 'r'),118: 1 => array('pipe', 'w'),119: 2 => array('pipe', 'w'));120:121: $resource = proc_open($c, $descriptorspec, $pipes);122: $error = "";123:124: if (!is_resource($resource))125: trigger_error("BADCMD : " . $cmd, E_USER_ERROR);126:127: $handle = $pipes[1];128: $firstline = true;129: while (!feof($handle))130: {131: $line = trim(fgets($handle));132: if ($firstline && empty($line) && !$mayReturnNothing)133: {134: $err = true;135: }136:137: $firstline = false;138: if (!empty($line))139: $output[] = $line;140: }141:142: while (!feof($pipes[2]))143: {144: $error .= fgets($pipes[2]);145: }146:147: $error = trim($error);148:149: fclose($pipes[0]);150: fclose($pipes[1]);151: fclose($pipes[2]);152:153: proc_close($resource);154:155: if (!$err)156: return $output;157: else158: trigger_error($error, E_USER_ERROR);159: }160:161: /**162: * On windows you are required to enclose the command in double quotes to163: * avoid the problems that may occure due to long file or path names.164: * @param string The command to be enclosed in quotes.165: * @return string166:167: */168: function quoteCommand($cmd)169: {170: $osName = strtoupper(php_uname('s'));171: // On Windows machines, the whole line needs quotes round it so that it's172: // passed to cmd.exe correctly173: if (stripos($osName, "WINDOWS") !== false)174: $cmd = "\"$cmd\"";175:176: return $cmd;177: }178: ?>179:
Tuesday, July 29, 2008
SVN Administration
Subversion (aka SVN) is a nice and robust source code control software. Only drawback it has that it does not boasts a GUI to do the administration. Until recently, like others, I was also doing subversion administration using simple text editor like notepad. Then I found a nice little project developed by Mr. M. Verkerk called SVNManager. This little web application developed in PHP provides all the necessary administration options, right from repository creation to repository ACL maintenance.
The user interface of this application though is very unattractive. So I decided to revamp the user interface of this little application. To my surprise changing it did not took much efforts, even though I never programmed in PHP before, thanks to the way author has written it and the underlying framework (PRADO). Here are few Screens of the modified application. Have a look and post your comments. I will try to post the modified code in a few weeks.
Friday, May 16, 2008
The truth behind Rise In Prices of Food
Recently there was a news which started a big debate in INDIA. The news was blaming Indian's and or Chinese for increase in the price of food all over the world as these people have consuming more and more food. Today I received an email from my colleague which shades some light on why the food prices are increasing. Go through it to decide whom to be blamed really.
Friday, April 11, 2008
SVN pre-commit hook in VBScript
Subversion’s hook scripts provide a powerful and flexible way to associate actions with repository events. For example, the pre-commit hook allows you to check and possibly abort a transaction before it actually gets committed. This entry describes how to write such a hook in VBScript® language and test whether the committer has supplied a commit message and that the message contains a valid issue id. The issue tracking system used for integration is JIRA®.
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.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.@ECHO OFF
SETLOCAL
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
Friday, July 13, 2007
Maven local repository synchronization
We extensively use maven for building our software. The reason behind it is the fact that maven manages inter project dependencies very effectively and efficiently. Another reason is the Repositories in maven, this helps individuals to grab the third party dependent jars from a single place and then use them in their favorite IDE. However very often you will find that local repository that maven maintains (usually under <user.home>/.m2/repository folder) is not in sync with the remote repository. I tried to find out a solution for this problem which lead me to two projects being developed and maintained by codehaus.
However none of them was particularly useful. They are still under development and buggy. What I found then was a simple solution in the from of ant task. Again this solution is itself available from Maven at http://maven.apache.org/download.html. Here is a small ant script snippet which demonstrate the usage of these ant task(s) to synchronize your local repository.
<target name="pomcheck">
<echo message="Runnint POM check."/>
<dependencies verbose="true"
settingsfile="${user.home}/.m2/settings.xml"
filesetId="maven-ant-tasks.dependency.fileset"
profiles="default, MavenCentral">
<dependency groupid="product" artifactid="core"
version="1.5.5.1"/>
<remoterepository id="maven_global"
url="http://maven.company.com/mavenrepo/global"/>
<remoterepository id="maven_released"
url="http://maven.company.com/mavenrepo/released"/>
<remoterepository id="maven-snaps"
url="http://maven.company.com/mavenrepo/snapshots"/>
<remoterepository id="MavenCentral"
url="http://repo1.maven.org/maven2"/>
</dependencies>
</target>
That's it folks. Tomorrow we will dive into details of the above ant scripts. I will show you how to configure eclipse to run this script and meaning of each of the tag.
Thursday, February 15, 2007
Getting rid of XWork
Yesterday was a busy day for me and my colleague babu. We were in process of removing xWork dependency from our project. As advised by xWork the obvious choice was to use spring framework. Initially we thought that it will require lot of code change and the way our application loads various sub components. However it was a very sweet experience and migration really happened smoothly. We did not have to write lot of code.
What I really liked about spring was ease of configuration/setup and support for singletons and independent instances (prototype). I haven't had a chance to try our more complex scenarios but I am sure that spring won't let me down.
Second exercise that I carried out was with another colleague Parag. This time it was removing dependency on concurrency jar and using built in package from JDK 5. This change was really simple we just had to change the imports and in one case the different queue implementation. Although we were unable to get rid of runtime dependency on concurrency.jar as the jgroups project has not yet shifted to JDK's concurrency package.
I analyzed the jgroups source code and found out that around 53 classes needs a change. The most significant out of this is the change related to “Sync” interface. I guess for JDK 5.0 they will have to use “Lock” interface.
At this stage I am really tempted to undertake this change and be a part of jgroups supporter team.
Wednesday, January 10, 2007
Blog Clients Frustration
Today's day is really a frustating day for me. Except for Windows Live Writer I found none of the following mentioned blog clients are working with blogger.com. I think all of them are using old blogger API.
- w.blogger
- Windows live writer
- BlogDesk
- Qumana blog editor
- SharpMT
- BottomLine
- Bleezer
- Zoundry
- Post2Blog Express
- Semagic
- PostXing
- dotPost