Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions jenkins_integration/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pipeline
steps {
script {
// Declare variables outside the Docker container block
def staticAnalysisResult = ""
def staticAnalysisStatus = ""
def staticAnalysisOutput = ""
def sonarCommitSha = ""
Expand All @@ -31,21 +30,18 @@ pipeline
def sonarResult = pipelineFunctions.publishSonarAnalysis()

// Extract results for GitHub reporting
staticAnalysisResult = sonarResult.result
staticAnalysisStatus = sonarResult.status
staticAnalysisOutput = sonarResult.output
sonarCommitSha = sonarResult.commit_sha

echo "Static Analysis Result: ${staticAnalysisResult}"
echo "Static Analysis Status: ${staticAnalysisStatus}"
echo "Commit SHA from SonarQube: ${sonarCommitSha}"
}
// Send results to GitHub PR only
try {
if (!bypass_send_results_gh && env.CHANGE_ID) {
if (env.CHANGE_ID) {
pipelineFunctions.send_sonar_results_to_github(
sonarCommitSha,
staticAnalysisResult,
staticAnalysisStatus,
staticAnalysisOutput,
env.CHANGE_ID,
Expand Down
175 changes: 164 additions & 11 deletions jenkins_integration/github/send_sonar_results_to_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import requests
import sys
from datetime import datetime
import urllib.parse

def post_pr_comment(github_token, repo_owner, repo_name, pr_number, comment_body):
"""Post a comment to GitHub PR"""
Expand Down Expand Up @@ -61,11 +62,82 @@ def post_commit_status(github_token, repo_owner, repo_name, commit_sha, state, d
print(f"Response: {response.text}")
return False

def create_comment_body(result, status, commit_sha, branch_name, target_branch, sonar_output):
def fetch_sonarqube_quality_gate(sonar_token, sonar_url, project_key, branch_name=None, pr_key=None):
"""Fetch detailed quality gate information from SonarQube API"""
try:
headers = {"Authorization": f"Bearer {sonar_token}"}

# Build the API URL for quality gate status
if pr_key:
# For pull requests
api_url = f"{sonar_url}/api/qualitygates/project_status?projectKey={project_key}&pullRequest={pr_key}"
elif branch_name:
# For branches
encoded_branch = urllib.parse.quote(branch_name, safe='')
api_url = f"{sonar_url}/api/qualitygates/project_status?projectKey={project_key}&branch={encoded_branch}"
else:
# Main branch
api_url = f"{sonar_url}/api/qualitygates/project_status?projectKey={project_key}"

print(f"🔍 Fetching quality gate from: {api_url}")
response = requests.get(api_url, headers=headers)

if response.status_code == 200:
data = response.json()
return data.get('projectStatus', {})
else:
print(f"⚠️ Failed to fetch quality gate details: {response.status_code}")
return None

except Exception as e:
print(f"⚠️ Error fetching quality gate details: {str(e)}")
return None

def fetch_sonarqube_measures(sonar_token, sonar_url, project_key, branch_name=None, pr_key=None):
"""Fetch detailed measures from SonarQube API"""
try:
headers = {"Authorization": f"Bearer {sonar_token}"}

# Metrics we want to fetch
metrics = [
"new_bugs", "new_vulnerabilities", "new_violations", "new_security_hotspots",
"new_code_smells", "new_coverage", "new_duplicated_lines_density",
"bugs", "vulnerabilities", "violations", "security_hotspots", "code_smells",
"coverage", "duplicated_lines_density", "ncloc"
]

metric_keys = ",".join(metrics)

if pr_key:
api_url = f"{sonar_url}/api/measures/component?component={project_key}&pullRequest={pr_key}&metricKeys={metric_keys}"
elif branch_name:
encoded_branch = urllib.parse.quote(branch_name, safe='')
api_url = f"{sonar_url}/api/measures/component?component={project_key}&branch={encoded_branch}&metricKeys={metric_keys}"
else:
api_url = f"{sonar_url}/api/measures/component?component={project_key}&metricKeys={metric_keys}"

print(f"🔍 Fetching measures from: {api_url}")
response = requests.get(api_url, headers=headers)

if response.status_code == 200:
data = response.json()
measures = {}
for measure in data.get('component', {}).get('measures', []):
measures[measure['metric']] = measure.get('value', '0')
return measures
else:
print(f"⚠️ Failed to fetch measures: {response.status_code}")
return {}

except Exception as e:
print(f"⚠️ Error fetching measures: {str(e)}")
return {}

def create_comment_body(status, commit_sha, branch_name, target_branch, sonar_output, quality_gate_details=None, measures=None):
"""Create formatted comment body for GitHub PR"""

# Determine status emoji and text
if result == "PASS":
if status == "PASSED":
result_emoji = "✅"
result_text = "**PASSED**"
else:
Expand All @@ -79,20 +151,75 @@ def create_comment_body(result, status, commit_sha, branch_name, target_branch,
else:
truncated_output = sonar_output

# Build quality gate details section
quality_gate_section = ""
if quality_gate_details:
conditions = quality_gate_details.get('conditions', [])
if conditions:
quality_gate_section = "\n### 🎯 Quality Gate Conditions\n"
for condition in conditions:
metric_name = condition.get('metricKey', 'Unknown')
status_icon = "✅" if condition.get('status') == 'OK' else "❌"
actual_value = condition.get('actualValue', 'N/A')
error_threshold = condition.get('errorThreshold', 'N/A')
quality_gate_section += f"- {status_icon} **{metric_name}**: {actual_value} (threshold: {error_threshold})\n"

# Build measures section
measures_section = ""
if measures:
measures_section = "\n### 📊 Code Quality Metrics\n"

# New code metrics (for PRs)
new_metrics = []
if measures.get('new_bugs') and measures.get('new_bugs') != '0':
new_metrics.append(f"🐛 **New Bugs**: {measures['new_bugs']}")
if measures.get('new_vulnerabilities') and measures.get('new_vulnerabilities') != '0':
new_metrics.append(f"🔒 **New Vulnerabilities**: {measures['new_vulnerabilities']}")
if measures.get('new_violations') and measures.get('new_violations') != '0':
new_metrics.append(f"⚠️ **New Violations**: {measures['new_violations']}")
if measures.get('new_security_hotspots') and measures.get('new_security_hotspots') != '0':
new_metrics.append(f"🔥 **New Security Hotspots**: {measures['new_security_hotspots']}")
if measures.get('new_code_smells') and measures.get('new_code_smells') != '0':
new_metrics.append(f"💨 **New Code Smells**: {measures['new_code_smells']}")
if measures.get('new_duplicated_lines_density'):
new_metrics.append(f"📄 **New Duplicated Lines**: {measures['new_duplicated_lines_density']}%")

if new_metrics:
measures_section += "**New Code:**\n"
for metric in new_metrics:
measures_section += f"- {metric}\n"
measures_section += "\n"

# Overall metrics
overall_metrics = []
if measures.get('bugs'):
overall_metrics.append(f"🐛 **Total Bugs**: {measures['bugs']}")
if measures.get('vulnerabilities'):
overall_metrics.append(f"🔒 **Total Vulnerabilities**: {measures['vulnerabilities']}")
if measures.get('violations'):
overall_metrics.append(f"⚠️ **Total Violations**: {measures['violations']}")
if measures.get('code_smells'):
overall_metrics.append(f"💨 **Total Code Smells**: {measures['code_smells']}")
if measures.get('ncloc'):
overall_metrics.append(f"📝 **Lines of Code**: {measures['ncloc']}")

if overall_metrics:
measures_section += "**Overall:**\n"
for metric in overall_metrics:
measures_section += f"- {metric}\n"

comment_body = f"""## 🔍 SonarQube Static Analysis Results

**Result:** {result_emoji} {result_text}
**Quality Gate Status:** {status}
**Commit SHA:** `{commit_sha}`

### 📊 Analysis Summary
- **Branch:** `{branch_name}`
- **Target:** `{target_branch}`
- **Analysis Time:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}

### 📋 Detailed Results
- **Analysis Time:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}{quality_gate_section}{measures_section}
### 📋 Scanner Output
<details>
<summary>Click to view SonarQube output</summary>
<summary>Click to view SonarQube scanner output</summary>

```
{truncated_output}
Expand All @@ -112,24 +239,50 @@ def main():
parser.add_argument("--repo_name", default="matter_extension", help="GitHub repository name")
parser.add_argument("--pr_number", required=True, help="Pull request number")
parser.add_argument("--commit_sha", required=True, help="Git commit SHA")
parser.add_argument("--result", required=True, choices=["PASS", "FAIL"], help="SonarQube analysis result")
parser.add_argument("--status", required=True, help="SonarQube quality gate status")
parser.add_argument("--status", required=True, choices=["PASSED", "FAILED"], help="SonarQube analysis result")

parser.add_argument("--branch_name", required=True, help="Source branch name")
parser.add_argument("--target_branch", required=True, help="Target branch name")
parser.add_argument("--sonar_output", required=True, help="SonarQube scanner output")
parser.add_argument("--sonar_token", help="SonarQube token for API access")
parser.add_argument("--sonar_url", default="https://sonarqube.silabs.net", help="SonarQube server URL")
parser.add_argument("--project_key", default="matter_extension", help="SonarQube project key")

args = parser.parse_args()

try:
# Fetch detailed SonarQube information if token is provided
quality_gate_details = None
measures = None

if args.sonar_token:
print("🔍 Fetching detailed SonarQube quality gate information...")
quality_gate_details = fetch_sonarqube_quality_gate(
args.sonar_token,
args.sonar_url,
args.project_key,
args.branch_name,
args.pr_number
)

print("📊 Fetching SonarQube measures...")
measures = fetch_sonarqube_measures(
args.sonar_token,
args.sonar_url,
args.project_key,
args.branch_name,
args.pr_number
)

# Create comment body
comment_body = create_comment_body(
args.result,
args.status,
args.commit_sha,
args.branch_name,
args.target_branch,
args.sonar_output
args.sonar_output,
quality_gate_details,
measures
)

# Post PR comment
Expand Down
19 changes: 9 additions & 10 deletions jenkins_integration/jenkinsFunctions.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ def send_test_results_to_github(commit_sha, sqa_tests_result, sqa_tests_summary)
/**
* Send SonarQube results to GitHub PR using Python script
*/
def send_sonar_results_to_github(commit_sha, result, status, sonar_output, pr_number, branch_name, target_branch) {
def send_sonar_results_to_github(commit_sha, status, sonar_output, pr_number, branch_name, target_branch) {
withCredentials([
usernamePassword(credentialsId: 'Matter-Extension-GitHub', usernameVariable: 'GITHUB_APP', passwordVariable: 'GITHUB_ACCESS_TOKEN')
usernamePassword(credentialsId: 'Matter-Extension-GitHub', usernameVariable: 'GITHUB_APP', passwordVariable: 'GITHUB_ACCESS_TOKEN'),
string(credentialsId: 'sonarqube_token', variable: 'SONAR_SECRET')
]) {
// Escape sonar output for shell command
def escapedOutput = sonar_output.replace('"', '\\"').replace('`', '\\`').replace('$', '\\$')
Expand All @@ -73,11 +74,13 @@ def send_sonar_results_to_github(commit_sha, result, status, sonar_output, pr_nu
--repo_name "matter_extension" \\
--pr_number ${pr_number} \\
--commit_sha ${commit_sha} \\
--result ${result} \\
--status ${status} \\
--branch_name "${branch_name}" \\
--target_branch "${target_branch}" \\
--sonar_output "${escapedOutput}"
--sonar_output "${escapedOutput}" \\
--sonar_token \${SONAR_SECRET} \\
--sonar_url "https://sonarqube.silabs.net" \\
--project_key "matter_extension"
"""
}
}
Expand Down Expand Up @@ -312,7 +315,6 @@ def publishSonarAnalysis() {

// Capture the sonar-scanner output with error handling
def sonarOutput = ""
def qualityGateResult = "FAIL"
def qualityGateStatus = "FAILED"
def commit_sha = env.GIT_COMMIT ?: "unknown"

Expand All @@ -324,9 +326,8 @@ def publishSonarAnalysis() {
def qualityGateMatcher = sonarOutput =~ /QUALITY GATE STATUS:\s*(PASSED|FAILED)/
if (qualityGateMatcher.find()) {
qualityGateStatus = qualityGateMatcher[0][1]
qualityGateResult = (qualityGateStatus == "PASSED") ? "PASS" : "FAIL"
} else {
qualityGateResult = "PASS"
qualityGateStatus = "PASSED"
}

// Parse SCM revision ID from output (with fallback)
Expand All @@ -341,14 +342,12 @@ def publishSonarAnalysis() {
} catch (Exception e) {
echo "SonarQube scanner failed with error: ${e.getMessage()}"
sonarOutput = "SonarQube analysis failed: ${e.getMessage()}"
qualityGateResult = "FAIL"
qualityGateStatus = "FAILED"
}

echo "Static Analysis Quality Gate Status: ${qualityGateStatus}"
echo "Static Analysis Result: ${qualityGateResult}"

return [status: qualityGateStatus, result: qualityGateResult, output: sonarOutput, commit_sha: commit_sha]
return [status: qualityGateStatus, output: sonarOutput, commit_sha: commit_sha]
}
}
}
Expand Down
Loading
Loading