一、背景随着敏捷开发和DevOps开发模式的普及,代码质量分析作为研发质量保证体系的重要组成部分,不仅可以有效降低迭代带来的失败风险对整个工程团队的绩效提升具有重要价值。携程很早就开始了DevOps建设。通过GitlabCI/CD,将静态扫描、单元测试、集成测试等流程引入开发和提交代码触发的pipeline中,在开发过程中打造一套代码质量闭环保障。系统。其中,在静态代码分析阶段引入了SonarQube,在原有的SonarQube代码规范库中对规范进行筛选扩展,形成了自己的代码规范库。但在实际应用过程中,我们发现还存在一些需要优化和解决的问题:在开发过程中,代码规范只能由开发者自己掌握。很难找到开发人员进行治理。代码单元测试通过率和代码覆盖率很高,但是还有一些应该在单元测试阶段发现的问题没有暴露出来,导致上线后出现bug,单元测试用例质量欠缺有效性和可靠性保证。随着项目的发展,为了避免影响已有功能,开发者在开发过程中大量复制粘贴,导致大量重复代码难以维护,程序逻辑结构过于复杂,修改逻辑会牵一发而动全身,可维护性差。代码中充斥着大量的sql拼接,一些不规范的写法导致潜在的问题,所以这种代码需要进行管理。2.平台介绍Alchemy平台是一个代码质量分析平台,提供Infer分析、代码分析、自定义扫描、代码搜索等功能。代码质量分析内容包括代码行数、sonar问题、infer问题、UT规则、重复代码、圈复杂度等,用户可根据自身需求在平台配置扫描项,查看应用的代码质量分析结果.为了及时获得提交代码变更的质量反馈,作为DevOps的重要组成部分,Alchemy平台结合GitlabCI/CD将静态代码分析推进到开发提交或合并代码阶段。开发者将代码提交到Gitlab,触发pipeline相关任务的执行。任务执行后,可以针对卡点的某些指标(比如增量代码引入的空指针)设置红线。如果指标在规定范围内,则允许合并发布代码,如果指标超出红线设置范围,则不允许合并代码,开发者修复问题后重新提交代码管道的集成发布。扫描分析结果可以展示在Gitlab或Alchemy平台上,帮助开发者在快速迭代的同时保证代码质量。静态扫码流程3.系统架构Alchemy平台包括Alchemy-client、Alchemy-service和Alchemy-web。其中Alchemy-client是一个扫描脚本,包括Infer分析、UT扫描、重复代码扫描、自定义扫描等功能,集成到Docker镜像中。Alchemy-service提供数据存储、分析等后台服务,依赖代码搜索服务CodeSearch-Service实现代码搜索功能,Alchemy-web负责页面交互。开发者提交代码,触发GitlabCI/CD中的静态代码分析作业在GitRunner中执行。执行时先从Docker仓库下载镜像,启动容器后执行Alchemy-client脚本。脚本会根据平台配置执行相应的扫描任务。扫描完成后,将结果上传到Alchemy-service,存入mongodb数据库,最后将分析结果显示在前端页面。Alchemy架构图4.功能4.1Infer分析Infer是Facebook出品的静态分析工具,可以分析Java、Objective-c或C代码,并报告潜在的问题,包括空指针、资源泄漏等。Alchemy平台引入了Infer进入代码静态分析阶段,目前支持全量和增量分析两种模式。full模式需要分析应用仓库中的所有代码,可以分析出所有代码引入的潜在问题。对于代码量大的应用,由于所有的代码文件都需要分析,所以扫描时间比较长,一定程度上影响了开发和发布进度。另外,对未修改的代码进行不必要的重复分析,在代码修改量不大的情况下造成资源浪费。因此,我们尝试加入缓存机制,引入增量分析模式。增量模式需要获取已经提交修改的文件。在分析阶段,只分析这些修改过的文件,可以大大节省分析时间。Infer分析流程如下:Infer分析流程图在分析过程中,首先判断是否是第一次分析,如果没有分析历史记录,系统默认为full模式,否则需要判断Infer扫描配置,如果配置是full模式,则分析本代码工程的所有文件。如果配置为增量模式,则需要获取本次提交修改的文件列表。编译过程完成后,在分析阶段指定要分析的文件列表。得到分析问题列表后,判断问题所在行是否为修改行,如果是,则记录为本次修改引起的新问题,否则为全面历史遗留问题。在实际应用中,对于封装的空判断方法,通过添加@TrueOnNull或@FalseOnNull注解,可以识别对象的空判断操作。但是对于第三方包的空检测方法,比如CollectionUtils.isEmpty(),由于没有添加注解,即使添加了空检测方法,仍然会被误识别为空引用。因此Alchemy平台增加了忽略操作,对此类问题进行二次确认,避免重复误判。推断误判结果4.2UT规则扫描单元测试是DevOps流程中非常重要的一个环节。我们可以用通过率、代码覆盖率等指标来衡量单元测试用例的完整性,但很难保证用例的有效性。阿里巴巴java开发手册规定,单元测试不允许使用System.out进行人肉验证,必须使用断言进行验证。在实际开发过程中,开发人员大部分时间都花在编写业务逻辑代码上。在编写单元测试用例时,往往容易忽略对结果的验证。虽然通过率和代码覆盖率很高,但是仍然存在接口结果没有经过验证而导致严重问题的情况。无效的单元测试用例包括以下几种:空函数:函数体为空;空断言:测试用例中实现了被测接口的调用逻辑,但不验证接口的返回结果;假断言:测试用例使用类似assertTrue(True)的假断言。通过扫描空断言、空函数、假断言等问题,判断用例是否需要验证代码逻辑。Alchemy平台支持单元测试用例的有效性验证。目前平台支持Java、Kotlin、Groovy、Nodejs,同时支持全量和增量两种扫描结果。测量结果为本次提交修改的测试用例中不符合规则的用例。UT扫描流程单个单元测试文件的扫描流程如图所示。首先根据文件后缀判断语言类型,然后根据不同的语言类型规则获取文件中的用例信息,包括用例名称、起止行、作者、最新修改时间、功能内容等。对于函数内容,首先判断用例是否有断言,如果有则判断是否为假断言。如果没有断言或者判断为虚假断言,则需要根据用例的起止行和本次提交的变更信息,进一步分析该用例是否为增量变更用例。标记为增量问题,最后将结果推送到gitlab,合并代码或发布时按照红线配置卡住。扫描结果如图所示。UT扫描结果4.3DuplicatecodescanningDuplicatecodes是重复或相似的代码。在开发过程中,开发人员为了避免影响已有功能,使用复制粘贴的方式快速完成开发任务,造成大量重复代码。重复的代码不仅大大增加了代码量,减慢了编译速度,而且占用了大量的存储空间。如果要修改一段代码逻辑,需要同时修改多个地方,容易遗漏,可维护性差。目前市面上的代码检测工具有很多,如Simian、PMD-CPD、CloneDR等,由于实现算法的差异,不同工具能够检测的重复代码类型也不同。我们使用PMD-CPD扫描代码仓库,可以检测到单个文件或多个文件中除空格、注释、换行、变量名外内容相同的代码段信息。这些信息包括文件路径、代码段内容、起止行、作者信息,详细结果如图所示。重复代码详情结果4.4自定义规则扫描Alchemy支持自定义规则扫描。通过配置自定义正则表达式和扫描范围,可以识别出代码文件中符合配置规则的代码段,可以用来扫描代码中拼接的SQL,哪些是敏感词等,不合规的代码可以位于相关开发商处。自定义扫描流程单个文件的扫描流程如图所示。首先判断文件是否在扫描范围内。如果没有,则跳过直接扫描下一个文件。否则,读取文件内容,根据文件类型获取对应的自定义规则。匹配很满意。规则的代码段信息,包括代码段内容、严重性、起止行、作者等。在某些场景下,需要设置子规则进行二次匹配。比如扫描没有指定where条件的sql语句进行update,可以先根据规则找到update语句,然后根据子规则判断是否有where条件,最后记录二次匹配结果。4.5代码分析不同工具统计的代码质量指标可能分散在不同的平台上,在综合分析这些指标的过程中难免会遗漏一些,特别是对于没有设置发布卡点的指标,开发者可能不知道关注它们会导致代码中存在大量未被分析和管理的潜在问题。Alchemy代码分析模块可以对代码不同维度的指标进行统计分析,包括代码行数、单元测试、infer问题、Sonar问题、重复代码、圈复杂度等,用户可以在上查看各个维度的问题分布情况代码分析页面,对项目的整体风险指标进行更全面的分析,并根据问题的严重程度设置优先级,进行针对性治理。代码分析结果4.6代码搜索开发者在开发过程中,可能需要通过中间件的使用等一些常用操作来搜索访问文档。Alchemy提供了代码搜索功能,可以帮助开发者根据关键字在包含的项目中找到代码使用示例。用户可以根据项目仓库、代码语言、作者进行细分查询。在编码过程中,命名约定是一个容易被忽视的问题。使用Alchemy的变量命名功能,用户可以通过搜索中英文关键字获取根据不同语言推荐的标准命名参考,可以大大提高开发效率。代码搜索结果五、结语在本文中,我们介绍了Alchemy平台提供的代码静态分析、代码探索以及与GitlabCI/CD集成带来的持续集成能力,可以在开发阶段暴露出更多的代码潜力.问题和风险,及时反馈给相关人员。目前,携程酒店已对接800+项目,分析出的潜在问题已在开发、提交、发布阶段对接卡点流程。在后续工作中,我们将在以下几个方向进一步优化:在代码分析层面支持更多语言;开发IDE插件,在打码阶段实时扫码;继续深挖代码风险指标,引入评估机制。
