Last active
July 3, 2019 09:23
-
-
Save x-cray/2828324 to your computer and use it in GitHub Desktop.
Jenkins email-ext plugin groovy template. Generates daily Sonar violations report grouped by culprits.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<head> | |
<title>Sonar violations report</title> | |
<style type="text/css"> | |
body | |
{ | |
margin: 0px; | |
padding: 15px; | |
} | |
body, td, th | |
{ | |
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Tahoma, sans-serif; | |
font-size: 10pt; | |
} | |
table | |
{ | |
border-collapse: collapse; | |
width: 100% | |
} | |
th | |
{ | |
text-align: left; | |
} | |
h1 | |
{ | |
margin-top: 0px; | |
} | |
li | |
{ | |
line-height: 15pt; | |
} | |
table.source-incut | |
{ | |
background-color: #EEE; | |
border: 1px solid #DDD; | |
} | |
table.source-incut td | |
{ | |
white-space: pre; | |
padding: 3px; | |
font-family: "Lucida Console", "Courier New"; | |
font-size: 9pt; | |
} | |
.priority-blocker | |
{ | |
color: #A22; | |
} | |
.priority-critical | |
{ | |
color: #A42; | |
} | |
.priority-major | |
{ | |
color: #A62; | |
} | |
.priority-minor | |
{ | |
color: #4A2; | |
} | |
.priority-info | |
{ | |
color: #2A2; | |
} | |
.line-number | |
{ | |
color: #777; | |
width: 20px; | |
} | |
.res-name | |
{ | |
color: #363; | |
} | |
.source | |
{ | |
color: #336; | |
} | |
.error | |
{ | |
background-color: #FCC; | |
} | |
</style> | |
</head> | |
<body> | |
<% | |
apiSonarRoot = "http://localhost:9000" | |
webSonarRoot = "http://gateway:9000" | |
def getSonarXml(path) { | |
format = (path.contains("?") ? "&" : "?") + "format=xml" | |
sonarUrl = "${apiSonarRoot}${path}${format}" | |
//println "Reading URL ${sonarUrl} contents" | |
sonarUrl.toURL().text | |
} | |
def getParsedResponse(responseXml) { | |
new XmlSlurper().parseText(responseXml) | |
} | |
def getNewViolatedResources(resKey, qualifier) { | |
violatedResources = [] | |
resParam = resKey ? "&resource=${resKey}" : "" | |
allResources = getParsedResponse(getSonarXml("/api/resources?depth=-1&qualifiers=${qualifier}&metrics=new_violations&includetrends=true${resParam}")) | |
allResources.resource.each { res -> | |
if (res.msr.var2.toFloat() > 0) { | |
//println "Adding violated resource ${res.id} [${res.key}] for qualifier ${qualifier}" | |
violatedResources << res.key.text() | |
} | |
} | |
violatedResources | |
} | |
def parseLineMetrics(resources, parser) { | |
result = [:] | |
if (resources?.resource?.size() == 1) { | |
resources.resource[0].msr.data.text().split(";").each { line -> | |
(lineNum, data) = line.split("=") | |
result.put(lineNum.toInteger(), parser(data)) | |
} | |
} | |
result | |
} | |
def getAuthorsByLine(resKey) { | |
authorsResource = getParsedResponse(getSonarXml("/api/resources?resource=${resKey}&metrics=authors_by_line")) | |
parseLineMetrics(authorsResource, { data -> data }) | |
} | |
def getDatesByLine(resKey) { | |
datesResource = getParsedResponse(getSonarXml("/api/resources?resource=${resKey}&metrics=last_commit_datetimes_by_line")) | |
parseLineMetrics(datesResource, { data -> getDateFromString(data) }) | |
} | |
def getResource(resKey) { | |
lines = [] | |
contents = getParsedResponse(getSonarXml("/api/sources?resource=${resKey}")) | |
if (contents?.line?.size() > 0) { | |
contents.line.each { line -> | |
lines << line.val.text() | |
} | |
} | |
lines | |
} | |
def getDateFromString(input) { | |
new Date().parse("yyyy-MM-dd'T'HH:mm:ssZ", input).clearTime() | |
} | |
def getResourceViolations(resKey) { | |
violations = [] | |
today = new Date().clearTime() | |
contents = getParsedResponse(getSonarXml("/api/violations?resource=${resKey}")) | |
contents.violation.each { violation -> | |
createdAt = getDateFromString(violation.createdAt.text()) | |
if (createdAt == today) { | |
authorsByLine = getAuthorsByLine(resKey) | |
datesByLine = getDatesByLine(resKey) | |
linesModifiedToday = datesByLine.findAll { entry -> entry.value == today }.collect { entry -> entry.key } | |
lineNumText = violation.line.text() | |
lineNumInt = lineNumText ? lineNumText.toInteger() : -1 | |
if (linesModifiedToday.size() > 0) { | |
culprits = linesModifiedToday.collect { line -> authorsByLine[line] }.unique() | |
} else { | |
culprits = [] | |
if (lineNumInt > -1) | |
culprits << authorsByLine[lineNumInt] | |
} | |
culpritsText = culprits.size() > 0 ? culprits.join(" or ") : "Someone" | |
violations << [ | |
rule: violation.rule.name.text(), | |
message: violation.message.text(), | |
priority: violation.priority.text(), | |
lineNum: lineNumInt, | |
authors: culpritsText | |
] | |
} | |
} | |
violations | |
} | |
def getUserMappedViolations() { | |
violatedModules = [] | |
violatedFiles = [] | |
// Get violated projects. | |
// Step 1. Read violated projects. | |
projectKeyParam = System.getenv("SONAR_PROJECT_KEY") | |
violatedProjects = projectKeyParam ? [projectKeyParam] : getNewViolatedResources(null, "TRK") | |
// Get violated modules. | |
// Step 2. Read violated modules for projects. | |
violatedProjects.each { project -> | |
violatedModules.addAll(getNewViolatedResources(project, "BRC")) | |
} | |
// Get violated files. | |
// Step 3. Read violated files for modules. | |
violatedModules.each { module -> | |
violatedFiles.addAll(getNewViolatedResources(module, "FIL")) | |
} | |
// Step 4. Group received data per authors. | |
allViolations = [] | |
violatedFiles.each { file -> | |
fileViolations = getResourceViolations(file) | |
fileContents = getResource(file) | |
fileViolations.each { violation -> | |
sourceFragment = null | |
if (violation.lineNum > -1) { | |
sourceFragment = [:] | |
for (ln in violation.lineNum - 3..violation.lineNum + 3) { | |
sourceFragment.put(ln, fileContents[ln - 1]) | |
} | |
} | |
allViolations << [violation: violation, resKey: file, source: sourceFragment] | |
} | |
} | |
[allViolations.size(), violatedFiles.size(), allViolations.groupBy { it.violation.authors }] | |
} | |
%> | |
<h1>Sonar violations report</h1> | |
<% | |
if (build.result == hudson.model.Result.SUCCESS) { | |
(violationsCount, filesCount, userMappedViolations) = getUserMappedViolations() | |
if (violationsCount > 0) { %> | |
<p>Found ${violationsCount} new violations in ${filesCount} files.</p> | |
<% userMappedViolations.each { authors, mappedViolations -> %> | |
<h2>Violations by ${authors}</h2> | |
<ol> | |
<% mappedViolations.each { mappedViolation -> %> | |
<li> | |
<p>[<b><span class="priority-${mappedViolation.violation.priority.toLowerCase()}">${mappedViolation.violation.priority}</span></b>] <b>${mappedViolation.violation.rule}</b>. ${mappedViolation.violation.message}<br /> | |
in resource <span class="res-name">${mappedViolation.resKey}</span> at line ${mappedViolation.violation.lineNum} | |
(<a href="${webSonarRoot}/drilldown/measures/${mappedViolation.resKey}?metric=new_violations#L${mappedViolation.violation.lineNum}">view in Sonar</a>):</p> | |
<% if (mappedViolation.source) { %> | |
<table class="source-incut"> | |
<% mappedViolation.source.each { lineNum, line -> %> | |
<tr<% if (lineNum == mappedViolation.violation.lineNum) { %> class="error"<% } %>> | |
<td class="line-number">${lineNum}</td><td class="source">${line.replaceAll('\t', ' ')}</td> | |
</tr> | |
<% } %> | |
</table> | |
<% } %> | |
</li> | |
<% } %> | |
</ol> | |
<% } %> | |
<% } else { %> | |
<p>No new violations! Woot!!!</p> | |
<% } %> | |
<% } else { %> | |
<p>Build was not successful. Therefore, no report is generated.</p> | |
<% } %> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi :) i seem to be getting an exception error, is there any way around it , for the template to work? ive attached the error log. i checked the root and set the localhost to my local sonar server, i must be missing something.