介紹
SonarQube是個(gè)開(kāi)源的代碼質(zhì)量管理平臺(tái),可以通過(guò)多種工具和維度對(duì)代碼質(zhì)量進(jìn)行分析,部分功能還支持可視化,對(duì)于代碼缺陷一目了然。SonarQube比較開(kāi)放,支持通過(guò)插件擴(kuò)展功能,市面上常用的語(yǔ)言基本可以支持。同時(shí),我們也可以嘗試將sonar跟常用的jekins或者gocd集成在一起,在持續(xù)構(gòu)建的同時(shí)對(duì)代碼質(zhì)量進(jìn)行監(jiān)測(cè)。該工具功能強(qiáng)大,使用簡(jiǎn)便,實(shí)乃程序員保證代碼健康的必備良藥。
環(huán)境搭建部署
環(huán)境搭建包含兩部分,數(shù)據(jù)庫(kù)后臺(tái)和sonarqube本身,sonarqube本身內(nèi)嵌了H2用來(lái)存儲(chǔ)分析數(shù)據(jù),但是應(yīng)用到生產(chǎn)環(huán)境并不合適,針對(duì)我們的情況還是單獨(dú)部署數(shù)據(jù)庫(kù)為好。
數(shù)據(jù)庫(kù)配置
官方支持對(duì)接四種數(shù)據(jù)庫(kù),Microsoft SQL Server、Oracle、PostgreSQL、MySQL(不推薦),我使用的是postgresql數(shù)據(jù)庫(kù)。其他幾種數(shù)據(jù)庫(kù)具體安裝注意事項(xiàng)大家可以看官方文檔:Installing the Database
為了與其他應(yīng)用隔離開(kāi),針對(duì)SonarQube單獨(dú)建立相應(yīng)的數(shù)據(jù)庫(kù)、用戶(hù)和schema,在Mac OS以及Linux上,還需要建立相應(yīng)的系統(tǒng)用戶(hù)sonarqube。數(shù)據(jù)庫(kù)建用戶(hù)、建庫(kù)等操作請(qǐng)自行g(shù)oogle,另外,別忘記賦予數(shù)據(jù)庫(kù)用戶(hù)相應(yīng)的建表等權(quán)限。
schema名稱(chēng)為sonar,用戶(hù)名稱(chēng)為sonarqube:
sonar=# \dn
List of schemas
Name | Owner
--------+-----------
public | postgres
sonar | sonarqube
數(shù)據(jù)庫(kù)名稱(chēng)也命名為sonar:
sonar=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+-----------+----------+---------+-------+-------------------------
open_lmis | postgres | UTF8 | C | C |
postgres | postgres | UTF8 | C | C |
sonar | sonarqube | UTF8 | C | C | =Tc/sonarqube +
| | | | | sonarqube=CTc/sonarqube
template0 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
這塊需要注意的是,在postgresql中,新建用戶(hù)sonarqube默認(rèn)的schema還是指向public,而我們要使用新建的sonar,需要在數(shù)據(jù)庫(kù)中執(zhí)行以下命令將sonarqube的默認(rèn)schema改為sonar:
ALTER USER sonarqube SET search_path to sonar
SonarQube部署
sonarqube的部署非常簡(jiǎn)單,我下載的版本是7.3。下載后解壓,然后執(zhí)行bin目錄中相應(yīng)操作系統(tǒng)的啟動(dòng)即可,不過(guò)在這之前需要對(duì)數(shù)據(jù)庫(kù)信息進(jìn)行配置。配置方法是進(jìn)入到解壓后的文件夾的conf目錄中,修改sonar.properties文件,文件中有很多配置項(xiàng),不過(guò)都是被注釋掉的。這個(gè)配置文件里面的說(shuō)明非常詳細(xì),包括需要適配數(shù)據(jù)庫(kù)的哪些版本等等。里面提示Postgresql需要在9.3版本及以上,我機(jī)器裝的是9.4,剛好可以用,大家在安裝相應(yīng)版本的時(shí)也請(qǐng)仔細(xì)閱讀這個(gè)配置里面的說(shuō)明信息,防止因?yàn)榘姹締?wèn)題出錯(cuò)。同時(shí),也包含其他配置信息,暫時(shí)沒(méi)有時(shí)間研究,我們先盡快把sonarqube跑起來(lái)再說(shuō)。
(2.0-moz)? % j sonar ~/IdeaProjects/open-lmis
/Users/zbao/Tool/sonarqube-7.3
% ll ~/Tool/sonarqube-7.3
total 16
-rw-r--r--@ 1 zbao staff 7.5K Aug 10 17:31 COPYING
drwxr-xr-x@ 8 zbao staff 256B Aug 10 17:31 bin
drwxr-xr-x@ 4 zbao staff 128B Sep 3 22:45 conf
drwxr-xr-x@ 6 zbao staff 192B Sep 3 22:46 data
drwxr-xr-x@ 11 zbao staff 352B Aug 10 17:31 elasticsearch
drwxr-xr-x@ 5 zbao staff 160B Sep 3 17:52 extensions
drwxr-xr-x@ 7 zbao staff 224B Aug 10 17:40 lib
drwxr-xr-x@ 16 zbao staff 512B Sep 5 09:27 logs
drwxr-xr-x@ 8 zbao staff 256B Sep 5 21:19 temp
drwxr-xr-x@ 24 zbao staff 768B Aug 10 17:40 web
% cd conf ~/Tool/sonarqube-7.3
% ll ~/Tool/sonarqube-7.3/conf
total 48
-rw-r--r--@ 1 zbao staff 20K Sep 3 22:45 sonar.properties
-rw-r--r--@ 1 zbao staff 3.1K Aug 10 17:31 wrapper.conf
這個(gè)配置現(xiàn)在只需要修改三個(gè)配置即可,數(shù)據(jù)庫(kù)用戶(hù)、數(shù)據(jù)庫(kù)密碼和jdbc的url:
# User credentials.
# Permissions to create tables, indices and triggers must be granted to JDBC user.
# The schema must be created first.
sonar.jdbc.username=sonarqube
sonar.jdbc.password=password
#----- PostgreSQL 9.3 or greater
# If you don't use the schema named "public", please refer to http://jira.sonarsource.com/browse/SONAR-5000
sonar.jdbc.url=jdbc:postgresql://localhost/sonar
sonarqube的web服務(wù)默認(rèn)是全零監(jiān)聽(tīng)在9000端口上,如果想修改,可以修改這兩個(gè)配置,我用的是默認(rèn):
# Binding IP address. For servers with more than one IP address, this property specifies which
# address will be used for listening on the specified ports.
# By default, ports will be used on all IP addresses associated with the server.
sonar.web.host=0.0.0.0
# Web context. When set, it must start with forward slash (for example /sonarqube).
# The default value is root context (empty value).
#sonar.web.context=
# TCP port for incoming HTTP connections. Default value is 9000.
sonar.web.port=9000
修改完成后,進(jìn)入bin目錄,根據(jù)操作系統(tǒng)選擇,我用的是Mac OS,直接使用下面命令啟動(dòng):
% cd macosx-universal-64 ~/Tool/sonarqube-7.3/bin
% ll ~/Tool/sonarqube-7.3/bin/macosx-universal-64
total 288
-rw-r--r-- 1 zbao staff 5B Sep 5 21:19 SonarQube.pid
drwxr-xr-x@ 3 zbao staff 96B Aug 10 17:31 lib
-rwxr-xr-x@ 1 zbao staff 15K Aug 10 17:31 sonar.sh
-rwxr-xr-x@ 1 zbao staff 123K Aug 10 17:31 wrapper
% sh sonar.sh start ~/Tool/sonarqube-7.3/bin/macosx-universal-64
Starting SonarQube...
Started SonarQube.
啟動(dòng)后,查詢(xún)sonar數(shù)據(jù)庫(kù)里面新建了不少表,說(shuō)明sonarqube已經(jīng)在啟動(dòng)時(shí)將必須的數(shù)據(jù)刷進(jìn)去了,下面是其中的部分表:
sonar=> \d
List of relations
Schema | Name | Type | Owner
--------+---------------------------------+----------+-----------
sonar | active_rule_parameters | table | sonarqube
sonar | active_rule_parameters_id_seq | sequence | sonarqube
sonar | active_rules | table | sonarqube
sonar | active_rules_id_seq | sequence | sonarqube
sonar | alm_app_installs | table | sonarqube
sonar | analysis_properties | table | sonarqube
sonar | ce_activity | table | sonarqube
sonar | ce_activity_id_seq | sequence | sonarqube
sonar | ce_queue | table | sonarqube
sonar | ce_queue_id_seq | sequence | sonarqube
sonar | ce_scanner_context | table | sonarqube
sonar | ce_task_characteristics | table | sonarqube
sonar | ce_task_input | table | sonarqube
sonar | default_qprofiles | table | sonarqube
sonar | deprecated_rule_keys | table | sonarqube
sonar | duplications_index | table | sonarqube
sonar | duplications_index_id_seq | sequence | sonarqube
sonar | es_queue | table | sonarqube
數(shù)據(jù)庫(kù)表中有張user表,是用來(lái)存儲(chǔ)用戶(hù)信息的,默認(rèn)用戶(hù)名和密碼均為admin和admin,第一次登陸系統(tǒng)需要輸入這個(gè)密碼:
sonar=> select * from users;
id | login | name | email | crypted_password | salt | active | created_at | updated_at | scm_accounts | external_login | external_identity_provider | user_local | is_root | onboarded | homepage_type | homepage_parameter | hash_method | uuid | external_id | organization_uuid
----+-------+---------------+-------+--------------------------------------------------------------+------+--------+---------------+---------------+--------------+----------------+----------------------------+------------+---------+-----------+---------------+--------------------+-------------+-------+-------------+-------------------
1 | admin | Administrator | | $2a$12$LJLoKEDVZgBCO6OahphMYeYS6lqE7p0J/Fm6EdKdsqxMgpXGNpmhq | | t | 1535987042646 | 1536023846785 | | admin | sonarqube | t | f | t | | | BCRYPT | admin | admin |
(1 row)
啟動(dòng)后稍等片刻,打開(kāi)瀏覽器輸入http://localhost:9000/,點(diǎn)擊Log in,用戶(hù)名admin,密碼admin,便可以登陸進(jìn)去了。至此,sonarqube環(huán)境搭建完成。

SonarJava插件
SonarQube支持了市面上大部分語(yǔ)言的分析,具體方式是通過(guò)Sonar插件來(lái)實(shí)現(xiàn)的,這樣很方便插件開(kāi)發(fā)人員。

根據(jù)官方介紹,使用了最先進(jìn)的技術(shù)(模式匹配、數(shù)據(jù)流分析)對(duì)代碼質(zhì)量進(jìn)行管理,包括代碼壞味道檢查(不了解的可以參考重構(gòu)一書(shū))、findbugs以及代碼安全檢查。自稱(chēng)檢查的深度、精度、速度都很不錯(cuò)。
SonarSource delivers what is probably the best static code analyzer you can find on the market for Java. Based on our own Java compiler front-end, it uses the most advanced techniques (pattern matching, dataflow analysis) to analyze code and find code smells, bugs and security vulnerabilities. As for any product we develop at SonarSource, it was built on the following principles: depth, accuracy and speed.
優(yōu)點(diǎn):
支持400+的規(guī)則,規(guī)則數(shù)還比較多
支持規(guī)則自定義,用戶(hù)實(shí)現(xiàn)相關(guān)接口便可以注入自己的檢查規(guī)則
支持與Maven、Ant、Gradle的集成,快速簡(jiǎn)便
支持Jacoco和Cobertura覆蓋率報(bào)告導(dǎo)入
支持導(dǎo)入用戶(hù)的findbugs/pmd/checkstyle報(bào)告
Gradle集成
由于項(xiàng)目是Gradle構(gòu)建,僅介紹與Gradle構(gòu)建相關(guān)的集成,具體其他方式可以參考Analyzing Source Code。
集成過(guò)程在Gradle中很簡(jiǎn)單,引入sonar-runner和org.sonarqube插件,插件默認(rèn)會(huì)導(dǎo)入sonarqube的task。
apply plugin: 'java'
apply plugin: 'sonar-runner'
apply plugin: 'org.sonarqube'
buildscript {
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2"
}
sonarqube {
properties {
property "sonar.sourceEncoding", "UTF-8"
}
}
關(guān)于Gradle更詳細(xì)的配置,可以參考:SonarQube與Gradle集成
在工程目錄中執(zhí)行如下命令,便開(kāi)始靜態(tài)檢查過(guò)程,檢查完成后會(huì)將結(jié)果存入數(shù)據(jù)庫(kù),并在前臺(tái)界面顯示。
./gradlew clean sonarqube --stacktrace
分析結(jié)果:

代碼覆蓋率根據(jù)不同的包進(jìn)行圖形化展示:

單元測(cè)試覆蓋率報(bào)告集成
Jacoco和Cobertura都是用來(lái)統(tǒng)計(jì)java單元測(cè)試覆蓋率的工具,SonarQube本身不具有統(tǒng)計(jì)覆蓋率的功能,它支持導(dǎo)入Jacoco和Cobertura的覆蓋率結(jié)果,然后進(jìn)行界面呈現(xiàn)。
該兩種覆蓋率分析工具都可以通過(guò)在Gradle中集成相關(guān)插件實(shí)現(xiàn)。
JaCoCo插件
Cobertura插件
這塊需要注意的地方,是需要在gradle腳本中的sonarqube任務(wù)中設(shè)置報(bào)告的路徑或者在sonar的界面配置中配置相關(guān)報(bào)告路徑,因?yàn)閷?shí)際上sonar只負(fù)責(zé)展示報(bào)告結(jié)果,分析報(bào)告還是由插件生成的。
sonar支持導(dǎo)入分析結(jié)果報(bào)告
sonar也支持導(dǎo)入自己工程跑出的checkstyle,findbugs等的結(jié)果報(bào)告,同樣,需要在gradle腳本中設(shè)置相關(guān)結(jié)果的報(bào)告路徑或者在界面配置中設(shè)置,具體配置大家自行g(shù)oogle就可以找到,不再贅述。
使用方法
Gradle的使用方法很簡(jiǎn)單,執(zhí)行執(zhí)行以下命令,執(zhí)行完成后,分析結(jié)果會(huì)自動(dòng)導(dǎo)入到數(shù)據(jù)庫(kù)中,并展示在web界面上。
./gradlew clean sonarqube --stacktrace
Q&A
問(wèn)題1
Caused by: The folder 'src/test' does not exist for ':modules:migration' (base directory = /Users/zbao/IdeaProjects/open-lmis/modules/migration)
解決辦法:在migration的src目錄下面創(chuàng)建test目錄
參考:(http://sonarqube-archive.15.x6.nabble.com/Build-fails-if-src-main-does-not-exists-td5027877.html)
問(wèn)題2
Caused by: File [...] can't be indexed twice.
解決辦法:在root project的build.gradle中添加如下配置
sonarqube {
properties {
property "sonar.sources", "src/main"
property "sonar.tests", "src/test"
}
}
參考:(https://github.com/SonarOpenCommunity/sonar-cxx/wiki/FAQ)
問(wèn)題3
Cobertura report not found at /Users/zbao/IdeaProjects/open-lmis/modules/reporting/target/site/cobertura/coverage.xml
將sonar.cobertura.reportPath設(shè)置成報(bào)告路徑,如下:
property "sonar.cobertura.reportPath", "build/report/cobertura/coverage.xml"
也可以通過(guò)SonarQube界面進(jìn)行修改:

參考:(https://github.com/galexandre/sonar-cobertura)(https://stackoverflow.com/questions/24134595/sonar-cobertura-plugin-not-finding-coverage-xml-file)
問(wèn)題4
如何掃描js代碼
參考問(wèn)題2配置sonar.sources屬性,其中包含js代碼即可,同時(shí)保證SonarJS插件已添加(默認(rèn)已經(jīng)安裝)。
問(wèn)題5
JS公共庫(kù)被掃描導(dǎo)致結(jié)果不準(zhǔn)
添加sonar.javascript.exclusions,從參數(shù)可以看出js是需要單獨(dú)指定參數(shù)才能exclude掉不需要分析公共庫(kù)。
property "sonar.javascript.exclusions", "src/main/webapp/public/lib/*,src/main/webapp/public/api-docs/*,src/main/webapp/public/device/*"
也可以通過(guò)界面配置sonar.javascript.exclusions:

處理這個(gè)問(wèn)題遇到了比較大的坑,因?yàn)槌薺s代碼外,其他的代碼都使用sonar.exclusions這個(gè)屬性還進(jìn)行剔除,之前并不知道存在sonar.javascript.exclusions這個(gè)屬性,導(dǎo)致在這里搞了好一會(huì)才解決。sonar.exclusions也支持從界面配置:

(https://docs.sonarqube.org/display/PLUG/SonarJS)
問(wèn)題6
Gradle 2.4集成checkstyle插件報(bào)找不到checkstyle()方法
Gradle低版本bug,將gradle或者gradlew升級(jí)到2.7解決
參考:(https://docs.sonarqube.org/display/SONAR/Narrowing+the+Focus)(https://stackoverflow.com/questions/21323276/sonarqube-exclude-a-directory)
問(wèn)題7
Jacoco單元測(cè)試覆蓋率報(bào)告不準(zhǔn)確
需要合并Jacoco單元測(cè)試結(jié)果,此處問(wèn)題暫未解決,解決后進(jìn)行更新
問(wèn)題8
Exception in thread "main" java.lang.ClassNotFoundException: org.jacoco.agent.rt.internal_932a715.PreMain
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
FATAL ERROR in native method: processing of -javaagent failed
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:304)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
解決辦法,加入 tasks.withType(Test) {
systemProperties['user.dir'] = workingDir
}:
tasks.withType(Test) {
systemProperties['user.dir'] = workingDir
}
參考:(https://discuss.gradle.org/t/jacoco-multiproject-build-cant-load-jacocoagent/11540)