1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-09-16 09:26:22 +02:00

feat(sonar): create combined report for Sonar scans (#5427)

Co-authored-by: Yuriy.Tereshchuk <astro.lutsk.aa@gmail.com>
This commit is contained in:
yuriy-tereshchuk-sap
2025-08-01 12:05:10 +02:00
committed by GitHub
parent fcd167b2e6
commit eedfeccc88
3 changed files with 77 additions and 17 deletions

View File

@@ -251,12 +251,17 @@ func runSonar(config sonarExecuteScanOptions, client piperhttp.Downloader, runne
return err
}
err = getStaticCodeCheckResults(config, &taskReport, serverUrl, influx, apiClient)
codeCheckData, err := getStaticCodeCheckResults(config, &taskReport, serverUrl, influx, apiClient)
if err != nil {
return err
}
err = getHotSpotSecurityCheckResults(config, &taskReport, serverUrl, apiClient)
hotSpotData, err := getHotSpotSecurityCheckResults(config, &taskReport, serverUrl, apiClient)
if err != nil {
return err
}
err = getCombinedReport(&codeCheckData, &hotSpotData)
if err != nil {
return err
}
@@ -264,30 +269,30 @@ func runSonar(config sonarExecuteScanOptions, client piperhttp.Downloader, runne
return nil
}
func getStaticCodeCheckResults(config sonarExecuteScanOptions, taskReport *SonarUtils.TaskReportData, serverUrl string, influx *sonarExecuteScanInflux, apiClient SonarUtils.Sender) error {
func getStaticCodeCheckResults(config sonarExecuteScanOptions, taskReport *SonarUtils.TaskReportData, serverUrl string, influx *sonarExecuteScanInflux, apiClient SonarUtils.Sender) (SonarUtils.ReportCodeCheckData, error) {
// fetch number of issues by severity
issueService := SonarUtils.NewIssuesService(serverUrl, config.Token, taskReport.ProjectKey, config.Organization, config.BranchName, config.ChangeID, apiClient)
var categories []SonarUtils.Severity
var err error
influx.sonarqube_data.fields.blocker_issues, err = issueService.GetNumberOfBlockerIssues(&categories)
if err != nil {
return err
return SonarUtils.ReportCodeCheckData{}, err
}
influx.sonarqube_data.fields.critical_issues, err = issueService.GetNumberOfCriticalIssues(&categories)
if err != nil {
return err
return SonarUtils.ReportCodeCheckData{}, err
}
influx.sonarqube_data.fields.major_issues, err = issueService.GetNumberOfMajorIssues(&categories)
if err != nil {
return err
return SonarUtils.ReportCodeCheckData{}, err
}
influx.sonarqube_data.fields.minor_issues, err = issueService.GetNumberOfMinorIssues(&categories)
if err != nil {
return err
return SonarUtils.ReportCodeCheckData{}, err
}
influx.sonarqube_data.fields.info_issues, err = issueService.GetNumberOfInfoIssues(&categories)
if err != nil {
return err
return SonarUtils.ReportCodeCheckData{}, err
}
reportData := SonarUtils.ReportCodeCheckData{
@@ -297,7 +302,7 @@ func getStaticCodeCheckResults(config sonarExecuteScanOptions, taskReport *Sonar
ChangeID: config.ChangeID,
BranchName: config.BranchName,
Organization: config.Organization,
Errors: categories[:],
ScanResults: categories[:],
NumberOfIssues: &SonarUtils.Issues{
Blocker: influx.sonarqube_data.fields.blocker_issues,
Critical: influx.sonarqube_data.fields.critical_issues,
@@ -323,16 +328,16 @@ func getStaticCodeCheckResults(config sonarExecuteScanOptions, taskReport *Sonar
log.Entry().Debugf("Influx values: %v", influx.sonarqube_data.fields)
return SonarUtils.WriteCodeCheckReport(reportData, sonar.workingDir, os.WriteFile)
return reportData, SonarUtils.WriteCodeCheckReport(reportData, sonar.workingDir, os.WriteFile)
}
func getHotSpotSecurityCheckResults(config sonarExecuteScanOptions, taskReport *SonarUtils.TaskReportData, serverUrl string, apiClient SonarUtils.Sender) error {
func getHotSpotSecurityCheckResults(config sonarExecuteScanOptions, taskReport *SonarUtils.TaskReportData, serverUrl string, apiClient SonarUtils.Sender) (SonarUtils.ReportHotSpotData, error) {
// fetch number of issues by severity
issueService := SonarUtils.NewIssuesService(serverUrl, config.Token, taskReport.ProjectKey, config.Organization, config.BranchName, config.ChangeID, apiClient)
var hotspotissues []SonarUtils.SecurityHotspot
err := issueService.GetHotSpotSecurityIssues(&hotspotissues)
if err != nil {
return err
return SonarUtils.ReportHotSpotData{}, err
}
reportData := SonarUtils.ReportHotSpotData{
@@ -345,7 +350,17 @@ func getHotSpotSecurityCheckResults(config sonarExecuteScanOptions, taskReport *
SecurityHotspots: hotspotissues[:],
}
return SonarUtils.WriteHotSpotReport(reportData, sonar.workingDir, os.WriteFile)
return reportData, SonarUtils.WriteHotSpotReport(reportData, sonar.workingDir, os.WriteFile)
}
func getCombinedReport(codeCheckData *SonarUtils.ReportCodeCheckData, hotSpotData *SonarUtils.ReportHotSpotData) error {
reportData := SonarUtils.ReportCombinedData{
NumberOfIssues: codeCheckData.NumberOfIssues,
ScanResults: codeCheckData.ScanResults,
SecurityHotspots: hotSpotData.SecurityHotspots,
}
return SonarUtils.WriteCombinedReport(reportData, sonar.workingDir, os.WriteFile)
}
// isInOptions returns true, if the given property is already provided in config.Options.

View File

@@ -6,7 +6,8 @@ import (
"path/filepath"
)
const reportCodeCheckFileName = "sonarscan.json"
const reportCodeCheckFileName = "codecheck.json"
const reportCombinedFileName = "sonarscan.json"
const reportHotSpotFileName = "hotspot.json"
// ReportCodeCheckData is representing the data of the step report JSON
@@ -18,7 +19,7 @@ type ReportCodeCheckData struct {
BranchName string `json:"branchName,omitempty"`
Organization string `json:"organization,omitempty"`
NumberOfIssues *Issues `json:"numberOfIssues"`
Errors []Severity `json:"errors"`
ScanResults []Severity `json:"scanResults"`
Coverage *SonarCoverage `json:"coverage,omitempty"`
LinesOfCode *SonarLinesOfCode `json:"linesOfCode,omitempty"`
}
@@ -34,6 +35,12 @@ type ReportHotSpotData struct {
SecurityHotspots []SecurityHotspot `json:"securityHotspots"`
}
type ReportCombinedData struct {
NumberOfIssues *Issues `json:"numberOfIssues"`
ScanResults []Severity `json:"scanResults"`
SecurityHotspots []SecurityHotspot `json:"securityHotspots"`
}
// HotSpot Security Issues
type SecurityHotspot struct {
Priority string `json:"priority"`
@@ -71,3 +78,11 @@ func WriteHotSpotReport(data ReportHotSpotData, reportPath string, writeToFile f
}
return writeToFile(filepath.Join(reportPath, reportHotSpotFileName), jsonData, 0644)
}
func WriteCombinedReport(data ReportCombinedData, reportPath string, writeToFile func(f string, d []byte, p os.FileMode) error) error {
jsonData, err := json.Marshal(data)
if err != nil {
return err
}
return writeToFile(filepath.Join(reportPath, reportCombinedFileName), jsonData, 0644)
}

View File

@@ -22,12 +22,12 @@ func writeToFileMock(f string, d []byte, p os.FileMode) error {
func TestWriteCodeCheckReport(t *testing.T) {
// init
const expected = `{"serverUrl":"https://sonarcloud.io","projectKey":"Piper-Validation/Golang","taskId":"mock.Anything","numberOfIssues":{"blocker":0,"critical":1,"major":2,"minor":3,"info":4},"errors":[{"severity":"CRITICAL","error_type":"CODE_SMELL","issues":10}],"coverage":{"coverage":13.7,"lineCoverage":37.1,"linesToCover":123,"uncoveredLines":23,"branchCoverage":42,"branchesToCover":30,"uncoveredBranches":3},"linesOfCode":{"total":327,"languageDistribution":[{"languageKey":"java","linesOfCode":327}]}}`
const expected = `{"serverUrl":"https://sonarcloud.io","projectKey":"Piper-Validation/Golang","taskId":"mock.Anything","numberOfIssues":{"blocker":0,"critical":1,"major":2,"minor":3,"info":4},"scanResults":[{"severity":"CRITICAL","error_type":"CODE_SMELL","issues":10}],"coverage":{"coverage":13.7,"lineCoverage":37.1,"linesToCover":123,"uncoveredLines":23,"branchCoverage":42,"branchesToCover":30,"uncoveredBranches":3},"linesOfCode":{"total":327,"languageDistribution":[{"languageKey":"java","linesOfCode":327}]}}`
testData := ReportCodeCheckData{
ServerURL: "https://sonarcloud.io",
ProjectKey: "Piper-Validation/Golang",
TaskID: mock.Anything,
Errors: []Severity{
ScanResults: []Severity{
{
SeverityType: "CRITICAL",
IssueType: "CODE_SMELL",
@@ -81,3 +81,33 @@ func TestWriteHotSpotReport(t *testing.T) {
assert.Equal(t, expected, fileContent)
assert.Equal(t, reportHotSpotFileName, fileName)
}
func TestWriteCombinedReport(t *testing.T) {
// init
const expected = `{"numberOfIssues":{"blocker":0,"critical":1,"major":2,"minor":3,"info":4},"scanResults":[{"severity":"CRITICAL","error_type":"CODE_SMELL","issues":10}],"securityHotspots":[{"priority":"HIGH","hotspots":1},{"priority":"LOW","hotspots":4}]}`
combinedData := ReportCombinedData{
ScanResults: []Severity{
{
SeverityType: "CRITICAL",
IssueType: "CODE_SMELL",
IssueCount: 10,
},
},
NumberOfIssues: &Issues{
Critical: 1,
Major: 2,
Minor: 3,
Info: 4,
},
SecurityHotspots: []SecurityHotspot{
{Priority: "HIGH", Hotspots: 1},
{Priority: "LOW", Hotspots: 4},
},
}
// test
err := WriteCombinedReport(combinedData, "", writeToFileMock)
// assert
assert.NoError(t, err)
assert.Equal(t, expected, fileContent)
assert.Equal(t, reportCombinedFileName, fileName)
}