草庐IT

代码质量检测-SonarQube

懒死洛特 2023-05-05 原文

文章目录


前言

CI/CD流水线完善计划, 增加代码质量检查作业,在开发代码合入前提前发现不安全问题,因此引入代码质量检测-SonarQube服务。


一、SonarQube是什么?

Sonar是一个用于代码质量管理的开源平台,用于管理Java源代码的质量。通过插件机制,Sonar 可以集成不同的测试工具,代码分析工具,以及持续集成工具,比如pmd-cpd、checkstyle、findbugs、Jenkins。通过不同的插件对这些结果进行再加工处理,通过量化的方式度量代码质量的变化,从而可以方便地对不同规模和种类的工程进行代码质量管理。同时 Sonar 还对大量的持续集成工具提供了接口支持,可以很方便地在持续集成中使用 Sonar。 此外,Sonar 的插件还可以对 Java 以外的其他编程语言提供支持,对国际化以及报告文档化也有良好的支持。

二、SonarQube安装步骤

当前最新发布 SonarQube 社区版9.7,以下步骤以此为例。

1.docker安装

快速安装,仅限于测试或则体验:

docker run -d --name sonarqube --restart always -p 9000:9000 sonarqube

2.docker-compose安装

可用于持久化,私有化部署使用。
docker-compose.yml 配置:

version: "3"
services:
  sonarqube:
    image: sonarqube:9.7-community
    container_name: sonarqube
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
    ports:
      - "9000:9000"
  db:
    image: postgres:12
    container_name: postgres
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - postgresql:/var/lib/postgresql
      - postgresql_data:/var/lib/postgresql/data
volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql:
  postgresql_data:


如果是非volume安装,注意给本地文件权限
chmod 777 -R xxxx

修改系统资源:

sysctl -w vm.max_map_count=524288
sysctl -w fs.file-max=131072
ulimit -n 131072
ulimit -u 8192

在docker-compose.yml 文件更目录执行:

docker compose up -d

3. 访问SonarQube

URL: http://localhost:9000
用户名: admin
密码: admin

4. 配置SonarQube

4.1 安装中文插件

  1. 在线安装:

    插件在GitHub,因为网络原因可能会安装失败,多试几次。

    
    Caused by: org.sonar.api.utils.SonarException: Fail to download: https://github.com/xuhuisheng/sonar-l10n-zh/releases/download/sonar-l10n-zh-plugin-9.7/sonar-l10n-zh-plugin-9.7.jar (no proxy)
            at org.sonar.core.util.DefaultHttpDownloader.failToDownload(DefaultHttpDownloader.java:157)
            at org.sonar.core.util.DefaultHttpDownloader.download(DefaultHttpDownloader.java:152)
            at org.sonar.server.plugins.PluginDownloader.downloadRelease(PluginDownloader.java:145)
            at org.sonar.server.plugins.PluginDownloader.download(PluginDownloader.java:118)
            ... 131 common frames omitted
    Caused by: java.net.SocketTimeoutException: connect timed out
            at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
            at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:412)
            at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:255)
            at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:237)
            at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
            at java.base/java.net.Socket.connect(Socket.java:609)
            at java.base/sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:305)
            at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177)
            at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:474)
            at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:569)
            at java.base/sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:266)
            at java.base/sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:373)
            at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:203)
            at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1187)
            at java.base/sun.net.www.protocol.http.HttpURLConnection$6.run(HttpURLConnection.java:1071)
            at java.base/sun.net.www.protocol.http.HttpURLConnection$6.run(HttpURLConnection.java:1069)
            at java.base/java.security.AccessController.doPrivileged(Native Method)
            at java.base/java.security.AccessController.doPrivilegedWithCombiner(AccessController.java:795)
            at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1068)
            at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:189)
            at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:168)
            at org.sonar.core.util.DefaultHttpDownloader$BaseHttpDownloader$HttpInputSupplier.getInput(DefaultHttpDownloader.java:274)
            at org.sonar.core.util.DefaultHttpDownloader.download(DefaultHttpDownloader.java:149)
            ... 133 common frames omitted
    
    

    安装完成重启

    也可以通过docker 重启容器

    docker restart sonarqube
    

    其他插件也可在应用市场安装。

  2. 离线安装
    下载好的插件直接扔到 sonarqube安装目录下的 extensions/plugins
    重启sonarqube即可。

    如果是先查找对应sonarqube_extensions 卷路径,插件路径默认在
    /var/lib/docker/volumes/sonarqube_sonarqube_extensions/_data/plugins

4.2 导入规则集

目前国内ali-p3c-pmd规则集比较流行,适用于没有自己规则的中小企业。

  1. 下载ali-p3c-pmd插件 源码
    https://github.com/caowenliang/sonar-pmd-p3c

  2. 打包部署插件

    mvn clean install -Dmaven.test.skip=true
    

    将{仓库目录}\sonar-pmd-plugin\target\ 下生成的sonar-pmd-plugin-3.2.1.jar
    copy到 sonarqube的{安装目录}/extensions/plugins下.。

    如果是docker数据卷安装可以直接扔到对应数据卷目录下。

    /var/lib/docker/volumes/sonarqube_sonarqube_extensions/_data/plugins
    

    重启sonarqube容器

    docker restart sonarqube
    
  3. 重启完成可以在插件市场看到已安装的规则集插件

  4. 进入代码规则页面,过滤器输入p3c,在仓库下可以看到PMD规则一共56条,为ali-p3c-pmd全部规则

4.3 配置规则集

创建质量配置

输入名称和代码语言

为新创建的配置添加规则

进入到规则页面,左侧过滤器显示当前选择的规则集,有系统自带e.g.:xxxx(内置)的规则集,和我们导入的外部规则集(ali-p3c-pmd)。

这里选择我们导入的规则集PMD(过滤器输入p3c >>选择仓库下的PMD(56条))

弹出框激活—选择我们的质量配置all-check----应用

ali-p3c-pmd一共56条规则,排除了PMD默认规则(因为没有中文描述)

4.3 安装多分支检查插件

  1. 下载插件

    https://github.com/mc1arke/sonarqube-community-branch-plugin/releases

  2. 离线 安装到插件目录 extensions/plugins/

  3. 修改config
    进入docker容器内部

    docker exec -it sonarqube bash
    

    修改sonar.properties

    vi conf/sonar.properties
    

    在文件末尾加入两行:

    sonar.web.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-${version}.jar=web
    
    sonar.ce.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin-${version}.jar=ce
    

    ${version}:你所下载的jar包的版本,本文因SonarQube版本为9.7所以是填入1.12.0

    保存文件,重启sonarqueb。

    9.7社区版安装1.12.0有问题:
    10:40:30.202 [main] INFO com.github.mc1arke.sonarqube.plugin.CommunityBranchAgent - Loading agent
    Exception in thread “main” java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513)
    at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:525)
    Caused by: java.lang.ClassNotFoundException: org.sonar.server.almsettings.MultipleAlmFeatureProvider
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:315)
    at com.github.mc1arke.sonarqube.plugin.CommunityBranchAgent.redefineEdition(CommunityBranchAgent.java:93)
    at com.github.mc1arke.sonarqube.plugin.CommunityBranchAgent.premain(CommunityBranchAgent.java:56)
    … 6 more
    *** java.lang.instrument ASSERTION FAILED ***: “result” with message agent load/premain call failed at src/java.instrument/share/native/libinstrument/JPLISAgent.c line: 422
    FATAL ERROR in native method: processing of -javaagent failed, processJavaStart failed

    安装9.6社区版,或者等待插件升级

  4. 登录sonarqube,忽略风险提示

    修改maven 构建命令,添加:

    -Dsonar.branch.name=${CI_COMMIT_BRANCH}
    

    选择对应分支扫描后就能看到

三、SonarQube使用步骤

1. 创建项目

  1. 创建项目

    输入项目名称

    选择本地构建

    创建令牌,默认即可


    测试项目为java maven项目,选择maven

    复制maven执行命令

2. 启动本地扫描

  1. 进入IDEA maven构建菜单,copy命令回车执行(注意删除输入栏的mvn)

  2. 等待执行完成,登录sonarqube项目页面查看结果。

    点击问题个数进入详情

    p3c规则都属于异味,sonar 扫描代码后,在类型 “异味” 可以找到,分“阻断”、“严重”、“主要” 3个严重等级

  3. 注意事项

    org/sonar/batch/bootstrapper/EnvironmentInformation has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
    

    当前项目为JDK1.8,sonarqube 9.7 在jdk11上运行,所以本地编译会报错;
    解决方法:
    修改本地JDK环境为jdk11
    安装低版本sonarqube7.8(最后一个支持jdk1.8的版本)—不推荐
    我们目标是流水线构建,本地构建可以采用idea插件,这里暂不介绍。

四、GitLab流水线集成Sonarqube扫描

1. sonarqube创建关联gitlab仓库的项目

  1. 在sonarqube配置-ALM集成-gitlab 创建配置

    配置名称:gitlab
    GitLab网址: http://localhost/api/v4
    个人访问令牌: 在gitlab中设置

    Gitlab个人访问令牌创建:

    复制令牌

    配置成功

  2. 新建项目=>更多=>来自gitlab

    输入需要访问项目的仓库管理员用户令牌,需要read_api权限

    保存后可以看到该用户名下的gitlab仓库

    点击设置,配置对应仓库的代码检查项目

2. 配置gitlab流水线作业

2.1 选择设置好的项目,使用Gitlab CI来分析项目




2.2 配置gitlab-runner

  1. docker创建gitlab-runner的步骤

    docker run -d --name gitlab-runner --restart always \
        -v /var/run/docker.sock:/var/run/docker.sock \
        -v gitlab-runner-config:/etc/gitlab-runner \
        gitlab/gitlab-runner:latest
    
  2. gitlab注册runner

    点击注册一个实例runner=>显示runner安装与注册说明

    复制 注册runner的命令
    进入刚才启动好的runner 容器

    docker exec -it gitlab-runner bash
    

    执行命令,一路回车确定

    gitlab-runner register --url http://localhost/ --registration-token D8pXEqBH6nRrRsNCRSk_
    
  3. 配置gitlab-runner config.toml

    [[runners]]
    name = “”
    url = “http://localhost/”
    token = “D8pXEqBH6nRrRsNCRSk_”
    executor = “docker”
    [runners.custom_build_dir]
    [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]

    注意修改 executor = “docker”

2.3 maven镜像私有化配置

  1. gitlab添加变量引入自定义settings.xml

    键:MVN_SETTINGS_XML
    值:settings.xml文件内容复制进来
    类型:文件
    # MVN_SETTINGS_XML变量在GitLab的设置=>CI/CD=>变量中定义,值就是整个settings.xml的内容(文本),变量类型设置成了"文件"

  2. maven构建脚本引入变量

    -s ${MVN_SETTINGS_XML}

      script: 
        - mvn -s ${MVN_SETTINGS_XML}
    
  3. docker镜像配置本地仓库缓存
    配置gitlab-ci缓存

      script: 
        - mvn -s ${MVN_SETTINGS_XML} -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository 
      cache:
        key: "${CI_JOB_NAME}"
        paths:
          - .m2/repository
    

2.4 启动流水线

完整gitalab-ci.yml配置

sonarqube-check:
  stage: build  
  image: maven:3.6.3-jdk-11
  tags:
    - docker-runner  
  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
  script: 
    - mvn -s ${MVN_SETTINGS_XML} -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository verify sonar:sonar -Dsonar.projectKey=test
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
      - .m2/repository
  allow_failure: true
  only:
    - master # or the name of your main branch

运行流水线完毕到sonarqube查看检测报告

注意事项
当前配置仅支持master分支检测,需要切分支,需要装插件
stage 和 tags值按照自己项目配置定义
目前缓存还不是很完美,每次需要从gitlab应用所在环境copy缓存到执行机,存在一些延迟。
如果放到runner执行机本地更好,但不灵活。

效率分析:

TODO 总用时59s
Analysis total time: 9.997
Total time: 29.079 s
pull images 59-29~= 20s
docker镜像下载还有必要优化

2.5 流水线门禁设置

我们期望当代码质量不满足sonarqube设置的门禁时,流水线运行阻塞,不进行后续作业。

  1. 创建sonarqube质量阀

  2. 如果没有创建,sonarque有默认值,如果需要自定义门禁值,创建规则后按需添加条件

  3. gitlab流水线如果需要sonarqube检查成功后才可执行下阶段作业,需要修改gitlab-ci.yml

    allow_failure: false    
    

  4. 当流水线未通过代码检查时

    点击sonarqueb-check job查看详情

    点击链接跳转sonarqube查看代码质量详情

五、代码质量

异味

ali-p3c-pmd问题都归属异味

点击数量进入

严重程度细分为: 阻断=>严重=>主要=>次要=>提示
其中阻断、严重需要解决

覆盖率

单元测试覆盖率,行覆盖率

pom.xml引入jacoco插件,注意版本不行就换

	<build>
		<plugins>
			<plugin>
				<groupId>org.jacoco</groupId>
				<artifactId>jacoco-maven-plugin</artifactId>
				<version>0.8.8</version>
				<executions>
					<execution>
						<goals>
							<goal>prepare-agent</goal>
							<goal>report</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

重复率

新增代码质量

本次推送或者MR的新增部分代码质量

全部代码质量

仓库全部代码当前质量

质量阀

质量阀俗称门禁值,根据项目要求设置各类指标,如不了解,用默认值。
超过门禁值,检测失败


总结

本文介绍了sonarqube的docker安装及使用,结合gitlab-ci完成自动化代码质量检测。

有关代码质量检测-SonarQube的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  4. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  5. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  6. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  7. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  8. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  9. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  10. git使用常见问题(提交代码,合并冲突) - 2

    文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g

随机推荐