jenkins集成sonarqube进行代码质量检查

1.搭建环境和下载工具包

所有工具下载地址
http://download.csdn.net/download/ly13237009762/11995111

1.1搭建环境

window系统+Jdk1.8+mysql5.6+python3.6

1.2下载工具包
工具包描述下载地址(未精确到版本号)
sonarqube-7.4.zipsonarqube核心包https://www.sonarqube.org/downloads/
sonar-scanner-2.8.zip代码扫描器https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/
jenkins-2.190.2.zipjenkins核心http://mirrors.jenkins.io/war-stable/

2.配置SonarQube

2.1创建数据库sonar

SonarQube需要连接mysql,并且mysql需要开启innodb存储引擎,查询是否开启的sql语句是SHOW ENGINES。mysql5.6默认开启了innodb,那么直接在数据库中创建以下数据库,Sql如下:
创建数据库sonar:

CREATE DATABASE sonar CHARACTER SET utf8 COLLATE utf8_general_ci;  
CREATE USER 'sonar' IDENTIFIED BY 'sonar';  
GRANT ALL ON sonar.* TO 'sonar'@'%' IDENTIFIED BY 'sonar';  
GRANT ALL ON sonar.* TO 'sonar'@'localhost' IDENTIFIED BY 'sonar';  
FLUSH PRIVILEGES;
2.2解压SonarQube到指定目录

本项目解压到了D:\jenkins_sonar\sonarqube-7.4,目录结构如图2-1
在这里插入图片描述
图2-1
2.3修改SonarQube配置文件
conf目录下的sonar.properties增加以下配置,主要是连接数据库的配置和tomcat端口。

sonar.jdbc.username=root
sonar.jdbc.password=root
sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance
sonar.web.port=9999

因为SonarQube需要jdk1.8或者以上才能启动,如图2-3所示wrapper.conf指定jdk版本为1.8。
图2-3
在这里插入图片描述

2.4 SonarQube汉化

下载汉化插件sonar-l10n-zh-plugin-1.24.jar放到extentions下的plugins目录下,下载地址https://github.com/SonarQubeCommunity/sonar-l10n-zh/releases
在这里插入图片描述

2.5 启动SonarQube

打开图示windows批处理文件,用本机浏览器输入localhost:9999能正常进入首页,则如果命令行出现SonarQube is up说明SonarQube配置成功。
在这里插入图片描述

3.配置SonarQube代码扫描器

3.1解压SonarQube扫描器到指定目录

如图3-1所示
在这里插入图片描述
图3-1

3.2 配置环境变量

把解压后得到的bin目录加入到环境变量中,图示目录是D:\jenkins_sonar\sonar-scanner-3.2.0.1227-windows\bin。(不会配置的百度)

3.3 测试本地代码扫描

在一个项目根目录下创建sonar-project.properties配置文件,并添加以下配置,以目录D:\jenkins_sonar\WebStarter-security为例。
注意:是在需要检查的项目根目录下

#sonarqube服务器地址
sonar.host.url=http://localhost:9999
#sonarqube用户名默认就是admin
sonar.login=admin
#sonarqube密码
sonar.password=admin默认就是admin
#项目唯一标识(不能出现重复)
sonar.projectKey=WebStarter-security
#项目名称
sonar.projectName=WebStarter-security
#源代码目录
sonar.sources=./
#编译生成的class文件的所在目录
sonar.java.binaries=target
#版本号
sonar.projectVersion=1.0
sonar.language=java
#源代码文件编码
sonar.sourceEncoding=UTF-8

用cmd进入项目根目录D:\jenkins_sonar\WebStarter-security,执行sonar-scanner命令,出现以下提示则说明项目代码扫描成功。
在这里插入图片描述
浏览器进入http://localhost:9999/projects,右上角进行登录,默认管理员用户名和密码都是admin。
点击项目名进入对应的项目,出现以下扫描结果,就是成功扫描代码了。
在这里插入图片描述

4.Jenkins集成SonarQube

4.1发布Jenkins到tomcat

从官网下载到的Jenkins是一个.msi文件,安装这个文件,在安装目录里面有个Jenkins.war,把它放入到tomcat的webapps里面,然后启动,就正常启动Jenkins了。
因为我设置的tomcat的端口是9998,所以用浏览器输入localhost:9998/jenkins就能进入jenkins,根据页面提示找到密码并输入进去系统。如图4-1
在这里插入图片描述
第一次进去会提示安装插件,先跳过,等会统一安装,跳过后出现添加用户的界面。我添加了一个用户名admin,密码为123456的用户。如图4-2
在这里插入图片描述
图4-2
然后进入到实例配置中把localhost改为ip,如图4-3
在这里插入图片描述

图4-3
最后进入到Jenkins首页,如图4-4
在这里插入图片描述
图4-4
修改tomcat配置
首次登录后会有以下提示,需要修改tomcat的server.xml 加入URIEncoding=”UTF-8”。
在这里插入图片描述

4.2安装Jenkins集成sonar所需插件

在jenkins首页左边导航栏,点击Manage jenkins,然后找到二级导航Manage Plugins,然后点击可选插件,最后右上角上面搜索需要的插件,点击直接安装就可以。
需要安装以下插件,安装完成需要重启jenkins。

插件名称插件描述
SonarQube Scanner for Jenkinsjenkins集成SonarQube 所需要的
Subversion Plug-inSvn插件
Localization: Chinese (Simplified) Svn插件汉化Jenkins插件
Email Extension Plugin发送邮件的插件

配置刚才安装在好的插件
在这里插入图片描述
Sonarqube配置
在这里插入图片描述
Jenkins访问地址配在这里插入图片描述

Svn配置
在这里插入图片描述
邮箱配置
在这里插入图片描述
邮件通知
在这里插入图片描述

测试邮箱配置是否成功
在这里插入图片描述
集成jdk和扫描器,导航如图
在这里插入图片描述
在这里插入图片描述
4.4安装python3.6
jenkins集成sonarqube时,需要将sonarqube检查的代码结果发送到指定邮箱,需要使用python脚本。
安装python-3.6.0-amd64.exe,并勾上把python添加到环境变量。如果无法自动添加,可以手动添加python根目录到环境变量。
在这里插入图片描述
安装python操作mysql的包,用cmd命令输入python -m pip install PyMySQL
提示successfully则安装成功,
安装jinja2模块,用cmd命令输入pip install jinja2,提示successfully则安装成功,
如图
在这里插入图片描述
提示:192.168.150.129服务器上面有python2.7,但是不支持发送邮件的脚本,所以我安装了python3.6和原来的2.7共存,并手动添加了python3.6的环境变量,把python3.6安装根目录的python.exe改成了python3.exe。所以129上面,使用cmd输入python命令执行的是python2.7,使用python3执行的是python3.6。
文档中脚本会执行python3命令。

4.4创建项目

安装好插件,先重启Jenkins,进入首页,点击新建项目,然后按照箭头进行设置,如图
在这里插入图片描述
然后进入输入项目表单界面,如图
在这里插入图片描述
然后点击添加svn账号
在这里插入图片描述
Svn检出策略
在这里插入图片描述
构建触发器,定期构建一次代码
Poll SCM:定时检查源码变更,如果有更新就checkout最新code下来,然后执行构建动作。
Build periodically:周期进行项目构建(源码是否发生变化没有关系)
H H * * 1 (每星期1构建一次)
H 8 * * * (每天8:00 必须build一次源码)
在这里插入图片描述
构建环境,选中无
在这里插入图片描述
构建
构建参数
projectKey,projectName:唯一标识,一般填写项目名,需要和python脚本后面的参数一致。
sources:是扫描代码的路径,./表示项目所有代码。
login,password:对应sonar的管理员账号和密码
java.binaries:编译后的字节码

sonar.projectKey=nkspt
sonar.projectName=nkspt
sonar.sources=./
sonar.projectVersion=1.0
sonar.language=java
sonar.sourceEncoding=UTF-8
sonar.java.binaries=./
sonar.login=admin
sonar.password=admin

构建脚本
进入到脚本目录,并运行脚本,脚本名称后面需要加参数(项目名)。
cd /d D:\jenkins_sonar\scripts
Python3命令
python3 sonar.py nkspt
脚本如下
注意:其中cd /d 后面是脚本的路径
nkspt是sonarqube项目名称,建议用一致的。

import pymysql,os,sys
from jinja2 import FileSystemLoader,Environment

def select_project_uuid(project_name):
    db = pymysql.connect(host="localhost", port=3306, user="root", passwd="root", db="sonar")
    cursor = db.cursor()
    select_p_uuid="SELECT project_uuid,kee FROM projects WHERE `name`= '%s'" %(project_name)
    cursor.execute(select_p_uuid)
    result = cursor.fetchone()
    p_uuid = result[0]
    projectKey = result[1]
    db.close()
    return(p_uuid, projectKey)

def select_total_info(p_uuid):
    total_info=[]
    db = pymysql.connect(host="localhost", port=3306, user="root", passwd="root", db="sonar")
    cursor = db.cursor()

    select_p_links = "SELECT text_value FROM project_measures WHERE text_value LIKE 'java=%' and component_uuid=" + "\'" + p_uuid + "\'"
    cursor.execute(select_p_links)
    p_links = cursor.fetchone()[0].split("=")[1]

    sql_info = "SELECT count(*) FROM issues WHERE project_uuid='%s' and issue_type =%s"
    for leak in [2,3,1]:
        search_data = sql_info %(p_uuid, leak)
        cursor.execute(search_data)
        total_info.append(cursor.fetchone()[0])
    db.close()
    return p_links,total_info

def select_bugs(p_uuid):
    bugs=[]
    db = pymysql.connect(host="localhost", port=3306, user="root", passwd="root", db="sonar")
    cursor = db.cursor()

    sql_info = "SELECT count(*) FROM issues WHERE project_uuid='%s' and issue_type =2 AND severity ='%s'"
    for leak in ['BLOCKER','CRITICAL',"MAJOR",'MINOR','INFO']:
        search_data=sql_info  % (p_uuid,leak)
        cursor.execute(search_data)
        bugs.append(cursor.fetchone()[0])
    db.close()
    return bugs

def select_leaks(p_uuid):
    leaks=[]
    db = pymysql.connect(host="localhost", port=3306, user="root", passwd="root", db="sonar")
    cursor = db.cursor()

    sql_info = "SELECT count(*) FROM issues WHERE project_uuid='%s' and issue_type =3 AND severity ='%s'"
    for leak in ['BLOCKER','CRITICAL',"MAJOR",'MINOR','INFO']:
        search_data=sql_info  % (p_uuid,leak)
        cursor.execute(search_data)
        leaks.append(cursor.fetchone()[0])
    db.close()
    return leaks

def select_bad_tastes(p_uuid):
    tastes=[]
    db = pymysql.connect(host="localhost", port=3306, user="root", passwd="root", db="sonar")
    cursor = db.cursor()

    sql_info="SELECT count(*) FROM issues WHERE project_uuid='%s' and issue_type =1 AND severity ='%s'"
    for leak in ['BLOCKER','CRITICAL',"MAJOR",'MINOR','INFO']:
        search_data=sql_info  % (p_uuid,leak)
        cursor.execute(search_data)
        tastes.append(cursor.fetchone()[0])
    db.close()
    return tastes
   

curpath = os.getcwd()
table_tem_name="table.html"    
def generate_errmsg_table(s_lines="", total_data=[], bugs=[],leaks=[],tastes=[],report_url=""):
    env = Environment(loader=FileSystemLoader(curpath, 'utf-8'))  
    template = env.get_template(table_tem_name)
    html_content = (template.render(lins=s_lines,total_data=total_data, bugs=bugs,leaks = leaks,tastes=tastes,report_url=report_url))
    fh = open(report_html_path, 'w')
    fh.write(html_content)
    fh.close()

project_name = sys.argv[1]
report_html_path="report\\"+project_name+".html"
p_uuid, projectKey=select_project_uuid(project_name)
s_lines,total_data=select_total_info(p_uuid)
bugs=select_bugs(p_uuid)
leaks=select_leaks(p_uuid)
tastes=select_bad_tastes(p_uuid)
report_url="http://192.168.150.129:9999/dashboard?id=%s" %(projectKey)
generate_errmsg_table(s_lines,total_data,bugs,leaks,tastes,report_url)

执行脚本,需要一个模(mú)板table.html,
注意模(mú)板需要和脚本放在同一目录
代码如下

<!DOCTYPE html>
<head>
<meta charset="GB2312"">
<body>
<p style="font-weight:bold;">一、总体情况:</p>
<ul>
<li style="font-weight:bold;">整体运行情况:扫描代码行数:<span style="color:blue">{{lins}}</span>, bugs:<span style="color:red">{{total_data[0]}}</span>, 漏洞:<span style="color:red">{{total_data[1]}}</span>, 坏味道:<span style="color:red">{{total_data[2]}}</span></li>
<li style="font-weight:bold;">URL地址:<a style="font-weight:bold;" href={{report_url}} >{{report_url}}</a></li>
<li style="font-weight:bold;">用户名:登陆用户名为您的名字拼音,密码为123456</a></li>
</ul>
<p style="font-weight:bold;">二、错误信息详情:</p>
<table border="1" cellpadding="10" width="540" height="120">
    <tr ><th></th><th>阻断</th><th>严重</th><th>主要</th><th>次要</th><th>提示</th><th>总数</th></tr>
    <tr bgcolor=#ECFFFF><td>bugs</td><td align="center">{{bugs[0]}}</td><td align="center">{{bugs[1]}}</td><td align="center">{{bugs[2]}}</td><td align="center">{{bugs[3]}}</td><td align="center">{{bugs[4]}}</td><td align="center" style="color:red">{{total_data[0]}}</td></tr>
    <tr bgcolor=#D2E9FF><td>漏洞</td><td align="center">{{leaks[0]}}</td><td align="center">{{leaks[1]}}</td><td align="center">{{leaks[2]}}</td><td align="center">{{leaks[3]}}</td><td align="center">{{leaks[4]}}</td><td align="center" style="color:red">{{total_data[1]}}</td></tr>
    <tr bgcolor=#ECFFFF><td>坏味道</td><td align="center">{{tastes[0]}}</td><td align="center">{{tastes[1]}}</td><td align="center">{{tastes[2]}}</td><td align="center">{{tastes[3]}}</td><td align="center">{{tastes[4]}}</td><td align="center" style="color:red">{{total_data[2]}}</td></tr>
</table>
<br></br>
</body>
</html>

注意:还需要在模板的目录下创建report目录
在这里插入图片描述
构建后操作
在这里插入图片描述

点击高级设置
在这里插入图片描述
增加一个触发器,点击allways
在这里插入图片描述
再点高级
在这里插入图片描述
最后的操作。
在这里插入图片描述
保存
在这里插入图片描述
4.4测试项目
首页找到刚才创建的项目,并点击build now,完成项目构建。
在这里插入图片描述
构建过程会产生一个构建状态,点击进去
在这里插入图片描述
然后查看控制台输出
在这里插入图片描述
出现success则说明项目构建成功,然后查看邮件是否正常成功发送到邮箱。最后访问sonarqube系统查看项目扫描信息是否存在。

5.其他事项

5.1 SonarQube分配项目权限

SonarQube默认是所有项目都是公开的,即使用户未登录,也能查看扫描结果,所以需要给项目分配给用户,让指定的用户能够访问。
先创建群组并把需要的用户添加到群组,如下图
在这里插入图片描述
在这里插入图片描述
点击一个项目,然后进入权限配置,如下图
在这里插入图片描述

如下图,把项目权限改成私有
在这里插入图片描述
设置用户的权限或者群组的权限。
在这里插入图片描述
5.2 Jenkins创建中文名称的项目
在使用Jenkins创建项目使用中文时,发现钉钉邮箱无法接收到邮件,而其他平台的邮箱能够正常接收,目前原因未知,所以建议,在创建项目时,尽量不要出现中文字符。
5.3 Jenkins第一次构建项目会失败
如图,第一次构建有时会无法正常执行python脚本,是正常情况,因为sonarqube进行代码扫描时,需要把扫描的结果保存到数据库,而此时,往数据库插入的数据尚未提交。
解决的方法是重新构建一次。
在这里插入图片描述
5.4 sonarqube添加checkstyle规则
使用CheckStyle代码规则配置文件
注意:这种方法只有新建一个质量配置时才能用,质量配置创建好后,就不能利用配置文件来配置代码规则了。
在这里插入图片描述
按照上面两个图,填写好相关信息后,点击创建按钮。这里有几个问题需要注意下:
问题一 配置文件不能以<?xml version="1.0" encoding="UTF-8"?>开头,否则点击创建按钮时会有如下错误提示:
在这里插入图片描述
因此要把此标签去掉,直接以标签开头,如下图所示:
在这里插入图片描述
问题二 配置文件中不能有重复的规则,否则点击创建按钮时,会有报错提示
问题三 配置文件中不能有规则模板(规则模板后面介绍),当配置文件中有规则模板,点击创建按钮时,会有报错提示,如下图,看不到具体错误信息,可以打开f12查询HTTP请求。
在这里插入图片描述

问题4:激活checkstyle规则。
搜索规则
在这里插入图片描述
创建自定义规则
在这里插入图片描述
填写规则表单,根据实际需求。
在这里插入图片描述
点击活动激活规则
在这里插入图片描述
5.5同时使用checkstyle规则和自带规则
进去创建好的规则
在这里插入图片描述
继承自带规则
在这里插入图片描述
5.6 sonarqube进行代码标记
当扫描的结果,进行标记的时候,会将这个结果从未处理加入到其他类别,如图我点击误判则将结果存放到误判的分类,下次扫描也不会影响本次标记的结果。
1.标记为解决,如果下次扫描还未解决,则会重开,也就是把结果从解决中移动到未处理中。
2.标记为误报,如下图,如果发现检查结果误判,可以点击误判,则下次不会检查此bug。
在这里插入图片描述
4.标记为不会修复,即使下次扫描还未解决,也不会出现在未处理中。
5.标记为确认,只是告诉开发人员,此代码需要进行修改,无其他作用。
总之,除了点击解决,但是下次扫描还未解决,会重新加入到未处理,其他都不会重新加入到未处理。
在这里插入图片描述
5.7安装翻译插件
Sonarqube的扫描结果的提示信息是没有办法汉化的,解决的方法是安装谷歌浏览器的一个插件,效果如图。
在这里插入图片描述
安装插件教程
第一步,打开拓展程序
在这里插入图片描述
第二步,打开开发者模式,并安装插件,如图
在这里插入图片描述
第三步,选择插件文件夹
在这里插入图片描述
第四步,配置插件
在这里插入图片描述
第五步,切换翻译引擎
在这里插入图片描述

选中百度翻译。
在这里插入图片描述
选中要翻译的文字,并点击翻译
在这里插入图片描述
查看翻译结果
在这里插入图片描述
5.8关于无法正确显示新增bug的问题
如图,当未配置版本号或者配置的版本号不发生改变时,无法正确显示新增的问题。
在这里插入图片描述
解决的方法是使用动态版本号如图
sonar.projectVersion= S V N R E V I S I O N 其 中 SVN_REVISION 其中 SVNREVISIONSVN_REVISION是Jenkins内置变量,可以用表达式直接获得.
在这里插入图片描述
5.9关于扫描结果会发送上一次的
正常情况下,扫描结果应该是发送当前扫描结果的,但是有时候会发送上一次的,造成这一现象的问题是Jenkins只是调用了sonar扫描的api之后,就直接执行下一步脚本发送了,而没有去判断结果有没有出来。
解决方法,python脚本延长等待时间如图。
在这里插入图片描述
6.Jenkins发送检查结果到钉钉
6.1创建钉钉群机器人
在钉钉群中,点击管理群助手
在这里插入图片描述
添加机器人
在这里插入图片描述

添加关键字,当标题或者内容具有关键字的消息,才会被发送,所以添加代码为关键字。
在这里插入图片描述

得到webhook地址。
6.2使用自定义机器人
获取到 Webhook 地址后,用户可以使用任何方式向这个地址发起 HTTP POST 请求,即可实现给该群组发送消息。注意,发起POST请求时,必须将字符集编码设置成 UTF-8。

发送消息类型如下图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述6.3使用python脚本发送消息
Jenkins设置
由于我的 Jenkins 跑在 window 平台,执行脚本需要加到 Post build task 批处理中。(需要安装 Post build task plugin)
如图设置运行脚本即可。
在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐