Skip to content

Instantly share code, notes, and snippets.

@akuryan
Last active August 13, 2023 21:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akuryan/5e2746b60c0e1505af1d34d1f343abc3 to your computer and use it in GitHub Desktop.
Save akuryan/5e2746b60c0e1505af1d34d1f343abc3 to your computer and use it in GitHub Desktop.
SonarQube Community edition: get quality gate status after analysis and retrieve possible issues with theirs severities; then create bugs, linked to some issues in Jira
# path to sonarscanner for msbuild
$SonarQubeRunner = $(Resolve-Path ".\SonarScanner.MSBuild.exe").Path;
# sonar qube token for auth
$sonarQubeToken = "";
# sonar server hostname
$sonarQubeHostName = "";
$sonarQubeScheme = "https";
$sonarQubeUrl = "$sonarQubeScheme://$sonarQubeHostName";
# sonarqube project name
$sonarQubeProjectName = "";
# jira user data
$jiraUserName = "";
$jiraPwd = "";
$jiraHostname = "";
$jiraScheme = "https";
$jiraUrl = "$jiraScheme://$jiraHostname";
# jira project name
$jiraProjectName = "";
$scannerOutput = & $SonarQubeRunner end /d:sonar.login="$sonarQubeToken";
if($LASTEXITCODE -ne 0) {
# exit with errorcode if scanner failed
Write-Host $scannerOutput;
Write-Error "SonarQube scan failed";
exit $LASTEXITCODE;
}
# to get scan result URL we need to parse sonar scanner output, which is just an array of strings
# it would be nice to get this URL back as environment variable, but that's not the way we see it now
$analysisResultIndicator = 'More about the report processing at '
foreach ($line in $scannerOutput.Split([Environment]::NewLine)) {
if ($line.Contains($analysisResultIndicator)) {
$taskUrl = $line.Substring(6);
$taskUrl = $taskUrl.Replace($analysisResultIndicator, '');
}
}
Write-Host $taskUrl " analysis result";
# severities of issues we are looking for
$severities = "BLOCKER,CRITICAL,MAJOR";
$componentKey = [uri]::EscapeUriString($sonarQubeProjectName);
$issuesUrl = "$sonarQubeUrl/api/issues/search?severities=$severities&ps=500&componentKeys=$componentKey&s=CREATION_DATE&statuses=OPEN,REOPENED";
$token = [System.Text.Encoding]::UTF8.GetBytes("$sonarQubeToken" + ":");
$base64 = [System.Convert]::ToBase64String($token);
$basicAuth = [string]::Format("Basic {0}", $base64);
$headers = @{ Authorization = $basicAuth };
# get issues before analysis
$issuesBeforeAnalysis = Invoke-RestMethod -Method Get -Uri $issuesUrl -Headers $headers;
# get analysis id from task
$result = Invoke-RestMethod -Method Get -Uri $taskUrl -Headers $headers;
$result | ConvertTo-Json | Write-Host;
$status = $result.task.status;
$retryLimit = 15;
$counter = 0;
while ($status -ne "SUCCESS") {
Start-Sleep -Seconds 20;
$result = Invoke-RestMethod -Method Get -Uri $taskUrl -Headers $headers;
$status = $result.task.status;
$counter++;
if ($counter -eq $retryLimit) {
Write-Error "Analysis have not finished; exiting with error";
exit -1;
}
}
# get analysis result again
$analysisResult = Invoke-RestMethod -Method Get -Uri $taskUrl -Headers $headers;
$analysisResult | ConvertTo-Json | Write-Host;
$analysisId = $analysisResult.task.analysisId;
Write-Host "analysisId: $analysisId";
# get quality gate status
$qualityGateStatus = Invoke-RestMethod -Method Get -Uri "$sonarQubeUrl/api/qualitygates/project_status?analysisId=$analysisId" -Headers $headers;
$qualityGateStatus | ConvertTo-Json | Write-Host;
if ($qualityGateStatus.projectStatus.status -eq "OK") {
Write-Host "Quality Gate Succeeded";
# no sense to move further
exit 0;
} else {
# let's store errors of quality gates in file, to show it in final step
$collectedErrors = $qualityGateStatus.projectStatus.conditions | Where-Object {$_.status -eq "ERROR" } | ConvertTo-Json;
}
$issuesAfterAnalysis = Invoke-RestMethod -Method Get -Uri $issuesUrl -Headers $headers;
# get issues diff, if there is any
$diff = Compare-Object $issuesBeforeAnalysis.issues $issuesAfterAnalysis.issues;
# we've got quality gate errors
Write-Host "Collected errors:";
Write-Host $collectedErrors;
if ($collectedErrors.Count -eq 0) {
Write-Host "There is no errors in quality gate";
exit 0;
}
# issue key for Jira to attach bug to ticket
$issueKey = "";
$pair = [string]::Format("{0}:{1}", $jiraUserName, $jiraPwd);
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair);
$base64 = [System.Convert]::ToBase64String($bytes);
$basicAuthValue = [string]::Format("Basic {0}", $base64);
$jiraAuthHeaders = @{ Authorization = $basicAuthValue };
$jiraAuthHeaders += @{
"Content-Type" = "application/json"
"verify" = "false"
};
# start of retrieval of specific data for project I am working on
# find project key from Jira
$projectData = Invoke-RestMethod -Uri "$jiraUrl/rest/api/latest/project" -Method GET -Headers $jiraAuthHeaders;
$projectKey = ($projectData | where {$_.name -eq "$jiraProjectName"}).key;
# find bug id from Jira for retrieved project key
$issuesTypes = Invoke-RestMethod -Uri "$jiraUrl/rest/api/latest/issue/createmeta/$jiraProjectName/issuetypes" -Method GET -Headers $jiraAuthHeaders;
$bugId = ($issuesTypes.values | where {$_.name -eq "Bug"} ).id
$bugMetaData = Invoke-RestMethod -Uri "$jiraUrl/rest/api/latest/issue/createmeta/$jiraProjectName/issuetypes/$bugId" -Method GET -Headers $jiraAuthHeaders;
$versionName = ($bugMetaData.values | where {$_.fieldId -eq "versions"}).allowedValues[-1].name;
$issueLinkTypes = Invoke-RestMethod -Uri "$jiraUrl/rest/api/latest/issueLinkType" -Method GET -Headers $jiraAuthHeaders;
$relationId = $($issueLinkTypes.issueLinkTypes |where {$_.name -eq "Bug" }).id;
# end of retrieval of specific data for project I am working on
if ($null -eq $diff) {
# there is no difference found
$diffFound = $false;
} else {
$diffFound = $true;
}
function GetIssues {
param(
$diffData,
$severityKey
)
$returnString = "";
if ($null -eq $diffData) { return $returnString; }
# could not get how to tell Jira to create new line in multiline field
$newLine = ' ' + [Environment]::NewLine + ' ';
$diffData | where { $_.severity -eq $severityKey } | ForEach-Object {
$returnString = "file: " + $_.component + $newLine + "line, where error located: " + $_.line + $newLine + "textrange data: " + $_.textrange + $newLine + "message: " + $_.message + $newLine;
}
Write-Host $returnString;
return $returnString;
}
foreach($error in $collectedErrors) {
# collect data from sonarqube quality gate into human readable format
switch ($error.metricKey) {
"new_reliability_rating" { $summary = "Reliability rating violation, related to $issueKey"; $description = $summary; Break; }
"new_security_rating" { $summary = "Security rating violation, related to $issueKey"; $description = $summary; Break; }
"new_maintainability_rating" { $summary = "Maintainability rating violation, related to $issueKey"; $description = $summary; Break; }
"new_duplicated_lines_density" { $summary = "Too much duplicated lines violation, related to $issueKey"; $description = $summary; Break; }
"new_blocker_violations" {
$summary = "New Blocker issues introduced, related to $issueKey";
$description = $summary;
if ($diffFound) {
$description += GetIssues -diffData $diff.InputObject -severityKey "BLOCKER"
}
Break;
}
"new_critical_violations" {
$summary = "New Critical issues introduced, related to $issueKey";
$description = $summary;
if ($diffFound) {
$description += GetIssues -diffData $diff.InputObject -severityKey "CRITICAL"
}
Break;
}
"new_major_violations" {
$summary = "New Major issues introduced, related to $issueKey";
$description = $summary;
if ($diffFound) {
$description += GetIssues -diffData $diff.InputObject -severityKey "MAJOR"
}
Break;
}
Default { $summary = "Unknown quality gate violation, related to $issueKey"; $description = $summary; Break; }
}
# get issue data
$issueData = Invoke-RestMethod -Uri "$jiraUrl/rest/api/latest/issue/$issueKey" -Method GET -Headers $jiraAuthHeaders;
# get assigned user from issue data to assign him new bugs
$assignee = $issueData.fields.assignee.name;
# $jiraUrl/rest/api/latest/issue/createmeta/$jiraProjectName/issuetypes/$bugId - here one can get required fields for issue creation
# mine goes there
$body = [pscustomobject]@{
fields = @{
project= @{ key = $projectKey }
assignee = @{ name = $assignee }
components = @(
@{ name = "NAME" }
)
#Severity
customfield_10696 = @{ value = "Severity" }
#Steps to reproduce
customfield_11138 = "N/A"
#Discovered By
customfield_11493 = @{ value = "developers" }
# is it regression
customfield_13191 = @{ value = "no" }
#Activity
customfield_14391 = @{
value = "Static Code Analyzer"
}
issuetype = @{ name = "Bug" }
summary = $summary
description = $description
versions = @(
@{ name = $versionName }
)
}
} | ConvertTo-Json -Depth 100;
$requestUri = "$jiraUrl/rest/api/latest/issue"
try {
$response = Invoke-RestMethod -Uri $requestUri -Method POST -Headers $jiraAuthHeaders -Body $body;
$bugKey = $response.key;
Write-Output "ID: $($response.id)";
Write-Output "Key: $bugKey";
Write-Output "Self: $($response.self)";
# create linked issue
$linkBody = [pscustomobject]@{
inwardIssue = @{ key = $issueKey }
outwardIssue = @{ key = $bugKey }
type = @{ id = $relationId }
} | ConvertTo-Json -Depth 100;
Invoke-RestMethod -Uri "$jiraUrl/rest/api/latest/issueLink" -Method POST -Headers $jiraAuthHeaders -Body $linkBody;
} catch [System.Net.WebException]{
if ($_.Exception -ne $null -and $_.Exception.Response -ne $null) {
$errorResult = $_.Exception.Response.GetResponseStream()
$errorText = (New-Object System.IO.StreamReader($errorResult)).ReadToEnd()
Write-Warning "The remote server response: $errorText"
Write-Output $_.Exception.Response.StatusCode
} else {
throw $_
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment