简介:SonarQube 7.4 是面向 Java、Python、C#、JavaScript 等多语言项目的开源代码质量管理平台,适合开发团队、测试人员及需要落地静态扫描与代码异味治理的工程人员使用。压缩包约161.38MB,页面记录的文件总数为0,类型明细暂无数据,包内清单暂无法核验;获取后可按目录结构自行查看配置、插件扩展、运行脚本与 Web 前端资源等组成部分。页面显示已有643人学习,说明该版本在社区中仍有一定检索与复用需求。其价值在于提供一套可本地部署的完整发行包,便于读者搭建代码质量分析环境,围绕漏洞、代码异味与复杂度指标开展扫描,并可与持续集成流程衔接,用于提交前的质量门禁和团队代码规范检查,也可作为旧版本行为对照与插件适配研究的样本,对需要复现旧版扫描结果的读者较为方便。
1. 还有人抱着 sonarqube-7.4 不放:一次代码质量门禁的落地现场
前阵子帮一个团队收拾 CI 的烂摊子,Jenkins 上一排 job 常年飘红没人管,代码合并全靠肉眼 review。他们想上 sonarqube,可服务器是台老机器,JDK 只有 8,运维一句「不动系统」把升级路堵死,最后版本锁在 sonarqube-7.4。不少人第一反应是「这版本太老了」,实际跑下来,sonar 在 7.4 上该有的都有:二十多种语言的扫描、质量门禁、Issue 跟踪、Web API,还完整支持 MySQL,对资源的要求比后几个大版本客气得多。这类资源适合两种人:被老环境绑住、又必须把代码质量门禁立起来的一线开发;以及想先在自己机器上把 sonar 全流程摸清、再决定要不要升级的运维。部署、扫描器接入、门禁配置和几个血泪坑,一条条拆开说。
2. 部署 sonarqube-7.4:从 MySQL 建库到 9000 端口跑通
2.1 版本对齐:JDK 8、MySQL 5.7 与内核参数
sonarqube-7.4 是 2018 年前后的版本,依赖关系比新版本苛刻得多,硬上高版本 JDK 会直接在启动阶段崩掉。常见做法有这么几条:JDK 锁死在 1.8(7.4 不认 JDK 11,UnsupportedClassVersionError是标配报错);数据库优先 MySQL 5.7,5.6 也能跑,但 8.0 不行,主要是驱动和认证插件的兼容问题;操作系统层面必须放开vm.max_map_count,因为内置的 Elasticsearch 5.x 会去申请大量虚拟内存映射区,默认的 65530 根本不够。
先做系统层准备,这几步不做,后面es.log会一路报max virtual memory areas vm.max_map_count [65530] is too low然后进程自杀:
# 关键内核参数,写入后重启也生效 echo "vm.max_map_count=262144" >> /etc/sysctl.conf sysctl -p # 文件句柄数,长连接多的时候不够用 echo "sonar soft nofile 65536" >> /etc/security/limits.conf echo "sonar hard nofile 65536" >> /etc/security/limits.conf # SonarQube 不允许 root 直接启动,必须建普通用户 useradd -r -m -s /bin/bash sonar前两条改的是内核和 shell 限制,第三条是硬性约束——用 root 执行sonar.sh start会被脚本直接拒绝,报「SonarQube can not be run as root」。我一般习惯在装包之前就把用户建好,tar解压后直接chown -R sonar:sonar /opt/sonarqube-7.4,省得后面权限问题排查半天。
2.2 建库与账号:字符集和隔离级别一次配到位
库建得不对,后面启动日志里会出现一堆看不懂的 SQL 异常。sonarqube-7.4 对 MySQL 有明确要求:字符集必须是utf8(不是utf8mb4,这一点和多数人直觉相反)、事务隔离级别必须是READ-COMMITTED。默认的REPEATABLE-READ在并发扫描时容易触发死锁,表现是扫描任务跑到一半卡住,web.log里刷Deadlock found when trying to get lock。
-- 建库,字符集按 7.4 的要求来 CREATE DATABASE sonar CHARACTER SET utf8 COLLATE utf8_general_ci; -- 专用账号,不要图省事用 root CREATE USER 'sonar'@'%' IDENTIFIED BY 'Sonar_7_4_pwd'; GRANT ALL PRIVILEGES ON sonar.* TO 'sonar'@'%'; FLUSH PRIVILEGES;my.cnf里同步补上这几项,改完重启 MySQL 生效:
[mysqld] # 缓冲池,按机器内存给,2G 以内都算保守 innodb_buffer_pool_size = 1G # 大项目扫描时单条 SQL 可能很长,32M 起步 max_allowed_packet = 32M # 5.7 默认还开着查询缓存,对 sonar 是负优化 query_cache_size = 0 query_cache_type = 0 # 必须改成 READ-COMMITTED,否则并发分析容易死锁 transaction_isolation = READ-COMMITTEDinnodb_buffer_pool_size是最值得调的一项:这个库小项目几百兆,大项目能涨到几个 G,缓冲池给太小,扫描任务表读写会明显拖慢。query_cache_size置 0 是有意为之,sonar 的查询模式以短平快为主,缓存命中率极低,反而增加锁竞争。transaction_isolation这一条是必须项,配置漏了大概率在第一次并发扫描时踩坑。
2.3 sonar.properties:把 web 与 ce 的内存分开算
conf/sonar.properties是唯一需要改的配置文件,7.4 的默认值只适合演示环境。这里有个容易忽略的点:SonarQube 内部其实是三块进程——web 负责页面和 API、ce(Compute Engine)负责消费分析报告、search 就是那个内嵌的 Elasticsearch,三者的 JVM 参数是分开配的,不能指望sonar.web.javaOpts一个参数包打天下。
# ---- 数据库 ---- sonar.jdbc.username=sonar sonar.jdbc.password=Sonar_7_4_pwd sonar.jdbc.url=jdbc:mysql://127.0.0.1:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false # ---- Web 服务 ---- sonar.web.host=0.0.0.0 sonar.web.port=9000 sonar.web.context=/sonar # 堆给足,页面和 API 都在这里 sonar.web.javaOpts=-Xms1g -Xmx2g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/sonarqube-7.4/logs # ---- Compute Engine,分析报告在这里落库 ---- sonar.ce.javaOpts=-Xms512m -Xmx2g -XX:+HeapDumpOnOutOfMemoryError # ---- 内嵌 Elasticsearch ---- sonar.search.javaOpts=-Xms1g -Xmx1g -XX:+UseG1GC # 强制外网访问的绝对地址,配了反向代理时尤其要写对 sonar.core.serverBaseURL=http://sonar.internal:9000/sonaruseConfigs=maxPerformance是 MySQL 驱动自带的性能配置集,配合rewriteBatchedStatements=true,能让批量插入快一个量级,扫描大项目时差别很明显。sonar.core.serverBaseURL这一项很多人不写,结果邮件通知里的链接、Web API 返回的serverUrl全是默认值,接反向代理后全部失效。内存分配上,三条-Xmx加起来不能超过物理内存的七成,否则 Elasticsearch 的 mmap 和 JVM 堆抢内存,表现就是机器负载飙升、SSH 都卡。
2.4 启动、验证与插件的版本对齐
配置改完就可以启动,但别急着开浏览器,先盯日志把三个进程的启动顺序确认清楚:
# 切到 sonar 用户启动,root 会被拒绝 su - sonar -c "/opt/sonarqube-7.4/bin/linux-x86-64/sonar.sh start" # 三个日志按顺序看:es 先起,然后 web,最后 ce tail -f /opt/sonarqube-7.4/logs/es.log tail -f /opt/sonarqube-7.4/logs/web.log tail -f /opt/sonarqube-7.4/logs/sonar.log判断启动成功的标志是web.log里出现SonarQube is up,或者直接curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9000/sonar/api/system/status返回200。如果es.log里一直刷max_map_count,就是第 2.1 节的内核参数没生效,sysctl -p重跑一次;如果是web.log里的Connection refused或Access denied,回到 2.2 检查账号权限和数据库地址。
插件安装有一个铁律:版本必须与 7.4 精确对齐。中文语言包、SonarJava、SonarJS 这些插件在extensions/plugins下解压即用,但版本不匹配时,7.4 的表现不是弹个错误提示,而是整个 Web 界面白屏或者plugins目录被清空重建,日志里只有一句Plugin is not compatible with the current SonarQube version。
cd /opt/sonarqube-7.4/extensions/plugins # 中文包文件名形如 sonar-l10n-zh-plugin-x.y.jar,x.y 必须与 7.4 兼容 # SonarJava 同理,装之前先看插件页的兼容矩阵 ls -l /opt/sonarqube-7.4/extensions/plugins chown sonar:sonar *.jar su - sonar -c "/opt/sonarqube-7.4/bin/linux-x86-64/sonar.sh restart"插件装完一定要重启,并且重启后回来看web.log里有没有Plugin ... loaded的成功记录。我一般装插件前先把整个extensions/plugins目录打一个 tar 包,出问题直接回滚,比一个个删 jar 快得多。7.4 的插件生态现在能找到的版本有限,安装前把兼容信息确认清楚,别拿一个高版本 jar 硬塞。
3. 扫描器接入:Maven 项目和非 Maven 项目两条路
3.1 Maven 项目:一条命令跑通,但参数要写全
Maven 项目的接入成本最低,sonar-maven-plugin不需要写进pom.xml,直接在命令行调用即可。7.4 配套的插件版本在 Maven 中央仓库里能直接拉到,命令跑不通多半是参数漏了或者网络代理配错。
mvn clean verify sonar:sonar \ -Dsonar.host.url=http://127.0.0.1:9000/sonar \ -Dsonar.login=8f2c1e9b7a6d4f3e0c5b2a1d9e8f7c6b5a4d3e2f \ -Dsonar.projectKey=order-service \ -Dsonar.projectName=order-service \ -Dsonar.projectVersion=1.0.0 \ -Dsonar.java.binaries=target/classes \ -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml逐项说清用途:sonar.host.url要带上sonar.web.context配的上下文路径,少写这一段会报 404;sonar.login在 7.4 上填的是用户 Token,不是密码,Token 在「My Account → Security」里生成,用密码认证的写法在这个版本已经开始被弃用;sonar.projectKey是全局唯一标识,改名等于换了一个项目,历史数据全部丢失,定下来之后别动;sonar.java.binaries是 Java 分析必需的,指向target/classes,Maven 扫描器多数情况下能自动探测,但多模块项目里单独指定更稳妥。
执行顺序上,clean verify必须在sonar:sonar之前,因为分析器需要读到编译产物和测试报告。有人图省事只写mvn sonar:sonar,结果java.binaries为空,日志里刷Please provide compiled classes of your project,规则一条都不生效。
3.2 非 Maven 项目:sonar-project.properties 的必填项
老系统、Gradle 项目或者纯脚本类项目,走sonar-scanner更直接。在项目根目录放一个sonar-project.properties,字段不多但每一项都别省:
# 项目标识,改一次历史数据就断一次 sonar.projectKey=legacy-batch sonar.projectName=legacy-batch sonar.projectVersion=1.0.0 # 源码与测试目录,相对路径 sonar.sources=src/main/java sonar.tests=src/test/java # 编译产物,Java 分析必需 sonar.java.binaries=target/classes sonar.java.test.binaries=target/test-classes sonar.java.libraries=lib/*.jar # 编码,中文注释多的项目不写这项会整片报错 sonar.sourceEncoding=UTF-8 # 排除目录,生成代码和 DTO 一律不扫 sonar.exclusions=src/main/java/**/generated/**,**/*DTO.java sonar.coverage.exclusions=**/config/**,**/*DTO.java # 覆盖率报告 sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xmlsonar.java.libraries这一项在非 Maven 项目里最关键:依赖 jar 不提供,分析器解析不了第三方类型,会大量误报Unresolved type相关的缺陷。写法上注意是相对路径加通配,多个目录用逗号分隔。sonar.tests和sonar.sources不能有重叠,重叠了会报File ... is indexed in both sources and tests,然后整个分析中断。
扫描命令本身很短,配合 Token 环境变量:
export SONAR_TOKEN=8f2c1e9b7a6d4f3e0c5b2a1d9e8f7c6b5a4d3e2f export SONAR_HOST_URL=http://127.0.0.1:9000/sonar /opt/sonar-scanner-3.2/bin/sonar-scanner \ -Dsonar.projectBaseDir=/data/projects/legacy-batch \ -X-X是 debug 模式,第一次接入建议加上,控制台会打印所有生效参数和文件索引过程,排查「为什么某目录没被扫到」时省时间。接入稳定后去掉-X,日志量小很多。
3.3 覆盖率接入:JaCoCo 的 XML 路径别写错
sonarqube-7.4 只认 JaCoCo 的 XML 报告,不认exec二进制文件。这是接入阶段最常见的翻车点:pom 里配了jacoco-maven-plugin,报告生成在target/jacoco.exec,扫描器读了半天发现是空文件,覆盖率显示-或者0.0%。
<!-- pom.xml 里 aggregator 模式,保证 XML 报告生成 --> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.2</version> <executions> <execution> <id>prepare-agent</id> <goals><goal>prepare-agent</goal></goals> </execution> <execution> <id>report</id> <phase>verify</phase> <goals><goal>report</goal></goals> </execution> </executions> </plugin>prepare-agent把 JaCoCo 挂到测试 JVM 上,report在verify阶段把exec转成html和xml。两者缺一不可,只配prepare-agent的话target/site/jacoco/jacoco.xml根本不会生成。多模块项目还有一个坑:默认每个子模块各生成一份 XML,而sonar.coverage.jacoco.xmlReportPaths只吃一个路径,正确的写法是用 aggregator 模块统一出报告,或者用逗号分隔把所有子模块路径列出来:
sonar.coverage.jacoco.xmlReportPaths=\ module-a/target/site/jacoco/jacoco.xml,\ module-b/target/site/jacoco/jacoco.xml,\ module-c/target/site/jacoco/jacoco.xml路径是相对项目根目录的,写绝对路径在部分版本的扫描器上会被忽略。覆盖率数据没进去,质量门禁上的Coverage on New Code条件就没法用,等于门禁聋了一半。
3.4 分支分析这件事,7.4 社区版做不了
很多人部署完第一件事就是问「怎么按分支扫描」。在 sonarqube-7.4 的社区版里,答案是不行——分支分析是商业版能力。命令行上跑-Dsonar.branch.name=feature/xxx不会报错,但服务端会忽略这个参数,所有分支的扫描结果全部覆盖到同一个项目上,历史趋势图会变成一条剧烈波动的锯齿线。
7.4 上可行的替代方案是「同项目多 projectKey」:把分支名拼进sonar.projectKey,例如order-service-feature-xxx,主分支保持order-service不变。代价是项目列表会膨胀,好处是每个分支有独立的门禁状态和历史数据。清理时按前缀批量删,用/api/projects/delete就行。如果团队确实需要分支级别的对比视图,那得评估升级到 7.9 之后的 LTS,这一点在选型阶段就该想清楚,别部署完再返工。
4. 质量门禁:让扫描结果真正卡住一次合并
4.1 Quality Gate 的条件组合,先宽后严
默认的「Sonar way」门禁对老项目极其不友好,新代码覆盖率和重复率的要求一上来就会让所有构建变红,团队跑两天就把门禁关了。我一般的做法是新建一个门禁,只卡「新代码」维度,阈值从宽开始:
| 条件 | 指标 key | 建议初始阈值 | 说明 |
|---|---|---|---|
| 新代码覆盖率 | new_coverage | ≥ 60% | 先立起来,季度往上抬 |
| 新代码重复率 | new_duplicated_lines_density | ≤ 5% | 复制粘贴重灾区 |
| 新增阻断级缺陷 | new_blocker_violations | = 0 | 这条不让步 |
| 新增严重级缺陷 | new_critical_violations | ≤ 5 | 留一点缓冲 |
| 新代码技术债比例 | new_technical_debt_ratio | ≤ 8% | 主观指标,别设太死 |
门禁的判定逻辑是「所有条件都满足才算通过」,任意一条超阈值整次分析就是 ERROR。只卡新增代码(New Code)是这套体系能活下去的关键,老代码的历史债用「技术债看板」慢慢还,不影响日常合并。
4.2 Quality Profile:继承内置规则再关掉噪音
7.4 上的规则配置叫 Quality Profile,内置的「Sonar way」已经把大部分规则按语言分好类,但直接拿来用噪音很大。正确的做法是「继承 + 局部覆盖」:新建一个 Profile,父级选 Sonar way,然后针对项目情况调整严重级别。
常见的调整方向有几类:日志打印相关的规则(System.out、printStackTrace)在内部工具项目里可以降到 Minor;MagicNumber规则对常量类聚集的项目误报率高,评估后可以关掉;空实现的方法、未使用的私有字段这类规则保持开启,投入产出比最高。规则 key 在 7.4 的 Web 界面和 API 返回里可能不一致,界面显示java:S106,实际配置时按 API 查出来的squid:S106写在 properties 里才生效,这一点在 4.3 节会再碰到。
Profile 是全局生效的,改一次影响所有关联项目,动阈值之前先在测试项目上验证一轮。
4.3 忽略指定 Issue:multicriteria 的写法
有些问题确认过是误报或者业务上必须保留,比如命令行工具里就要往System.out打提示。7.4 支持按规则 + 文件路径的正则组合忽略,写法是multicriteria系列参数:
# 声明几条忽略规则,编号随便起,逗号分隔 sonar.issue.ignore.multicriteria=e1,e2,e3 # 规则 1:命令行工具类里允许 System.out sonar.issue.ignore.multicriteria.e1.ruleKey=squid:S106 sonar.issue.ignore.multicriteria.e1.resourceKey=**/cli/*.java # 规则 2:DTO 目录下允许字段命名不规范 sonar.issue.ignore.multicriteria.e2.ruleKey=squid:S116 sonar.issue.ignore.multicriteria.e2.resourceKey=**/dto/**/*.java # 规则 3:测试代码里允许 magic number sonar.issue.ignore.multicriteria.e3.ruleKey=squid:S109 sonar.issue.ignore.multicriteria.e3.resourceKey=**/src/test/**/*.javaruleKey必须是规则的实际 key,resourceKey是 Ant 风格的通配路径,相对于projectBaseDir。三条中最容易写错的是resourceKey的前导**/,少写会导致匹配不上;路径分隔符统一用/,Windows 上跑扫描器也一样,写\不生效。忽略配置改了之后必须重新分析才生效,不用重启服务。
一套项目中multicriteria编号超过十个之后维护成本会急剧上升,我的经验是控制在五条以内,超出的部分通过调整 Profile 里的规则级别解决,别把忽略列表当垃圾桶。
4.4 CI 里怎么真的把构建拦下来
这里有个反直觉的事实:sonarqube-7.4 的扫描器退出码只反映「分析过程是否成功」,不反映「质量门禁是否通过」。门禁失败时扫描器仍然返回 0,CI 一片绿,代码照合。要做到真正拦截,得在扫描命令之后主动查一次 API。
#!/usr/bin/env bash set -euo pipefail SONAR_URL="http://127.0.0.1:9000/sonar" SONAR_TOKEN="8f2c1e9b7a6d4f3e0c5b2a1d9e8f7c6b5a4d3e2f" PROJECT_KEY="order-service" # 第一步:跑扫描 mvn clean verify sonar:sonar \ -Dsonar.host.url="$SONAR_URL" \ -Dsonar.login="$SONAR_TOKEN" \ -Dsonar.projectKey="$PROJECT_KEY" # 第二步:查门禁状态,注意 ce 是异步的,要留一点时间 sleep 10 STATUS=$(curl -s -u "$SONAR_TOKEN:" \ "$SONAR_URL/api/qualitygates/project_status?projectKey=$PROJECT_KEY" \ | python -c "import sys,json;print(json.load(sys.stdin)['projectStatus']['status'])") echo "Quality Gate: $STATUS" if [ "$STATUS" != "OK" ]; then echo "门禁未通过,构建终止" exit 1 fiproject_status接口返回三个值:OK、ERROR、NONE。NONE表示项目还没跑过分析或者门禁没配置,判断时按失败处理最安全。sleep 10是为了等 Compute Engine 消费完分析报告,大项目可能要更久,稳妥的做法是轮询api/ce/task拿到SUCCESS状态再查门禁,但多数项目固定延时够用。curl -u "$TOKEN:"里的冒号不能省,那是 HTTP Basic 认证的空密码占位符,写成-u $TOKEN会认证失败。
5. 排查清单:sonarqube-7.4 上最容易翻车的五个地方
5.1 启动即退出,es.log 里全是 max_map_count
现象:sonar.sh start显示启动成功,几秒后进程消失,es.log里刷max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]。
原因:7.4 内嵌的 Elasticsearch 5.x 需要大量内存映射区,宿主机默认值不够,容器环境下更常见——/etc/sysctl.conf改的是宿主机,容器内读的是另一套命名空间。
解决:宿主机执行sysctl -w vm.max_map_count=262144并写进/etc/sysctl.conf;容器部署时在docker run上加--sysctl vm.max_map_count=262144,或者编排文件里的sysctls段声明。改完必须重启容器,光改宿主机不重启不会生效。
5.2 数据库连接失败,报 Unknown character set 或认证错误
现象:web.log里java.sql.SQLException: Unknown character set 'utf8mb4',或者Unable to connect to database。
原因:两个独立问题。一是建库时用了utf8mb4,7.4 的 JDBC 配置里写死了characterEncoding=utf8,两边对不上;二是 MySQL 8.0 的默认认证插件是caching_sha2_password,7.4 带的驱动太老不认。
解决:建库统一用CHARACTER SET utf8 COLLATE utf8_general_ci;数据库版本严格控制在 5.6 / 5.7,非要用 8.0 就得手工降认证插件并把驱动 jar 换成mysql-connector-java-5.1.x塞进lib/jdbc-driver/mysql,不推荐,风险大于收益。
5.3 扫描跑完了,覆盖率一直是横杠
现象:扫描日志显示Coverage report not found,Web 界面覆盖率显示-,门禁里覆盖率条件永远无法满足。
原因:sonar.coverage.jacoco.xmlReportPaths指向的路径不存在,或者指向了jacoco.exec。多模块项目里还有一层:报告生成在子模块目录下,但路径写的是根目录。
解决:先在文件系统上ls -l确认jacoco.xml真的存在,再检查路径是否相对projectBaseDir。多模块用逗号分隔列全,或者配一个 aggregator 模块统一出报告。视觉上确认一下 XML 内容非空,<counter type="LINE"这类节点存在才算有效报告。
5.4 中文包装上后界面白屏
现象:重启后登录页面能打开,登录进去一片空白,浏览器控制台报静态资源 404,web.log里出现Plugin is not compatible。
原因:语言包 jar 的版本与 7.4 不匹配。7.4 只认特定版本区间的语言包,高版本的 jar 会被加载器直接拒绝,而拒绝时清理了部分静态资源。
解决:停服务,把extensions/plugins下不兼容的 jar 移走,重启确认界面恢复英文,再换一个兼容版本重新放进去。稳妥的操作是装插件前把整个plugins目录打包备份,出问题整目录回滚,比逐个排查快。
5.5 门禁显示 ERROR,但 CI 依然是绿的
现象:Web 界面上项目状态红色,质量门禁明确的Failed,可 Jenkins 上构建结果SUCCESS,代码照样合并。
原因:扫描器退出码只反映分析过程是否成功,不反映门禁结果,这是 7.4 的设计行为,不是 bug。后来的版本才加了sonar.qualitygate.wait参数。
解决:按 4.4 节的脚本,扫描完成后主动调/api/qualitygates/project_status拿状态,非OK就exit 1。注意 CE 异步消费有延迟,查得太早会拿到上一次的结果,用固定延时或者轮询api/ce/task都行。
6. 用 Web API 把结果拉出来:趋势看板与阈值告警
门禁解决的是「拦住不合格的提交」,但团队真正需要的是「知道质量在往哪走」。sonarqube-7.4 的 Web API 足够撑起一个轻量看板,不用装任何插件,拿 Token 直接调。
最常用的两个接口:/api/measures/component拿指标,/api/qualitygates/project_status拿门禁状态。下面这段脚本每天定时跑一次,把核心指标落到 CSV,接 Grafana 或者直接生成周报都行:
import csv import datetime import requests BASE = "http://127.0.0.1:9000/sonar" TOKEN = "8f2c1e9b7a6d4f3e0c5b2a1d9e8f7c6b5a4d3e2f" PROJECT = "order-service" # 7.4 支持的指标 key,逗号分隔一次拿全 METRICS = ",".join([ "bugs", "vulnerabilities", "code_smells", "coverage", "duplicated_lines_density", "sqale_index", "ncloc", ]) def fetch_measures(): resp = requests.get( f"{BASE}/api/measures/component", params={"component": PROJECT, "metricKeys": METRICS}, auth=(TOKEN, ""), # Token 当用户名,密码留空 timeout=15, ) resp.raise_for_status() data = resp.json()["component"]["measures"] # 接口返回的是列表,转成 key -> value 方便写 csv return {m["metric"]: m.get("value", "") for m in data} def append_row(row): today = datetime.date.today().isoformat() with open("sonar_trend.csv", "a", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow([today, PROJECT] + [row.get(k, "") for k in METRICS.split(",")]) if __name__ == "__main__": append_row(fetch_measures())auth=(TOKEN, "")这个写法对应 HTTP Basic 的空密码,和 4.4 节 curl 里的冒号是同一件事。measures返回结构是数组,metric是 key、value是字符串,有些指标在某些项目上不存在,用.get()兜底避免 KeyError。sqale_index是技术债,单位是分钟,看板上通常除以 60 再除以 8 换算成人天,别直接把分钟数丢给业务方看,没人有感觉。
三个实际用起来比较顺的做法:一是把qualitygates/project_status的conditions数组展开,能看到门禁是哪一条没达标,比只看一个 ERROR 有用得多;二是历史趋势不要自己存——7.4 的/api/measures/search_history直接返回时间序列,metrics=coverage&from=2024-01-01就能拉出一段时间的数据,比自己攒 CSV 更准;三是告警阈值别设太密,覆盖率单日跌 0.5% 就发消息,两天之后所有人都会把告警静音,按周对比更有意义。
那台老机器上跑到现在,我最大的一个教训是:不要在部署当天就把门禁阈值调到理想值。当初为了「显得规范」,上来就把新代码覆盖率卡在 80%,第一周整个团队的合并全被堵住,第二周门禁被管理员手动关掉,之后再也没人提过这件事。后来重来一次,60% 起步,每季度复盘上调 5%,反而一路推到了 75%。从那以后我每次接质量门禁的活,都强制走一遍「先宽后严 + 只看新增代码」的流程,宁可慢一点,也别让它变成第一个被绕过的东西。希望帮到你。
本文还有配套的精品资源,点击获取