Skip to content

Commit 87396c8

Browse files
add results check
1 parent d39c06d commit 87396c8

File tree

3 files changed

+258
-5
lines changed

3 files changed

+258
-5
lines changed

jenkins_integration/Jenkinsfile

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,43 @@ pipeline
2121
stage('Static Analysis with SonarQube') {
2222
steps {
2323
script {
24+
// Declare variables outside the Docker container block
25+
def staticAnalysisResult = ""
26+
def staticAnalysisStatus = ""
27+
def staticAnalysisOutput = ""
28+
def sonarCommitSha = ""
29+
2430
withDockerContainer(image: 'sonarsource/sonar-scanner-cli:latest') {
25-
def result = pipelineFunctions.publishSonarAnalysis()
31+
def sonarResult = pipelineFunctions.publishSonarAnalysis()
32+
33+
// Extract results for GitHub reporting
34+
staticAnalysisResult = sonarResult.result
35+
staticAnalysisStatus = sonarResult.status
36+
staticAnalysisOutput = sonarResult.output
37+
sonarCommitSha = sonarResult.commit_sha
38+
39+
echo "Static Analysis Result: ${staticAnalysisResult}"
40+
echo "Static Analysis Status: ${staticAnalysisStatus}"
41+
echo "Commit SHA from SonarQube: ${sonarCommitSha}"
42+
}
43+
// Send results to GitHub PR only
44+
try {
45+
if (!bypass_send_results_gh && env.CHANGE_ID) {
46+
pipelineFunctions.send_sonar_results_to_github(
47+
sonarCommitSha,
48+
staticAnalysisResult,
49+
staticAnalysisStatus,
50+
staticAnalysisOutput,
51+
env.CHANGE_ID,
52+
env.CHANGE_BRANCH,
53+
env.CHANGE_TARGET
54+
)
55+
echo "✅ Posted SonarQube results to GitHub PR #${env.CHANGE_ID}"
56+
} else {
57+
echo "ℹ️ Skipping GitHub PR comment (not a PR or bypass enabled)"
58+
}
59+
} catch (Exception githubEx) {
60+
echo "❌ Failed to post to GitHub PR: ${githubEx.getMessage()}"
2661
}
2762
}
2863
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Script to post SonarQube static analysis results to GitHub PR as a comment.
4+
"""
5+
6+
import argparse
7+
import json
8+
import os
9+
import requests
10+
import sys
11+
from datetime import datetime
12+
13+
def post_pr_comment(github_token, repo_owner, repo_name, pr_number, comment_body):
14+
"""Post a comment to GitHub PR"""
15+
url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/issues/{pr_number}/comments"
16+
17+
headers = {
18+
"Authorization": f"token {github_token}",
19+
"Accept": "application/vnd.github.v3+json",
20+
"Content-Type": "application/json"
21+
}
22+
23+
payload = {"body": comment_body}
24+
25+
response = requests.post(url, headers=headers, json=payload)
26+
27+
if response.status_code == 201:
28+
print(f"✅ Successfully posted SonarQube results to PR #{pr_number}")
29+
return True
30+
else:
31+
print(f"❌ Failed to post comment to PR #{pr_number}")
32+
print(f"Status: {response.status_code}")
33+
print(f"Response: {response.text}")
34+
return False
35+
36+
def post_commit_status(github_token, repo_owner, repo_name, commit_sha, state, description, target_url):
37+
"""Post commit status to GitHub"""
38+
url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/statuses/{commit_sha}"
39+
40+
headers = {
41+
"Authorization": f"token {github_token}",
42+
"Accept": "application/vnd.github.v3+json",
43+
"Content-Type": "application/json"
44+
}
45+
46+
payload = {
47+
"state": state,
48+
"target_url": target_url,
49+
"description": description,
50+
"context": "ci/sonarqube-analysis"
51+
}
52+
53+
response = requests.post(url, headers=headers, json=payload)
54+
55+
if response.status_code == 201:
56+
print(f"✅ Successfully posted commit status for {commit_sha[:8]}")
57+
return True
58+
else:
59+
print(f"❌ Failed to post commit status for {commit_sha[:8]}")
60+
print(f"Status: {response.status_code}")
61+
print(f"Response: {response.text}")
62+
return False
63+
64+
def create_comment_body(result, status, commit_sha, branch_name, target_branch, sonar_output):
65+
"""Create formatted comment body for GitHub PR"""
66+
67+
# Determine status emoji and text
68+
if result == "PASS":
69+
result_emoji = "✅"
70+
result_text = "**PASSED**"
71+
else:
72+
result_emoji = "❌"
73+
result_text = "**FAILED**"
74+
75+
# Truncate output if too long - show last 2000 characters for most relevant info
76+
max_output_length = 2000
77+
if len(sonar_output) > max_output_length:
78+
truncated_output = "[Output truncated - showing last 2000 characters]\n\n..." + sonar_output[-max_output_length:]
79+
else:
80+
truncated_output = sonar_output
81+
82+
comment_body = f"""## 🔍 SonarQube Static Analysis Results
83+
84+
**Result:** {result_emoji} {result_text}
85+
**Quality Gate Status:** {status}
86+
**Commit SHA:** `{commit_sha}`
87+
88+
### 📊 Analysis Summary
89+
- **Branch:** `{branch_name}`
90+
- **Target:** `{target_branch}`
91+
- **Analysis Time:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}
92+
93+
### 📋 Detailed Results
94+
<details>
95+
<summary>Click to view SonarQube output</summary>
96+
97+
```
98+
{truncated_output}
99+
```
100+
101+
</details>
102+
103+
---
104+
*🤖 Automated comment by Jenkins CI*"""
105+
106+
return comment_body
107+
108+
def main():
109+
parser = argparse.ArgumentParser(description="Post SonarQube results to GitHub PR")
110+
parser.add_argument("--github_token", required=True, help="GitHub access token")
111+
parser.add_argument("--repo_owner", default="SiliconLabsSoftware", help="GitHub repository owner")
112+
parser.add_argument("--repo_name", default="matter_extension", help="GitHub repository name")
113+
parser.add_argument("--pr_number", required=True, help="Pull request number")
114+
parser.add_argument("--commit_sha", required=True, help="Git commit SHA")
115+
parser.add_argument("--result", required=True, choices=["PASS", "FAIL"], help="SonarQube analysis result")
116+
parser.add_argument("--status", required=True, help="SonarQube quality gate status")
117+
118+
parser.add_argument("--branch_name", required=True, help="Source branch name")
119+
parser.add_argument("--target_branch", required=True, help="Target branch name")
120+
parser.add_argument("--sonar_output", required=True, help="SonarQube scanner output")
121+
122+
args = parser.parse_args()
123+
124+
try:
125+
# Create comment body
126+
comment_body = create_comment_body(
127+
args.result,
128+
args.status,
129+
args.commit_sha,
130+
args.branch_name,
131+
args.target_branch,
132+
args.sonar_output
133+
)
134+
135+
# Post PR comment
136+
comment_success = post_pr_comment(
137+
args.github_token,
138+
args.repo_owner,
139+
args.repo_name,
140+
args.pr_number,
141+
comment_body
142+
)
143+
144+
# Post PR comment only (skip commit status due to permissions)
145+
if comment_success:
146+
print("✅ GitHub PR comment posted successfully")
147+
sys.exit(0)
148+
else:
149+
print("❌ Failed to post GitHub PR comment")
150+
sys.exit(1)
151+
152+
except Exception as e:
153+
print(f"❌ Error posting to GitHub: {str(e)}")
154+
sys.exit(1)
155+
156+
if __name__ == "__main__":
157+
main()

jenkins_integration/jenkinsFunctions.groovy

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,32 @@ def send_test_results_to_github(commit_sha, sqa_tests_result, sqa_tests_summary)
5656
}
5757
}
5858

59+
/**
60+
* Send SonarQube results to GitHub PR using Python script
61+
*/
62+
def send_sonar_results_to_github(commit_sha, result, status, sonar_output, pr_number, branch_name, target_branch) {
63+
withCredentials([
64+
usernamePassword(credentialsId: 'Matter-Extension-GitHub', usernameVariable: 'GITHUB_APP', passwordVariable: 'GITHUB_ACCESS_TOKEN')
65+
]) {
66+
// Escape sonar output for shell command
67+
def escapedOutput = sonar_output.replace('"', '\\"').replace('`', '\\`').replace('$', '\\$')
68+
69+
sh """
70+
python3 -u jenkins_integration/github/send_sonar_results_to_github.py \\
71+
--github_token \${GITHUB_ACCESS_TOKEN} \\
72+
--repo_owner "SiliconLabsSoftware" \\
73+
--repo_name "matter_extension" \\
74+
--pr_number ${pr_number} \\
75+
--commit_sha ${commit_sha} \\
76+
--result ${result} \\
77+
--status ${status} \\
78+
--branch_name "${branch_name}" \\
79+
--target_branch "${target_branch}" \\
80+
--sonar_output "${escapedOutput}"
81+
"""
82+
}
83+
}
84+
5985
def execute_sanity_tests(nomadNode, deviceGroup, deviceGroupId, harnessTemplate, appName, matterType, board, wifi_module, testSuite, branchName, runNumber)
6086
{
6187
def failed_test_results = [failedTests: [], failedCount: 0]
@@ -253,6 +279,7 @@ def publishSonarAnalysis() {
253279
// Create necessary directories
254280
sh "mkdir -p ${env.WORKSPACE}/sonar"
255281
sh "mkdir -p ${env.WORKSPACE}/sonar-cache"
282+
sh "mkdir -p ${env.WORKSPACE}/sonar-user-home"
256283

257284
// Prepare global SonarQube parameters
258285
def sonarqubeParams = [
@@ -283,11 +310,45 @@ def publishSonarAnalysis() {
283310
sonarqubeParams += ["-Dsonar.branch.name=${env.BRANCH_NAME}"]
284311
}
285312

286-
// Capture the sonar-scanner output to parse quality gate status
287-
def sonarOutput = sh(script: "sonar-scanner ${sonarqubeParams.join(' ')}", returnStdout: true).trim()
288-
echo "SonarQube Scanner Output:\n${sonarOutput}"
313+
// Capture the sonar-scanner output with error handling
314+
def sonarOutput = ""
315+
def qualityGateResult = "FAIL"
316+
def qualityGateStatus = "FAILED"
317+
def commit_sha = env.GIT_COMMIT ?: "unknown"
318+
319+
try {
320+
sonarOutput = sh(script: "sonar-scanner ${sonarqubeParams.join(' ')}", returnStdout: true).trim()
321+
echo "SonarQube Scanner Output:\n${sonarOutput}"
322+
323+
// Parse quality gate status from output
324+
def qualityGateMatcher = sonarOutput =~ /QUALITY GATE STATUS:\s*(PASSED|FAILED)/
325+
if (qualityGateMatcher.find()) {
326+
qualityGateStatus = qualityGateMatcher[0][1]
327+
qualityGateResult = (qualityGateStatus == "PASSED") ? "PASS" : "FAIL"
328+
} else {
329+
qualityGateResult = "PASS"
330+
}
331+
332+
// Parse SCM revision ID from output (with fallback)
333+
def scmRevisionMatcher = sonarOutput =~ /SCM revision ID '([a-fA-F0-9]+)'/
334+
if (scmRevisionMatcher.find()) {
335+
commit_sha = scmRevisionMatcher[0][1]
336+
echo "Extracted SCM revision ID: ${commit_sha}"
337+
} else {
338+
echo "SCM revision ID not found, using fallback: ${commit_sha}"
339+
}
340+
341+
} catch (Exception e) {
342+
echo "SonarQube scanner failed with error: ${e.getMessage()}"
343+
sonarOutput = "SonarQube analysis failed: ${e.getMessage()}"
344+
qualityGateResult = "FAIL"
345+
qualityGateStatus = "FAILED"
346+
}
347+
348+
echo "Static Analysis Quality Gate Status: ${qualityGateStatus}"
349+
echo "Static Analysis Result: ${qualityGateResult}"
289350

290-
return sonarOutput
351+
return [status: qualityGateStatus, result: qualityGateResult, output: sonarOutput, commit_sha: commit_sha]
291352
}
292353
}
293354
}

0 commit comments

Comments
 (0)