美文网首页自己尝试等装一下恩美第二个APP项目iOS
iOS 持续集成系列 - 自动化 Code Review

iOS 持续集成系列 - 自动化 Code Review

作者: PPPan | 来源:发表于2016-11-24 23:17 被阅读4917次

    为了保证代码质量,Code Review 是非常重要的一环。细到*的位置是否正确,大到代码的结构是否符合了软件开发的一些基本原则,都在这项工作的范围内。

    受限于现实情况,大多数团队没有足够的时间进行 Code Review,那么只能把一部分 CR 工作交给计算机去完成了。我们只需要定下合理的流程,用代码告诉计算机需要做什么,剩下的就交给我们可靠的伙伴吧。

    应用了自动化 Code Review 后,如果你的代码写得不好,Xcode 会表示不开心。

    如果你忽略 Xcode 的心情,那么质量管理平台会默默地记录这一切。

    这套东西既帮助开发们写出更高质量的的代码,也给经理们对工程质量的评估提供了一个切面的支持,同时只需要花费较少的人力维护,听起来是不是跃跃欲试了呢 : )

    流程

    整体的工作流程非常简单,如图:

    自动化 Code Review 总体流程自动化 Code Review 总体流程

    关键点在于本地 Review远端 Review这两步。前者是提供给开发者一个即时的代码质量反馈,以便开发者修改,从而避免在接下来的远端 Review 中得到一个较低的得分。后者则是为了生成相关报表,为项目管理人员跟踪项目质量提供依据。在很多大公司里,这也是开发者们绩效的参考之一。

    剩下的就是一些胶水步骤了,如何让过程更自动化,就是胶水步骤要做的事。例如利用 WebHook 自动触发远端 Review,利用 Git 的钩子进行增量校验而不是全量校验等。这些我们放在后面聊,先来看看本地校验的流程。

    本地 Review

    本地自动化 Code Review本地自动化 Code Review

    在本地 Review 环节,开发者只需要像往常一样按下 CMD + B,然后只要静静地等待进度条读完,满屏的⚠️就会精确地指示出某一行的代码违反了哪条规则。此时开发者就可以根据代码规范进行对应修改。

    从按下按键到产生警告主要发生了这么几件事情:

    • 生成 compile_commands.json 文件
    • OCLint 读取相关的 Rules,逐个扫描 compile_commands.json 中的 .m 文件
    • OCLint 将生成的报告展示在 Xcode 上

    实现本地 Review 的核心就是 OCLint 和 compile_commands.json文件

    OCLint

    工欲善其事,必先利其器

    OCLint 是一个开源的,基于 Clang 用 C++ 编写而成的,可以用于 C、C++ 和 Objective-C 的静态代码分析器。它可以在扫描的过程中动态加载规则文件(Rules),因此可以实现非常灵活的,高度可自定义的代码分析方案。它几乎可以和大多数系统无缝集成,例如 Cmake、Bear、xcodebuild、xctool、Xcode、xcpretty、Jenkins CI、Travis CI 等。你可以在这里找到如何将其和 Xcode 配合使用。

    最新版本的 OCLint 已经自带了 71 条 Rules,基本上都是先人宝贵的经验,比如这条禁用 goto 语句的 Rule,就是来源于 Edsger W. Dijkstra 1968 年的一篇手稿

    这 71 条 Rules 已经可以帮助我们避免一部分因书写习惯和语言误区而导致的问题,但是对于有完整编码规范的公司来说显然是不够的。我们必须要自己开发 Rules。

    幸运的是,OCLint 已经为我们准备好了一切。

    OCLint 提供了 Clang 和 AST (Abstract Syntax Tree) 的一层封装,使我们不必对抽象语法树进行解析,只需要专注规则相关的逻辑开发即可。从其提供的接口中我们可以很明显地看出这一点。

    // 遇到一元操作符
    bool VisitUnaryOperator(UnaryOperator *node)
    
    // 遇到二元操作符
    bool VisitBinaryOperator(BinaryOperator *node)
    
    // 遇到 Objective-C 的函数声明
    bool VisitObjCMethodDecl(ObjCMethodDecl *node)
    

    在开发好相关的规则后,打包成 dylib,就可以在分析的时候加载我们自己的 Rule 了

    compile_commands.json

    compile_commands.json 是 Clang 定义的一个规范,里面存放了一组工作目录目标文件需要被执行的命令,帮助相关工具可以独立于编译系统来将源代码文件转换为 AST 并做对应的事。

    看文件内容会更直观一些:

    [
    {
      "directory": "/path/to/project/", 
      "command": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x ...", 
      "file": "/path/to/project/XXXViewController.m"
    },
    ...
    ]
    

    OCLint 可以根据 compile_commands.json 中的内容,批量检查源代码文件。

    xcpretty

    还有一个点需要关注的是,如何生成 compile_commands.json 文件?

    最便捷的方式是使用 oclint-xcodebuild 来生成。首先,利用xcodebuild 生成 xcodebuild.log 文件。

    xcodebuild | tee xcodebuild.log
    

    然后利用 oclint-xcodebuild 生成 compile_commands.json

    oclint-xcodebuild
    

    截至 Xcode 8.1,这种做法可以正确生成 json 文件。由于 OCLint 团队已经声称不再维护 oclint-xcodebuild , 因此可能在未来的某个 Xcode 版本中这个方法将不再适用。

    另一个推荐的方法是利用 xcpretty

    xcpretty 可以一句话生成 json 文件。

    xcodebuild | xcpretty -r json-compilation-database --output /path/to/compile_commands.json
    

    使用本地 Review

    了解了这些工具后就很容易明白本地自动化 Code Review 是如何工作的,使用方式也非常容易理解了:

    1. 首先在电脑本地安装好 OCLint 并拿到公司自定义的 Rules 文件
    2. 在 Xcode 上配置好工程
    3. build 工程,等待结果显示在 Xcode 上。

    附一个我们团队的配置脚本供参考:

    source ~/.bash_profile
    cd ${SRCROOT}
    xcodebuild clean
    xcodebuild | tee xcodebuild.log
    oclint-xcodebuild
    oclint-json-compilation-database \
    -e Vendor \
    -e Pods \
    -- \
    -max-priority-1 100000 \
    -max-priority-2 100000 \
    -max-priority-3 100000 \
    -report-type xcode \
    -R /path/to/rules
    

    远端 Review

    远端自动化 Code Review远端自动化 Code Review

    远端 Review 和 本地 Review 大体相似,区别在与引用构建的脚本的对象从 Xcode 变成了 Jenkins CI ,报告的展示者从 Xcode 变成了 SonarQube 。其流程是这样的:

    工程师通过 git push 提交代码
    → Web Hook 触发 Jenkins 构建
    OCLint 扫描代码生成PMD格式报告
    Sonar-runner 读取报告并展现到 SonarQube

    CI 环境

    为了实现远端 Review ,服务端必须首先有一套 CI 环境。鉴于 iOS 的特殊性,服务器必须是 macOS 系统。CI 我们直接选择开源的 Jenkins,质量管理平台则选用开源的 SonarQube。Jenkins 大名鼎鼎大家都非常熟悉了,SonarQube 则相对少的人了解。

    SonarQube 是一个质量管理平台,在 SonarQube 上,你可以看到一个项目的代码行数、文件数量、代码重复率、违反的代码规范、技术债时间等等指标。SonarQube 对 Java 的支持极度友好,提供了 SonarScanner 可以直接对 Java 源代码进行扫描。Objective-C 就没有这么幸运了。虽然 SonarQube 也提供了 Objective-C 的报告展示的支持,但静态分析还是得依靠 OCLint 。

    Sonnar-Runner

    我们在 Jenkins 上运行 OCLint 生成了报告。需要一个中间人将报告解析成 SonarQube 可以理解的格式并传输到 SonarQube 平台。这个中间人就是 Sonnar-Runner。Sonnar-Runner 在我们的系统中也仅仅扮演这个搬运工的角色。你可以从这里了解到如何在 Jenkins 上安装和使用 Sonnar-Runner。

    Sonnar-Runner 只能解析 PMD 格式的报告,因此我们在使用 OCLint 分析代码后,需要将报告格式输出为 PMD 格式

    oclint -report-type pmd -o ./report.xml
    

    Rules in Sonar

    SonarQube 有一套规则,将代码问题按照严重程度分为 5 个等级,不同等级的问题会以不同权重影响到项目质量评分。这套规则和 OCLint 生成的报告中的 Rule name 必须要一一对应,SonarQube 才能正确将报告中的问题归类并评分。

    如果你使用 OCLint 原生的 Rules 来检查代码,只需要在 SonarQube 上安装 SonarQube Plugin for Objective C 插件,相关的报告就会被正确识别了。

    如果是使用了自行开发的 Rules ,只需要 Clone 上述插件,并在profile-oclint.xmlrules.txt 中添加相关的 rule name ,然后打包并将这个插件安装到 SonarQube 上即可。

    举个例子:

    当我们用自行开发的 Rule 检查完代码后,生成了report.xml,内容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <pmd version="oclint-0.11">
        <file name="/path/to/TerribleCode.m">
            <violation rule="binary operator space (HT_iOS_Coding_style 2.8)" begincolumn="9" endcolumn="157" beginline="73" endline="73" priority="3" ruleset="HT_iOS_rules" >
                多元运算符和他们的操作数之间至少需要一个空格
            </violation>
        </file>
    </pmd>
    

    其中 binary operator space (HT_iOS_Coding_style 2.8) 是我们定义的错误rule name。在 SonarQube 上,也必须对应有这么一条 rule 的 name,才能正确识别这个错误。

    此时我们只需要在上述插件的 rules.txt 中添加一段

    binary operator space (HT_iOS_Coding_style 2.8)
    ----------
    
    Summary:多元运算符和他们的操作数之间至少需要一个空格。 
    
    Severity: 2
    Category: Hengtian iOS Coding Standard
    

    在上述插件的 profile-oclint.xml 中添加另外一段代码

         <rule>
          <repositoryKey>OCLint</repositoryKey>
          <key>binary operator space (HT_iOS_Coding_style 2.8)</key>
        </rule>
    

    然后将这个插件打包并安装到 SonarQube 上,SonarQube 就可以正确识别我们的问题并分类了。

    使用远端 Review

    在使用前,一定要确保你的 macOS 服务器已经安装好了最新版的 Xocde、OCLint、Jenkins、sonnar-runner,安装好 Jenkins 的相关插件,并将自定义的 Rule 放置在服务器上(如果有的话)。

    检查并生成报告

    在 Jenkins 上新建工程并配置好Git、构建触发器等其他内容。在构建步骤中添加一步 Execute Shell ,填入下述脚本

    cd YourProjectDir
    xcodebuild clean
    xcodebuild -workspace MyProject.xcworkspace -scheme HTMarket -sdk iphonesimulator | tee xcodebuild.log | xcpretty
    oclint-xcodebuild
    oclint-json-compilation-database -e Pods \
    -v \
    -- \
    -max-priority-1 100000 \
    -max-priority-2 100000 \
    -max-priority-3 100000 \
    -report-type pmd \
    -R /path/to/diy-rules \
    -o /path/to/report.xml 
    

    脚本大致和本地 Review 一致,有三个地方需要注意一下。

    1. xcodebuild 命令添加了 -sdk iphonesimulator参数,以避免 build 需要 Code Sign 的问题。
    2. -report-type pmd 输出格式必须为 pmd 格式
    3. -o /path/to/report.xml 注意输出报告的路径,下一步sonnar-runner 读取时会用到。

    读取到 SonarQube

    在上一步的下方再添加一步 Invoke Standalone SonarQube Analysis,选择好你的 sonnar-runner。并在 Analysis Properties 中添加如下配置:(如果没有这一项,你可能需要安装 SonarQube 相关的插件。)

    sonar.projectKey=YOUR_PROJECT_NAME
    sonar.projectName=YOUR_PROJECT_NAME
    sonar.projectVersion=1.0
    sonar.language=objc
    sonar.projectDescription=YOUR_PROJECT_DESCRIPTION
     
    # Path to source directories 
    sonar.sources=/path/to/source/directories
     
    # Xcode project configuration (.xcodeproj or .xcworkspace)
    # -> If you have a project: configure only sonar.objectivec.project
    # -> If you have a workspace: configure sonar.objectivec.workspace and sonar.objectivec.project
    # and use the later to specify which project(s) to include in the analysis (comma separated list)
    sonar.objectivec.project=YOUR_PROJECT_NAME.xcodeproj 
    sonar.objectivec.workspace= YOUR_PROJECT_NAME.xcworkspace
    
    # Scheme to build your application
    sonar.objectivec.appScheme=YOUR_PROJECT_NAME
     
    sonar.sourceEncoding=UTF-8
    
    # OCLint report generated by run-sonar.sh is stored in sonar-reports/oclint.xml
    # Change it only if you generate the file on your own
     sonar.objectivec.oclint.report=YOUR_REPORT_FILE_PATH
    
    

    注意看注释并修改 YOUR_PROJECT_NAMEYOUR_PROJECT_DESCRIPTION、和 YOUR_REPORT_FILE_PATH为你项目的值。

    一切顺利的话,在 Jenkins 上立即构建,你就可以在你的 Sonar 平台上看到代码质量报告了。

    配合好构建触发器 和 Git 平台的 WebHook 功能,就可以在开发提交代码或者合并分支等关键点自动触发构建了。

    Troubleshooting

    为什么生成的 compile_commands.json 为空

    检查 log 是否为空,如果 log 为空则代表 build 失败。排除失败原因后即可正常生成。

    Jenkins 构建遇到了如下问题

    ❌  Code signing is required for product type 'Application' in SDK 'iOS 10.0'
    

    遇到这样的情况,是因为构建了 Release 版本,且项目在 Xcode8+ 上开启了 Automatic Code Sign。解决方法如下:

    1. 如果只需要检查代码规范,则在 xcodebuild 命令后添加 -sdk iphonesimulator 参数指明以 Debug 方式构建即可。
    2. 如果希望构建 Release 版本,那么关闭自动签名,在 CI 系统上手动配置证书和Proversion Profile。或者保留自动签名,参考这个回答sed 命令在构建前修改相关配置。

    参考链接

    相关文章

      网友评论

      • 岁与禾:compile_commands.json是空的是什么情况呀?
        另外,xcodebuild | tee xcodebuild.log 这个脚本执行后报错,报AFNetworking找不到,我的项目用了cocoapods
        PPPan:@一月二十三 用 workspace 的话,需要指定 scheme。compile_commands.json 为空一般是某些地方配置错误,时间比较久了,我也不太记得,多做几次试验定位一下原因吧
      • e000a9279be3:执行这个报错:
        oclint -p ./ -report-type pmd -o ./report.xml
        oclint: Not enough positional command line arguments specified!
        Must specify at least 1 positional argument: See: oclint -help
        另外:
        oclint-json-compilation-database -e Pods -v -- -report-type pmd -o oclint.xml -max-priority-1=10000 -max-priority-2=10000 -max-priority-3=10000
        ------------------------------ OCLint ------------------------------
        /usr/local/bin/oclint -p /Users/Jenkins/workspace/Sonar-project-iOS -report-type pmd -o oclint.xml -max-priority-1=10000 -max-priority-2=10000 -max-priority-3=10000
        --------------------------------------------------------------------
        oclint: Not enough positional command line arguments specified!
        Must specify at least 1 positional argument: See: /usr/local/bin/oclint -help
      • ChuckWang:说实话
        内容全是干货.
        去楼主的Github上面去膜拜了下.
        有机会希望做朋友.

        花了一上午在现在的工程当中集成了OCLint.
        接着看远程的Review.
      • fir_im官方:PPPan,快来试试 flow.ci 吧 https://flow.ci
      • 梅西和他的世界杯:想问下楼主的代码环境,我的Xcode8.1,跑在主target每次都编译好长时间,我看oclint提供的aggregate这种方法按照官方不好使,想问楼主用什么方式
        PPPan:@Yiweiwoshiniya 用 C++ 写,写完打包成 dylib 。 OCLInt 官网有文档,上面写得很详细了。
        梅西和他的世界杯:@PPPan 我想要自定义一套自己的规则如何写规则文件 , /path/to/rules这里是楼主自己的规则文件吗,有没有介绍如何写的,想写一套带中文message的规则,谢谢楼主,忘告知下😄
        PPPan:@Yiweiwoshiniya 按照官方的教程来,脚本稍微改改就可以了。试试下面的脚本👇

        source ~/.bash_profile
        cd ${SRCROOT}
        xcodebuild clean
        xcodebuild | tee xcodebuild.log
        oclint-xcodebuild
        oclint-json-compilation-database \
        -e Vendor \
        -e Pods \
        -- \
        -max-priority-1 100000 \
        -max-priority-2 100000 \
        -max-priority-3 100000 \
        -report-type xcode \
        -R /path/to/rules
      • 司令写简书:关于这一块的持续化集成能不能提供step by step的教程? :smiley:
      • 8abf4a1d1d51:大神 你好你好 :smile:
        我今年刚毕业,在新加坡进了一家start up公司,但iOS developer就我一个人,感觉有很多东西可以学,但是因为没有人带,不太懂新人应该学一些什么东西,目前项目快做完了,是用swift+storyboard写的,CMD + B run出来是没有⚠️警告的,但看了网上感觉有很多东西应该优化,最近也才看到AFNetworking,Alamofire, Swifty这些,但之前实习公司manager又说third party少用,不然又学不到东西,感觉自己一个人会有点迷惑
        请问大神,新人应该从iOS哪几个方面学习呢?或者有什么文档可以看看吗?平时都是google上搜索答案,一般在stackoverflow上看看
        谢谢大神回答 :))
        8abf4a1d1d51:@PPPan 好的,谢谢。这个项目做完就准备整理归纳一下。
        Afer:@hhhhhaosoar 卧槽 新加坡 求带
        PPPan:@hhhhhaosoar 如果仅仅是使用第三方库是学不到东西的,在用的过程中带着好奇心不断问为什么,然后去源码中查看原因,这样提升会很快。
        另外,提升都是基于大量实践的基础上的。我的建议是,不断地做项目,解决问题,积累到一定程度后,可以系统地归纳一下。这时再去看一些编程思想类的优秀书籍,把量变变为质变。

        一句话就是带着问题,静心积累。
        加油 : )
      • 哇丶啦咔咔:这个报错是怎么办?line 2: /Users/XXXX/.bash_profile: No such file or directory
        @PPPan
        PPPan:@哇丶啦咔咔 删掉脚本里的 source ~/.bash_profile 这一句。

        或者命令行里输入 `touch ~/.bash_profile`
      • 是我始终拒绝成长吗:pch文件引用了第三方类库的头文件 报错 这个该怎么解决呢
        PPPan:@是我始终拒绝成长吗 这个不是本文讨论的问题吧?
      • 心亦逸风:刚来公司,项目都是一堆警告=。=
      • shajia:支持swift吗
        PPPan:Swift 可以用 SwiftLint 替换 OCLint 流程都一样的。
      • 咖啡bu加糖:自动化打包啥时候能出炉呢
        Afer:@李大宝是个小胖子 哈哈 我也写了一个简单的 https://github.com/myafer/HHSSJJ
        咖啡bu加糖:@PPPan 期待,之前仿照别人的研究了一下没弄明白,期待你的自动化打包
        PPPan:@李大宝是个小胖子 下一篇先出这个
      • 晒着的鱼_9524:必须赞一个
      • Schorem:首先先赞一个,我们公司没有code review 但是我想对自己要求高一点,可以只是本地review吗?还有规则默认就行吗?
        PPPan:@Schorem 可以的 只要安装好 OCLint 配置好 Xcode 就好了
      • 天堂秀:题主文章字体能变颜色并做调整怎么做到的?
        PPPan:@天堂秀 还是Markdown 的写法

        [持续集成系列 - 开篇 ](http://shengpan.net)
        天堂秀:@PPPan iOS 持续集成 - 开篇 看到你是可以跳转到另一页的,就想问这个怎么做到的?
        PPPan:@天堂秀 你是指代码块里面的代码高亮吗? 那个用 Markdown 的写法就可以了。

        ```java
        这里放想要高亮的代码
        ```
      • 成功的失败者:我想问工作了,还有时间学习与工作无关的知识吗?还有时间写长长的博客吗?
        SMFly:@成功的失败者 11点了再下班回家路上,怎么破
        hauibojek:@成功的失败者 看你的去什么公司了 :joy:
      • zhangferry:喜欢 :heart:
      • bdb5afbd7384:支持一下。

      本文标题:iOS 持续集成系列 - 自动化 Code Review

      本文链接:https://www.haomeiwen.com/subject/wfjipttx.html