Thursday, September 26, 2013

Jira integration with Jenkins

Jenkins provides a way to inject a custom groovy script into build job.

There are two way to call Jira remote API.
  1. JIRA SOAP Client
  2. JIRA REST API 

Example
  1. JIRA SOAP Client
    1. Get all Jenkins environment values 
    2. Get all Jira issue key value(s) from source control(git) change log 
    3. Query all proper Jira issue based on the issue keys and status 
    4. Add new Jira version if it is not exist yet 
    5. Change the Jira issue's status 
    6. Apply the Jira version onto all the Jira issues 
    7. Comment proper information with the Jira version, deploy (Test, Staging, Live) environment and executor's name on the Jira issues
    import hudson.model.*
    import com.atlassian.jira.rpc.soap.client.*
    
    def thr = Thread.currentThread()
    def build = thr?.executable
    
    def vars = build.getEnvVars()
    
    def txt = new File("${vars['WORKSPACE']}/../../jobs/${vars['JOB_NAME']}/builds/${vars['BUILD_ID']}/changelog.xml").getText()
    def ids = []
    
    def match = txt =~ /(\w+-\d+)/
    match.each {
       ids << it[0]
    }
    
    def exitWarning() {  
      println 'Warning: There is no jira # in commit comment'
      build.setResult(hudson.model.Result.UNSTABLE)
      build.executor.interrupt(hudson.model.Result.UNSTABLE)
    }
    
    if(ids.size() == 0) {
      exitWarning()
      return
    }
    
      def strVersion = "${vars['MajorVersion']}.${vars['MinorVersion']}.${vars['BUILD_NUMBER']}-Dev"
    
    
      JiraSoapServiceServiceLocator jiraSoapServiceLocator = new JiraSoapServiceServiceLocator();
      def jiraSoapService = jiraSoapServiceLocator.getJirasoapserviceV2(new URL('https://****/rpc/soap/jirasoapservice-v2'));
      String token = jiraSoapService.login('****', '****');
      def issues = jiraSoapService.getIssuesFromJqlSearch(token, "project=${vars['jiraProjectKey']} and issuekey in (${ids.join(', ')}) and status in ('Open', 'In Progress', 'Dev Open')", 20)                      
    
      println "Jira Issues # : " + issues.size()
        
    if(issues.size() == 0) { 
      exitWarning()
      return  
    }
    
      println "Jira Project Key : " + vars['jiraProjectKey']
      RemoteVersion version
      try {
        RemoteVersion _version = new RemoteVersion()
        _version.setName(strVersion)
        version = jiraSoapService.addVersion(token, vars['jiraProjectKey'], _version)
      }
      catch(e) {
        version = jiraSoapService.getVersions(token, vars['jiraProjectKey']).toList().find{ it.getName() == strVersion}
      }
      
      String[] resolutions = ["10"] // develop complete
      for (RemoteIssue issue : issues) {
         println issue.getKey()
          def actions = jiraSoapService.getAvailableActions(token, issue.getKey() );
          def startAction = actions.find{ it.getName() == 'Start Progress' }
          if(startAction) {
              jiraSoapService.progressWorkflowAction(token, issue.getKey(), startAction.getId());               
          }
          //def pushToTestAction = actions.find{ it.getName() == 'Push to Test' }
          //if(pushToTestAction) {
          jiraSoapService.progressWorkflowAction(token, issue.getKey(), "41"); // push to test
          //}
    
         def vals = new ArrayList()
         
        /*
         def assignee = issue.getAssignee()
         def reporter = issue.getReporter()
         if(assignee != reporter) {
               println assignee
               vals.add( new RemoteFieldValue("assignee", [ reporter ].toArray(new String[1]) ) )
         }*/
    
          def versions = issue.getFixVersions().toList()
             
          if(!versions.contains(version)) {   
          /*
             RemoteVersion[] vers = versions.toArray(new RemoteVersion[versions.size()])     
             println vers.size()
             issue.setFixVersions( vers )*/
           
           versions.push(version)
           //String[] a = [ version.getId() ]
           def al = new ArrayList() //a.toList()
           versions.each{
             al.add(it.getId())
           }     
    
           vals.add(new RemoteFieldValue("fixVersions", al.toArray(new String[al.size()]) ) )   
          }
    
          if(vals.size() > 0) {
                  println vals.first().getValues()[0]
                   //RemoteFieldValue[] vals = [ new RemoteFieldValue("fixVersions", al.toArray(new String[al.size()]) ) ]
                  jiraSoapService.updateIssue(token, issue.getKey(), vals.toArray(new RemoteFieldValue[vals.size()]) )
          }
    
          RemoteComment comment = new RemoteComment()
          
          comment.setBody("Update has been applied to ${vars['deploy_target']} environment with version # (${strVersion}) by ${vars['userName']}.\r\nPlease verify it.")
          jiraSoapService.addComment(token, issue.getKey(), comment);
      }
    
    Here is a way to get issues by using Jira query.
    import com.atlassian.jira.rpc.soap.client.*
    
    JiraSoapServiceServiceLocator jiraSoapServiceLocator = new JiraSoapServiceServiceLocator();
    def jiraSoapService = jiraSoapServiceLocator.getJirasoapserviceV2(
                             new URL('https://[jira web domain]/rpc/soap/jirasoapservice-v2'));
    String token = jiraSoapService.login('[jenkins-id]', '[jenkins-password]');
    def issues = jiraSoapService.getIssuesFromJqlSearch(token, "project=KEY", 1)
    
    issues.each{ println it.getName() }
    

    Add new Jira Version
      def strVersion = "1.0.0"
      RemoteVersion version
      try {
        RemoteVersion _version = new RemoteVersion()
        _version.setName(strVersion)
        version = jiraSoapService.addVersion(token, "PROJECTKEY", _version)
      }
      catch(e) {
        version = jiraSoapService.getVersions(token, "PROJECTKEY").toList().find{ it.getName() == strVersion}
      }
    
    Change Jira issue Status
      def actions = jiraSoapService.getAvailableActions(token, issue.getKey() );
      def startAction = actions.find{ it.getName() == 'Start Progress' }
      if(startAction) { // if it hasn't been started, push it to in progress status.
         jiraSoapService.progressWorkflowAction(token, issue.getKey(), startAction.getId());
      }
      //def pushToTestAction = actions.find{ it.getName() == 'Push to Test' }
      //if(pushToTestAction) {
      jiraSoapService.progressWorkflowAction(token, issue.getKey(), "41"); // 41 is "Push to Test" action(transition) number.
    

  2. JIRA REST API

    First of all, I've defined below function with credential info
    import hudson.model.*
    import com.atlassian.jira.rpc.soap.client.*
    //import groovy.json.JsonSlurper
      
      
      // jira rest api
      def urlRoot = "https://jira.domain.net"
      def userName = "jenkins_id"
      def password = "password"
      addr       = "${urlRoot}/rest/api/2/"  
      authString = "${userName}:${password}".getBytes().encodeBase64().toString()
    
    
    def requestJSON(method, url)
    {
      def conn = (addr + url).toURL().openConnection()
      conn.setRequestMethod(method)
      if(authString.length() > 0)
      {
         conn.setRequestProperty( "Authorization", "Basic ${authString}" )
      }
      conn.connect()
      if( conn.responseCode == 200 ) {
        return conn.content.text
      }
      else if(method == "GET") {
        
        println "Something bad happened."
        println "${conn.responseCode}: ${conn.responseMessage}" 
      }
      return ""
    }
    
    After then, i could call any Jira REST API easily as below - delete all Jira version which is applied on an issue.
     def versions = issue.getFixVersions().toList()
                          .findAll{ it.getName() ==~ /.+[Dev]/ }
        for(RemoteVersion ver : versions)
        {
            println "delete a develop version (${ver.getName()})"
            requestJSON("DELETE", "version/${ver.getId()}") // jira rest api
        }
    

No comments: