基础信息
接入须知
接入产品线联系王浩彬,进行产品线管理账户创建,其中创建的项目务必改为私有,避免项目公开
Sonar 访问地址:
前端接入
接入逻辑:通过gitlab ci runner来进行代码静态扫描
添加文件总体结构
配置gitlab ci
- 编写.gitlab-ci.yml
stages:
- test
- sonar
- report
variables:
SONAR_PROJECT_KEY: tz-plugin #此处换成snonar的项目key
unit-test:
image: node:16.20.2
stage: test
before_script:
# - npm install pnpm@8.10.3 -g
# - echo 'set npm registry'
# - npm config set registry https://registry.npmmirror.com
# - npm config set @cvte:registry http://artifactory.gz.cvte.cn/artifactory/api/npm/cvte-npm-registry/
# - npm config set @cvte-eus:registry http://artifactory.gz.cvte.cn/artifactory/api/npm/cvte-npm-registry/
# - pnpm install
# - npm install --force
- npm install --legacy-peer-deps
script:
# - npm run test:ci
- npm run test
coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
artifacts:
paths:
- __test__/
- test-report.xml
- coverage
allow_failure: true
sonarqube-check:
stage: sonar
image:
name: sonarsource/sonar-scanner-cli:latest
entrypoint: [""]
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner
allow_failure: true
report:
image: python:latest
stage: report
script:
- pip install requests
- python report.py
- 配置gitlab runner
- 配置gitlab变量,参考sonar的指引
sonar-project.properties,配置sonar的扫码配置
sonar.projectKey=tz-plugin
sonar.qualitygate.wait=true
# Source
sonar.sources=/builds/it-frontend/tz-plugin/packages
# Where to find tests file, also src
sonar.tests=/builds/it-frontend/tz-plugin/packages
# But we get specific here
# We don't need to exclude it in sonar.sources because it is automatically taken care of
sonar.test.inclusions=src/**/*.test.js,src/**/*.test.jsx,src/**/*.test.ts,src/**/*.test.tsx
# Now specify path of lcov and testlog
#sonar.testExecutionReportPaths=/test-reporter.xml
sonar.javascript.lcov.reportPaths=/builds/it-frontend/tz-plugin/__test__/coverage/lcov.info
report.py 配置企微机器人推送,可选
主要调整配置信息即可,其中
SONAR_USERNAME = “squ_2a5598f035ff6e165ae052ece4cffb2b025bfd87”
import requests
import json
import sys
import base64
import os
from datetime import datetime
def get_sonar_auth_header(username, password):
auth_str = f"{username}:{password}"
auth_bytes = auth_str.encode('ascii')
base64_auth = base64.b64encode(auth_bytes).decode('ascii')
return {"Authorization": f"Basic {base64_auth}"}
def get_latest_analysis(sonar_url, project_key, auth_header):
"""
获取最新的 Sonar 分析结果
"""
api_url = f"{sonar_url}/api/project_analyses/search"
params = {
"project": project_key,
"ps": 1 # 只获取最新的一次分析
}
try:
response = requests.get(api_url, params=params, headers=auth_header)
response.raise_for_status()
analyses = response.json()['analyses']
return analyses[0] if analyses else None
except requests.RequestException as e:
print(f"获取Sonar分析结果时出错: {e}")
sys.exit(1)
def get_sonar_issues(sonar_url, project_key, auth_header, created_after):
"""
获取 Sonar 最新扫描发现的问题
"""
api_url = f"{sonar_url}/api/issues/search"
params = {
"componentKeys": project_key,
"createdAfter": created_after,
"ps": 100 # 每页显示的问题数,可以根据需要调整
}
all_issues = []
page = 1
while True:
params['p'] = page
try:
response = requests.get(api_url, params=params, headers=auth_header)
response.raise_for_status()
data = response.json()
issues = data['issues']
all_issues.extend(issues)
if len(issues) < params['ps']:
break
page += 1
except requests.RequestException as e:
print(f"获取Sonar问题时出错: {e}")
sys.exit(1)
return all_issues
def get_sonar_metrics(sonar_url, project_key, auth_header):
"""
获取 Sonar 项目的度量指标,包括单元测试覆盖率
"""
api_url = f"{sonar_url}/api/measures/component"
params = {
"component": project_key,
"metricKeys": "coverage,tests,test_errors,test_failures,test_execution_time"
}
try:
response = requests.get(api_url, params=params, headers=auth_header)
response.raise_for_status()
return {measure['metric']: measure['value'] for measure in response.json()['component']['measures']}
except requests.RequestException as e:
print(f"获取Sonar度量指标时出错: {e}")
sys.exit(1)
def format_message(latest_analysis, issues, metrics, sonar_url, project_key):
"""
格式化 Sonar 结果为企业微信消息,包含 GitLab CI 信息和 Sonar 仪表板链接
"""
# 获取 GitLab CI 环境变量
ci_project_name = os.environ.get('CI_PROJECT_NAME', 'Unknown Project')
ci_commit_sha = os.environ.get('CI_COMMIT_SHORT_SHA', 'Unknown Commit')
ci_commit_branch = os.environ.get('CI_COMMIT_BRANCH', 'Unknown Branch')
ci_commit_author = os.environ.get('GITLAB_USER_NAME', 'Unknown User')
sonar_dashboard_url = f"{sonar_url}/dashboard?id={project_key}"
message = f"Sonar 代码检测结果 - {ci_project_name}\n\n"
message += f"构建信息:\n"
message += f"- 分支: {ci_commit_branch}\n"
message += f"- 提交: {ci_commit_sha}\n"
message += f"- 作者: {ci_commit_author}\n\n"
if latest_analysis:
message += f"最新分析日期: {latest_analysis['date'][:10]}\n\n"
message += f"本次扫描发现的问题:\n"
issue_counts = {'BUG': 0, 'VULNERABILITY': 0, 'CODE_SMELL': 0}
for issue in issues:
issue_counts[issue['type']] += 1
message += f"- Bugs: {issue_counts['BUG']}\n"
message += f"- 漏洞: {issue_counts['VULNERABILITY']}\n"
message += f"- 代码异味: {issue_counts['CODE_SMELL']}\n\n"
message += f"单元测试信息:\n"
message += f"- 覆盖率: {metrics.get('coverage', 'N/A')}%\n"
message += f"- 测试用例数: {metrics.get('tests', 'N/A')}\n"
message += f"- 错误数: {metrics.get('test_errors', 'N/A')}\n"
message += f"- 失败数: {metrics.get('test_failures', 'N/A')}\n"
message += f"- 执行时间: {float(metrics.get('test_execution_time', 0)) / 1000:.2f} 秒\n\n"
message += f"详细报告: {sonar_dashboard_url}"
return message
def send_to_wechat(webhook_url, message):
"""
发送消息到企业微信机器人
"""
headers = {'Content-Type': 'application/json'}
data = {
"msgtype": "text",
"text": {
"content": message
}
}
try:
response = requests.post(webhook_url, headers=headers, data=json.dumps(data))
response.raise_for_status()
print("消息已成功发送到企业微信")
except requests.RequestException as e:
print(f"发送消息到企业微信时出错: {e}")
sys.exit(1)
if __name__ == "__main__":
# 配置信息
SONAR_URL = "http://itsonar.gz.cvte.cn"
PROJECT_KEY = os.environ.get('SONAR_PROJECT_KEY', 'your-project-key')
SONAR_USERNAME = "squ_2a5598f035ff6e165ae052ece4cffb2b025bfd87"
SONAR_PASSWORD = ""
WECHAT_WEBHOOK = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c144bea9-1adf-4da4-99f7-1672bd90628f" ## 换成自己的群机器人
auth_header = get_sonar_auth_header(SONAR_USERNAME, SONAR_PASSWORD)
# 获取最新的 Sonar 分析结果
latest_analysis = get_latest_analysis(SONAR_URL, PROJECT_KEY, auth_header)
# 获取最新扫描发现的问题
created_after = latest_analysis['date'] if latest_analysis else None
issues = get_sonar_issues(SONAR_URL, PROJECT_KEY, auth_header, created_after)
# 获取项目度量指标,包括单元测试覆盖率
metrics = get_sonar_metrics(SONAR_URL, PROJECT_KEY, auth_header)
# 格式化消息
message = format_message(latest_analysis, issues, metrics, SONAR_URL, PROJECT_KEY)
# 发送到企业微信
send_to_wechat(WECHAT_WEBHOOK, message)
后端接入
参考文档:https://kb.cvte.com/pages/viewpage.action?pageId=436872198
作者:王浩彬 创建时间:2024-09-20 11:27
最后编辑:王浩彬 更新时间:2024-12-23 11:22
最后编辑:王浩彬 更新时间:2024-12-23 11:22