Groovy-based custom report-generator for SonarQube

The Webinterface of SonarQube is nice, but sometimes you (or your boss) simply want a report that gives an overview over the current status of only the most important measure-results of all your software projects. With a little bit of Groovy-magic and some minutes with good old bad java-scripting this is possible.

environment-setup:

  • Oracle Java 1.7.0_17 (Hotspot)
  • Groovy 2.2.1
  • SonarQube 4.1

Here are the results that are visible in Sonar-webinterface:
sonar-webinterface

A good and detailed overview for the dev-teams… but not for management. So what to do now?

SonarQube offers a nice webservice-client (that needs several deps):

  • sonar-ws-client-4.0.jar
  • commons-codec-1.9.jar
  • commons-httpclient-3.1.jar

This client can simply be used in the following way:

import org.sonar.wsclient.Sonar;
import org.sonar.wsclient.services.Resource;
import org.sonar.wsclient.services.ResourceQuery; 
...
def sonarUrl = "http://localhost:9000"
def sonarUser = "admin"
def sonarPass = "admin"
...
Sonar sonar = Sonar.create(sonarUrl, sonarUser, sonarPass);
Resource sonarProject = sonar.find(ResourceQuery.createForMetrics("${projectName}:${projectVersion}","ncloc"));
locs = sonarProject.getMeasureValue("ncloc");

easy, right?

OK, let’s put this together with some scripting. I prefer having all the build-projects in one central projects.xml-file in order to have them easily available for tasks like this.
The following implementation will use this projects.xml:

<projects> 
	<project>
		<name>eu.christophburmeister:component1</name>
	</project>
	<project>
		<name>eu.christophburmeister:component2</name>
	</project>
	<project>
		<name>eu.christophburmeister:component3</name>
	</project>
	<project>
		<name>eu.christophburmeister:component4</name>
	</project>
</projects>

So here comes the script that parses the central projects.xml-file for all projects, make a call for each project to the sonar-instance and fetches the resulting values of locs, violations-density (rules compliance index) and details of blockers, criticals, majors, minors and info violations. The list of versions starts with the most current (release_0.3). So, if there are values for a project with this version, it will fetch the results and break the loop, because we’re just interested in the highest branch. If a project is not analyzed within one of the versions in the list, it is simply excluded (look at component 4). This can be helpful to ignore old and deprecated values.

import org.sonar.wsclient.Sonar;
import org.sonar.wsclient.services.Resource;
import org.sonar.wsclient.services.ResourceQuery; 

def projectsFile = "./projects.xml"
def outputFile= "./ca-report.html"
def sonarUrl = "http://localhost:9000"
def sonarUser = "admin"
def sonarPass = "admin"

// -------------- start -----------------------
// get the parts form projects-file
projects = new XmlSlurper().parse(projectsFile)
def allProjects = projects.project

// prepare the sonar-access
Sonar sonar = Sonar.create(sonarUrl, sonarUser, sonarPass);

// prepare the output-file
outputFile = new File(outputFile)
outputFile.write("") 
outputFile.append("<h1>CA-Report</h1>")
outputFile.append("<i>" + new Date() + "</i>")
outputFile.append("<table border=\"1\" celladding=\"3\" cellspacing=\"0\" width=\"100%\">")
outputFile.append("	<tr>");
outputFile.append("		<th>Name</th><th>last analysis <br>in branch</th><th>locs</th><th>RCI (%)</th><th>blocker</th><th>criticals</th><th>majors</th><th>minors</th><th>infos</th>");
outputFile.append("	</tr>");

rciValues = [];

// go through all projects in projects-file
for(p in allProjects ){	
	projectName = p.name.text()	
	
	// the version-list, most current first
	versionList = ["release_0.3", "release_0.2", "release_0.1", "n.a."]
		
	for (int i=0; i<versionList.size; i++){
		projectVersion = versionList[i]
		Resource sonarProject = sonar.find(
			ResourceQuery.createForMetrics(
				"${projectName}:${projectVersion}", 
				"ncloc", 
				"violations_density",						
				"blocker_violations", 
				"critical_violations", 
				"major_violations", 
				"minor_violations", 
				"info_violations")
			);
	
		if (sonarProject == null){
			locs = "n.a.";
			rci = "n.a.";
			blocker_violations = "n.a.";
			critical_violations = "n.a.";
			major_violations = "n.a.";
			minor_violations = "n.a.";
			info_violations = "n.a.";
		} 
		else {
			locs = sonarProject.getMeasureValue("ncloc");
			rci = sonarProject.getMeasureValue("violations_density");
			blocker_violations = sonarProject.getMeasureValue("blocker_violations");
			critical_violations = sonarProject.getMeasureValue("critical_violations");
			major_violations = sonarProject.getMeasureValue("major_violations");
			minor_violations = sonarProject.getMeasureValue("minor_violations");
			info_violations = sonarProject.getMeasureValue("info_violations");
			
			rciValues.add(rci)
			
			break;			
		}		
	}		

	println "${projectName}"
	
	tableRow = ""
	tableRow += "     	<tr>\n"
	tableRow += "	      		<td>${projectName}</td>\n"
	tableRow += "	      		<td>${projectVersion}</td>\n"
	tableRow += "	      		<td>${locs}</td>\n"
	tableRow += "	      		<td>${rci}</td>\n"
	tableRow += "	      		<td>${blocker_violations}</td>\n"
	tableRow += "	      		<td>${critical_violations}</td>\n"
	tableRow += "	      		<td>${major_violations}</td>\n"
	tableRow += "	      		<td>${minor_violations}</td>\n"
	tableRow += "	      		<td>${info_violations}</td>\n"
	tableRow += "     	</tr>\n"
		
	outputFile.append(tableRow)
}

outputFile.append("<table>")

rciSum = 0
numberOfMeasurs = rciValues.size();
for (value in rciValues){ rciSum += value }
rciAvg = rciSum / numberOfMeasurs

println rciValues
println rciSum
println numberOfMeasurs
println rciAvg

outputFile.append("<ul>")
outputFile.append("	<li>RCI-Average: ${rciAvg.round(2)}%</li>")
outputFile.append("</ul>")

After fetching the values directly from sonar-server, the results are written into a simple html-table and that’s it. You can hire a designer who makes it a bit nicer, but basically this report is simple and shows the current status of all your projects (and of course can be included in Powerpoint-slides for management-calls 😉 ).

ca-report

In your sonar-server you can get all configured measures easily via http://:/api/metrics

nice, no?