iOS 测试 iOS 静态代码扫描平台 Sonarqube 实战 Objective-C、Swift

lsy · 2018年04月25日 · 最后由 HEYRIX 回复于 2020年11月06日 · 522 次阅读

前言

特别鸣谢:Haiyi(独门秘籍)、LV(实践修改 Objective-C 插件源码)、YONG(处理打包脚本)

本文主要阐述在事业部使用 SonarQube 构建 iOS:Objective-C、Swift 静态代码分析,分享遇到的坑,文章内容有限,一些细节不能到位的,请各位脑补下,谢谢。

SonarQube 简介

旧版 Sonar 展示维度如下:

新版 SonarQube 已经改变了关注维度,推出质量模型:

  • Bugs:是出现了明显错误或是高度近似期望之外行为的代码。
  • 漏洞:是指代码中可能出现被黑客利用的潜在风险点。
  • 坏味道:代码异味会困扰代码的维护者并降低他们的开发效率。主要的衡量标准是修复它们所需的时间。

建议根据团队需要更新到新版本:至少 5.6+ 以上。

SonarQube 架构

SonarQube 平台的组成:
  • 数据库:存放 SoanrQube 的配置数据,代码质量的快照数据
  • Web 服务:用于查看 SonarQube 配置数据,代码质量的快照数据
  • 分析器:对项目代码进行分析,生成质量结果数据并存于数据库中
  • 插件:各种语言支持的插件

不要忽略了 CI

虽然 SonarQube 具备分析器,可以对多种编程语言进行构建分析,但是依然建议使用 CI 工具,例如 Jenkins 来管理日常构建,让 SonarQube 仅仅展示最终数据即可。

iOS 静态代码分析

目前 iOS 核心开发语言:Objective-C,也有不少项目采用了 Swift 语言,逐步过渡,因此项目的组成有两种模式:

  • 单一语言使用:Objective-C、Swift
  • 混合语言使用:Objective-C+Swift

下面通过实战分析两种模式的构建。

iOS 静态代码分析的计划

Objective-C 原以为就跟 Java 构建一样,如此简单,

实际遇到的坑是很大的,而且很受伤,


踩过坑的路才踏实


捅一万刀也不过分

iOS 静态代码分析:Objective-C 实战

工欲善其事必先利其器,工具如下:

  • 环境工具:XCode 8.2+、Xcpretty 0.2.8、OCLint 0.12、xctool、gcovr
  • 构建静态分析插件
SonarCFamily 官方收费

官方插件太贵了,找开源吧

  • 开源 SonarQube Plugin for Objective C(传送门) 插件安装参考网上教程,下载 jar 拷贝到 SonarQube 项目目录下:extensions/plugins


安装成功的示例

构建脚本
  • run-sonar.sh 在 Jenkins 的 Execute shell 配置脚本如下,也可以按照项目要求重名更好格式
cd $WORKSPACE
xcodebuild -workspace xxx.xcworkspace -scheme xxx clean build | tee xcodebuild.log | xcpretty --report json-compilation-database

mv build/reports/compilation_db.json compile_commands.json
oclint-json-compilation-database -exclude Pods -- -report-type pmd -o oclint.xml -max-priority-1 99999 -max-priority-2 99999 -max-priority-3 99999 -rc LONG_LINE=140 -rc LONG_METHOD=80 -rc NCSS_METHOD=50 -rc SHORT_VARIABLE_NAME=1 -rc CYCLOMATIC_COMPLEXITY=13 -rc MINIMUM_CASES_IN_SWITCH=2 -rc NPATH_COMPLEXITY=1500

rm -rf sonar-reports
mkdir sonar-reports

cat oclint.xml  | sed "s#Switch Statements Should Have Default Rule#switch statements should have default#g" \
| sed "s#missing hash method#must override hash with isEqual#g" \
| sed "s#prefer early exits and continue#use early exits and continue#g" \
| sed "s#use boxed expression#replace with boxed expression#g" \
| sed "s#use container literal#replace with container literal#g" \
| sed "s#use number literal#replace with number literal#g" \
| sed "s#use object subscripting#replace with object subscripting#g" \
| sed "s#missing default in switch statements#switch statements should have default#g" \
| sed "s#unnecessary default statement in covered switch statement#switch statements don't need default when fully covered#g" \
| sed "s#covered switch statements dont need default#switch statements don't need default when fully covered#g" > sonar-reports/oclint.xml
rm -f sonar-project.properties 

cat > sonar-project.properties <<- EOF 
sonar.projectKey=xxx-iOS
sonar.projectName=xxx-iOS 
sonar.projectVersion=x.x.x
sonar.language=objectivec 
sonar.sources=sources 
sonar.sourceEncoding=UTF-8 
sonar.objectivec.oclint.reportPath=sonar-reports/oclint.xml 
EOF 

/bin/sh sonar-scanner -X
构建结果

独门绝技介绍(感谢事业部:haiyi 大神倾亲奉献)

构建错误 errors generated

3 errors generated.
20 errors generated.
20 errors generated.
20 errors generated.
8 errors generated.
19 errors generated.
3 errors generated.
63 errors generated.

检查 OCLint,升级到 0.12 版本,与 XCode8.2+ 配合

构建错误 does not exist

The rule 'OCLint:use number literal' does not exist.
The rule 'OCLint:use object subscripting' does not exist.
The rule 'OCLint:ill-placed default label in switch statement' does not exist.
The rule 'OCLint:Switch Statements Misplaced Default Label' does not exist.

主要原因是 sonar-objective-c-plugin-0.5.0-SNAPSHOT.jar 中未包含此规则,可以通过修改源码添加规则解决(网上有一堆教程),比较繁琐的是,不同项目遇到不同错误,需要添加多次,则多次打包 jar,再导入 SonarQube,开销大,haiyi 大大给的秘籍是:用 sed 替换构建的 oclint.xml 文件

sed "s#missing hash method#must override hash with isEqual#g"

将缺失规则:missing hash method,替换为:must override hash with isEqual,每次遇到有缺失的新规则,脚本替换即可,至于怎么准确匹配,去看看质量配置的具体含义再替换。

Objective-C 实战总结
  • 安装构建工具所需版本号
  • xcodebuild 构建项目生成 compile_commands.json
  • oclint-json-compilation-database 构建 compile_commands.json 生成 oclint.xml
  • sed 替换 oclint.xml 缺失规则
  • sonar-project.properties 配置 oclint.xml 文件路径
  • /bin/sh sonar-scanner -X 增加-X 输出 debug 日志跟踪

iOS 静态代码分析:Swift 实战

工欲善其事必先利其器,工具如下:

环境工具
brew install Swiftlint
gem install slather
sudo pip install lizard

构建静态分析插件

SonarSwifty 官方收费

官方插件太贵了,找开源吧

开源 sonar-swift(传送门

插件安装参考网上教程(backelite-sonar-swift-plugin-0.3.4.jar SonarQube5.4/5/6/6/3 测试通过),下载 jar 拷贝到 SonarQube 项目目录下:extensions/plugins


安装成功的示例

构建脚本
  • run-sonar.sh 在 Jenkins 的 Execute shell 配置脚本如下,也可以按照项目要求重名更好格式
cd $WORKSPACE

rm -rf kuai-swiftlint.txt
swiftlint lint --path Duobao > xxx-swiftlint.txt

rm -rf sonar-project.properties

cat > sonar-project.properties <<- EOF
sonar.projectKey=xxx-iOS-swift
sonar.projectName=xxx-iOS-swift
sonar.projectVersion=x.x.x
sonar.language=swift
sonar.projectDescription=xxx with Swift
sonar.sources=sources
sonar.swift.workspace=xxx.xcworkspace
sonar.swift.appScheme=xxx
sonar.sourceEncoding=UTF-8
sonar.swift.swiftlint.report=xxx-swiftlint.txt
EOF

/bin/sh sonar-scanner -X
构建结果

  • 注意事项
The structure of the plugin is based on the sonar-objective-c plugin.
In SonarQube under Quality Profiles the used Linter can be specified by  selecting either the SwiftLint Profile or the Tailor Profile as Default  profile for Swift Projects:


如果不设置,关联规则有问题

目前暂无遇到缺失规则问题

Swift 实战总结
  • 安装构建工具所需版本号
  • swiftlint 生成 xxx-swiftlint.txt
  • sonar-project.properties 配置 xxx-swiftlint.txt 文件路径
  • /bin/sh sonar-scanner -X 增加-X 输出 debug

iOS 静态代码分析:Objective-C+Swift 实战

这里不详细介绍实战过程,直接说总结
  • Objective-C+Swift:分开构建,脚本如下,当做两个项目配置如上文所示
  • Objective-C+Swift:一起构建,本质上他们两个的插件是不同的,但是可以利用 sonar 模块的概念来构建
objectivec_swift目录结构
--objectivec:完整项目文件
--swift:完整项目文件
脚本如下
sonar.projectKey=objectivec_swift
sonar.projectName=objectivec_swift
sonar.projectVersion=1.9.0
sonar.sourceEncoding=UTF-8
分模块
sonar.modules=objective,swift
构建 objectivec
objective.sonar.projectName=objectivec
objective.sonar.language=objectivec
objective.sonar.projectBaseDir=objectivec
objective.sonar.sources=sources
objective.sonar.oclint.reportPath=sonar-reports/oclint.xml
构建 swift
swift.sonar.projectName=swift
swift.sonar.language=swift
swift.sonar.sources=sources
swift.sonar.projectBaseDir=swift
swift.sonar.swift.workspace=swift/xxx.xcworkspace
swift.sonar.swift.appScheme=Duobao
swift.sonar.sourceEncoding=UTF-8
swift.sonar.swift.swiftlint.report=swift/xxx-swiftlint.txt
构建结果


没有看到扫描规则问题展示,估计是配置文件或者构建文件路径有问题,暂且告一段落吧,有折腾出来的小伙伴喊一下,谢谢哟,_^

最后附上 github 地址,随意取:

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 18 条回复 时间 点赞
lsy #1 · 2018年04月25日 Author

目前已经在多个部门应用成功,特此发帖纪录。

的确,我们结合到研发过程,形成一个闭环,发现的问题,拿出来评审,团队认为重要的安排修复,不然就忽略。
之前一个例子:扫描找到一些逻辑问题(未必是 iOS),然后拉着研发团队评审,整了几次,大家都重视起来了。
用再多工具,没有得到研发、整个团队认可,带来价值的话,其实没啥意义

自定义扩展规则有哪些?

lsy #16 · 2018年04月25日 Author
simple 回复

想跟 360 借鉴学习下,你们走在前面呀,我们还是实践小打小闹,现在还在摸索自定义扩展的呢。

lsy 回复

360 的 iOS 产品。。。。不说了

lsy #6 · 2018年04月25日 Author
simple 回复

我们可以说说 android、java 的思路参考下,哈哈

cat oclint.xml | sed "s#Switch Statements S 这一直报找不到 oclint.xml 文件

./objectivec_build.sh: line 19: syntax error near unexpected token |'
./objectivec_build.sh: line 19:
| sed "s#missing hash method#must override hash with isEqual#g" \'

iOS 的 bugs、漏洞都是 0,这个问题解决了吗?

lsy #10 · 2018年10月10日 Author
xingsong 回复

自己改一下语法,当时的环境可能有差异,就是替换掉找不到的规则为可识别的。

lsy #11 · 2018年10月10日 Author
xingsong 回复

oclint.xml 在当前目录下,是不是在其他目录,还是没有生成呢

SonarQube Plugin for Objective C 进到 github 项目里,jar 包链接打开是 404

装了 backelite-sonar-swift-plugin-0.3.4.jar 后,sonarqube 出现启动失败,有遇到这个问题吗?

请问大牛有没有遇到除了 duplicate 是有数字的之外,bug,code smells 和 Vulnerabilities 都是 0 的情况

jiangjinlian 回复

遇到了,求教这个怎么解。。。

木头 回复

我也遇到了,有解决吗?

jiangjinlian 回复

在 Sonar 配置中检测 SCM 的值,改为 enable。

yuweixx 回复

版本不兼容。更新版本。

xinxi iOS 代码扫描从放弃到入门 中提及了此贴 01月24日 08:27
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册