Monday, 30 March 2015

Delete Jenkins job workspaces left after renaming

When renaming a job or moving one from slave to slave Jenkins copies it and does not delete the original. This script can fix that.

#!/usr/bin/env python
import urllib, json, os, sys
from shutil import rmtree
url = 'http://jenkins/api/python?pretty=true'
data =  eval(urllib.urlopen(url).read())
jobnames = []
for job in data['jobs']:
def clean(path):
  builds = os.listdir(path)
  for build in builds:
    if build not in jobnames:
      build_path = os.path.join(path, build)
      print "removing dir: %s " % build_path

Monday, 23 March 2015

Merging two sets of Jacoco Integration Test Coverage Reports for Sonar using Gradle

In a Jenkins build:

./gradlew cukeTest
./gradlew integTest
./gradlew sonarrunner

This leaves three .exec files behind. SonarQube can only use two. So we merge the two integration tests results.

task integCukeMerge(type: JacocoMerge) {
  description = 'Merge test code coverage results from feign and cucumber'
  // This assumes cuketests have already been run in a separate gradle session

  doFirst {
    delete destinationFile
    // Wait until integration tests have actually finished
    println start.process != null ? start.process.waitFor() : "In integCukeMerge tomcat is null"

  executionData fileTree("${buildDir}/jacoco/it/")


sonarRunner {
  tasks.sonarRunner.dependsOn integCukeMerge
  sonarProperties {
    property "sonar.projectDescription", "A legacy codebase."
    property "sonar.exclusions", "**/fakes/**/*.java, **/domain/**.java, **/*"

    properties["sonar.tests"] += sourceSets.integTest.allSource.srcDirs

    property "sonar.jdbc.url", "jdbc:postgresql://localhost/sonar"
    property "sonar.jdbc.driverClassName", "org.postgresql.Driver"
    property "sonar.jdbc.username", "sonar"
    property "", "http://sonar.we7.local:9000"

    def jenkinsBranchName = System.getenv("GIT_BRANCH")
    if (jenkinsBranchName != null) {
      jenkinsBranchName = jenkinsBranchName.substring(jenkinsBranchName.lastIndexOf('/') + 1)
    def branch = jenkinsBranchName ?: ('git rev-parse --abbrev-ref HEAD'.execute().text ?: 'unknown').trim()

    def buildName = System.getenv("JOB_NAME")
    if (buildName == null) {
      property "sonar.projectKey", "${name}"
      def username = System.getProperty('')
      property "sonar.projectName", "~${name.capitalize()} (${username})"
      property "sonar.branch", "developer"
    } else {
      property "sonar.projectKey", "$buildName"
      property "sonar.projectName", name.capitalize()
      property "sonar.branch", "${branch}"
      property "", "http://jenkins/job/${buildName}/"

    property "sonar.projectVersion", "git describe --abbrev=0".execute().text.trim()

    property "", "jacoco"
    property "sonar.jacoco.reportPath", "$project.buildDir/jacoco/test.exec"
    // feign results
    //property "sonar.jacoco.itReportPath", "$project.buildDir/jacoco/it/integTest.exec"
    // Cucumber results
    //property "sonar.jacoco.itReportPath", "$project.buildDir/jacoco/it/cukeTest.exec"
    // Merged results
    property "sonar.jacoco.itReportPath", "$project.buildDir/jacoco/integCukeMerge.exec"

    property "sonar.links.homepage", "${org}/${name}"

Remember to use Overall Coverage in any Sonar Quality Gates!