Aladdin编码规范Idea插件实践

1.背

在软件开发过程中,不规的代码和违件的引⼊常常会导代码量下降和给项⽬引⼊⻛险集成业务中,现代码规扫描拦截代码push候,件拦截⽣成建产物后现问题点较晚,为了将卡点进⼀步左降低研员因代码准⼊失败造成的⼯成提⾼代码量,需要寻求上问题场景的解决⽅

2.⽅

经过插件⽅的调研与验证,我们基于intellijAladdin插件,插件主要提供则基于Alibaba码规范条款发中组件使⽤规,提供多扫描⽅式为:实增量代码扫描增量代码扫描提交前增量扫描⼿动全局代码扫描⼿动增量代码扫描同时提供提交问题拦截扫描⽅式配置

3.插功能

3.1注册Inspection

想要对代码⾏扫描,我们需要idea册⼀个Inspection查器,当编辑器中代码动或保存代码会调⽤个检查器,扫描代码,渲染问题描到对应代码中

  • ⼀个定义inspection , ⾸先要册我们的增量inspection,  插件项⽬中

\src\main\resources\META-INF\plugin.xml   插件配置⽂件中配置我们的增量⾼代码分inspection

XML

  1. <extensions defaultExtensionNs="com.intellij">
  2. <localInspection language="JAVA"
  3. shortName="AladdinCodeStyleP1Incr"
  4. displayName="增量⾼代码分"
  5. hasStaticDescription="true"
  6. enabledByDefault="true"
  7.                                              implementationClass="com.example.PmdP1IncrInspect ion"
  8. groupName="Aladdin码规"
  9. level="ERROR"
  10. runForWholeFile="true"

11                    />

12      </extensions>

  • 册好Inspection,接下实现我们的增量代码分idea编辑器中⽂件动或主动保候,会触inspection的扫描,Inspection很多,每⼀个会触⼀次,此实对性要求⽐确增量代码Inspection主要以下⼏件事情要做:

1.插件动的候初始化好 PMD规则,并按照类型到不Inspection

2.只分析⽬录src/main/java下的有代码动的Java⽂件,⼩扫描⽂件范围

3.调⽤PMD完成增量⽂件的扫描,获取代码存的问题

4.根据增量代码⾏来控制代码问题的和编辑器中的问题标注显示

初始化等级分类

Kotlin

    1. abstract class PmdBaseInspection : LocalInspectionTool(), PmdInspectionIde ntify {
    2. private val javaPathIdentify: String = "src/main/java"
    3. //据不的规则类型放⼊到不InspectionruleRests
    4. private val ruleRests: RuleSets = run {
    5. listOf(
    6. "java-ali-comment",
    7. "java-ali-concurrent",
    8. "java-ali-constant",
    9. "java-ali-exception",
    10. "java-ali-flowcontrol",
    11. "java-ali-naming",
    12. "java-ali-oop",
    13. "java-ali-orm",
    14. "java-ali-other",
    15. "java-ali-set",

16                    )

17                                  .flatMap { RuleSetFactory().createRuleSet(it).rules }

18                                  .filter { it.priority == filterByPmdRulePriority() }

19                                  .map { RuleSetFactory().createSingleRuleRuleSet(it) }

20                                  .fold(RuleSets()) { rss, rs ->

21                                          rss.addRuleSet(rs)

22                                          rss

23                                  }

24             }

25      }

⾼危问题Inspection

继承PmdBaseInspection重写 filterByPmdRulePriority() ,将所 问题 纳⼊

PdP1IncrInspection中

Kotlin

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

class PmdP1IncrInspection : PmdBaseInspection() {

//过滤危级别规则

override fun filterByPmdRulePriority(): RulePriority { return RulePriority.HIGH

}

// inspection

override fun getStaticDescription(): String { return "Aladdin码规问题"

}

// interception 等级

override fun getDefaultLevel(): HighlightDisplayLevel { return HighlightDisplayLevel.ERROR

}

18

19

20

override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean

): PsiElementVisitor {

// 实现psiElementVisitor,扫描代码,册⾼问题

}

}

议问题 Inspection

继承PmdBaseInspection重写filterByPmdRulePriority()⽅,将所建议问题纳⼊

PdP2IncrInspection 

Kotlin

  1. class PmdP2IncrInspection : PmdBaseInspection() {
  2. // 过滤建议别规则
  3. override fun filterByPmdRulePriority(): RulePriority {
  4. return RulePriority.MEDIUM_HIGH

5             }

6

  1. // inspection
  2. override fun getStaticDescription(): String {
  3. return "Aladdin码规建议修复"

10             }

11

  1. // interception 等级
  2. override fun getDefaultLevel(): HighlightDisplayLevel {
  3. return HighlightDisplayLevel.WARNING

15             }

16

  1. override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean

): PsiElementVisitor {

  1. // 实现psiElementVisitor,扫描代码,册建议问题

19             }

20      }

  • 接下是实现psiElementVisitor完成增量代码的扫描问题渲染了,文具体介绍

3.2实时扫描

3.2.1时扫描原理图

写代码,实对增量/动代码⾏扫描,并对问题的代码标红,并提供快修复功问题解释说

⽬的:在存量代码不动的情况下,希对增量的代码⾏代码问题扫描,防⽌新增代码引⼊代码问题

3.2.2时扫功能及效果图

  • 插件可配置增量代码扫描,并提供实修复建议
  • 插件代码实扫描,实提供问题提示及示

3.2.3码扫描原

分代码扫描⼯具都是对全量代码⾏扫描,但⼀些情况下,如项⽬的存量代码不敢轻易优化,对增量代码⾏扫描,防⽌引⼊新的代码问题,对此我们增加了增量代码扫描的功到不场景获取增量代码⾏的能和扫范围的要求不,我们⾏了⼀些探最终采⽤了以下两种获取增量代码⾏的⽅式:

增量扫描⽅式

扫描频率

范围

要求

获取⽅式

增量代码扫描

⾼,⽂件

⼩,编辑器中当前

⽂件

  1. 增量⽂件全为增量代码⾏
  2. 增量⾏数据 idea帮我们 好了,拿⽤就

次提交增量代码扫描

低,主动触

⼤,git项⽬中动⽂件

插件中集成

                                                                   “

git4idea插件

调⽤git diff获取增量代码⾏

有以

  1. 扫描对当前⽂件的扫描,对实时,应⼩成的实现(采⽤读idea的增量⾏数据)
  2. 主动扫描,扫描范围为整个git项⽬中件和变更的增量代码⾏,需要我们⼰实现(⾥加⼊扫描降低扫描增量代码⾏成

获取件代量⾏

⾸先,我们观idea本身就帮助我们计了增量⾏数据,编辑器⻚⾯就体现,直接试⽤ideaapi :project.getLineStatusManager().getLineStatusTracker 获取使⽤即可,如下

获取项⽬的所有增码⾏

插件中集成 git4idea,直接调⽤ git diff 的⽅式获取增量代码⾏

1class DiffLineContext { companion
2

object {

3

// 存增量代码⾏数据, key:⽂件径,value:增量代码⾏set

4

var DIFF_FILE_VS_NEW_LINES: Map<String, Set<Int>> = mapOf()

5
6

fun analysePatch(project: Project) {

7

ProgressManager.getInstance().runProcessWithProgressSynchronou

sly({
8

//调⽤git diff,析后获得增量代码⾏

9

DIFF_FILE_VS_NEW_LINES = project.getGitRepository()

10

?.root

11

?.let { GitLineHandler(project, it, GitCommand.DIF

F)

}

12

?.also { it.addParameters("HEAD") }

13

?.exec()

14

?.outputAsJoinedString

15

?.let { PatchParseHelper.parsePatch(it) }

16

?.entries

17

?.groupingBy { it.key }

18

?.fold(setOf()) { _, ii -> ii.value.toSet() }

19

?: mapOf()

20

21

}, "Aladdin码规:增量代码分", false, project)

22

}

23

}

24

}

:

1. idea提供了PsiElementVisitor抽象类扩展点visitFile()可完成代码扫描功能

2. ​​​我们实现⾃⼰的PsiElementVisitor,来完成代码问题扫描渲染逻辑

Kotlin

    1. class PmdInspectionVisitor(
    2. private val ruleSets: RuleSets,
    3. private val holder: ProblemsHolder,
    4. private val isOnTheFly: Boolean,
    5. ) : PsiElementVisitor() {
    6. override fun visitFile(psiFile: PsiFile) {
    7. //按规则扫描代码,并获取扫描结果
    8. val violations = processFile(psiFile, ruleSets)
    9. .takeIf { it.isNotEmpty() }
    10. ?: return

11

  1. // isOnTheFly是否是扫描
  2. val newLines = if (isOnTheFly)
  3. // 获取idea好的增量代码⾏
  4. project.getLineStatusManager().getLineStatusTracker(document)
  5. ?.getRanges()
  6. ?.filter { it.line1 != it.line2 }
  7. ?.map { it.line1 + 1..it.line2 }
  8. ?.flatten()
  9. ?.toSet()
  10. ?: setOf()
  11. else
  12. //通过git diff的⽅式获取增量⾏的
  13. psiFile.project.basePath
  14. ?.let { psiFile.virtualFile.path.substringAfter(it) }
  15. ?.let { DiffLineContext.DIFF_FILE_VS_NEW_LINES[it] }
  16. ?.toSet()
  17. ?: setOf()

29

  1. //扫描出的问题始⾏和结束⾏信息,判断是否在增量代码⾏范围
  2. violations.filter {
  3. newLines.contains(it.beginLine)
  4. || newLines.contains(it.endLine)

34                    }

  1. // 调⽤ holder.registerProblem 完成代码问题和渲染
  2. // ......

37             }

38      }

​​​​​​​3.3危组件实时扫描

​​​​​​​3.3.1危组件实时扫描原理图

功能:对新引⼊或已修改⾏依扫描(Aladdin配置件),包括新引⼊件的直接或间接依赖组

⽬的:当前阶段,线在跑危组件的应⽤已全完成理,为了防⽌新的⾼危组件引⼊,增加了增件扫描功

​​​​​​​3.3.2危组件实时扫功能及效果图

  • 危组件实扫描并提供修复建议,仅针对新加或修改
  • 危组配置Aladdin集成系统保持⼀,且时配置⽣效

​​​​​​​3.3.3组件描原

实现组件扫有以

  1. 获取增量件(git show⽐对pom⽂件化)
  2. 获取增量其依赖组件的依idea帮我们分好了,我们直接读idea赖树上的依
  3. 将增量件信息传Aladdin化集成平台进件规扫描并存扫描结果

算增组件

Kotlin

    1. private fun analyse(project: Project, mavenProject: MavenProject) {
    2. // git show HEAD:pomFilePath 获取最近⼀次代码提交信息
    3. val oldDeps: Set<String> = pomFileStatus
    4. .takeIf { it == FileStatus.MODIFIED }
    5. ?.let { project.getGitRepository()?.root }
    6. ?.let { GitLineHandler(project, it, GitCommand.SHOW) }
    7.                       ?.also { it.addParameters("HEAD:${mavenProject.file.path.subst ringAfter("$pjPath/")}") }
    8. ?.exec()
    9. ?.outputAsJoinedString
    10.                       ?.let { pomToDepSet(it) } // 析获取最近⼀次pom⽂件的直接依depen dencies
    11. ?: setOf()

12

  1.        // 获取当前pom⽂件的内容,并解析获取当前pom⽂件的直接dependencies,排除没有发动的依为增量依
  2. val diffDeps: Set<String> = mavenProject.file.contentsToByteArray()
  3. ?.let { String(it) }
  4. ?.let { pomToDepSet(it) }
  5. ?.let { it - oldDeps }
  6. ?.map { it.substringBefore("##") }
  7. ?.toSet()
  8. ?: setOf()

21

  1.        //⾥直接从ideamaven赖树种增量依对应的依包数据,包增量依赖及其间接,丢Aladdin集成平台进规扫描,并存扫描结果
  2. //......

24      }

获取增 组件及其 ⻛险 缓存分析结

IDEA maven项⽬的maven赖树完毕,通过述过程获取到增量依赖后直接从maven赖树获取增量依包下间接依

Kotlin

    1. // 增量件分线问题,存的⽬的,当打开pom⽂件候,需要对问题的增量渲染问题描,如每次都取请求分包,成本较⼤,⾥采⽤每次idea刷新mavenProject去更
    2. val DIFF_DEP_VIOLATION_CACHE = mutableMapOf<String, Map<String, List<Aladd inHttpClient.DepValResponseData>?>>()

3

  1. mavenProject.dependencyTree
  2.                                     .filter { diffDeps.contains("${it.artifact.groupI d}:${it.artifact.artifactId}") }
  3.                                     .filter { !(it?.artifact?.path?.startsWith(pjPath) ?: false) }
  4. .takeIf { it.isNotEmpty() }
  5. ?.mapNotNull { assembleDepDataWrapper(it) }
  6. // 增量发送Aladdin⾏⻛险分
  7. ?.let { AladdinHttpClient.DepValRequest(gitRepo, it) }
  8. ?.query()
  9. ?.filter { it.violations?.isNotEmpty() ?: false }
  10. ?.groupingBy { "${it.groupId}:${it.artifactId}" }
  11.       ?.aggregate<AladdinHttpClient.DepValResponse, String, List<AladdinHttpClient.DepValResponseData>?> { _, _, dep, _ -> dep.violati ons }
  12. ?.ifEmpty { null }
  13. // 增量件⻛险问题放⼊
  14.                                   ?.also { DIFF_DEP_VIOLATION_CACHE[mavenProject.path] = it }
  15. ?: DIFF_DEP_VIOLATION_CACHE.remove(mavenProject.path)

现⻛险组件结果⻚⾯渲染 Inspection

Kotlin

    1. class MavenPomInspection : LocalInspectionTool(), DepInspectionIdentify {
    2. override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean

): PsiElementVisitor {

    1. // pom⽂件径,获取缓存的⻛险件问题
    2.               val violationCache = DiffDependencyContext.DIFF_DEP_VIOLATION_CACH E[pf.virtualFile.path]
    3. //重写visitXmlTagpom⽂件中的标签判断是否是问题件,是根xml

标签dependency中的grouIdartifactId判定的

    1. return object : XmlElementVisitor() {
    2. override fun visitXmlTag(tag: XmlTag?) {
    3. super.visitXmlTag(tag)

9

10                                  val dep = tag as XmlTagImpl

11                                  if (XML_TAG_DEPENDENCY != dep.name) return

12                                  val groupId = dep.findFirstSubTag(XML_TAG_GROUP)?.value?.t ext ?: return

13                                  val artifactId = dep.findFirstSubTag(XML_TAG_ARTIFACT)?.va lue?.text ?: return

14                                  // 存中获取⻛险问题

15                                  val violations = violationCache["${groupId}:${artifactId}"

]

16                                  // idea册问题,并渲染编辑⻚⾯

17                                  holder.registerProblem(dep,

18                                                 violations.mapNotNull { it.failMessage }.reduce { acc, s -> acc + "\n\n" + s },

19                                              ProblemHighlightType.ERROR)

20                           }

21                    }

22             }

23      }

​​​​​​​3.4提交动扫描

​​​​​​​3.4.1提交拦截扫描原理图

功能: 代码push时若增量⽂件存Block等级问题将被拦截提醒,并提示编/件依/代码规约等问题数量,可查看详情

⽬的: 希对增量代码译问题/件依问题/代码问题扫描拦截说,防⽌⾼问题遗漏

​​​​​​​3.4.2提交拦截核功能及效果图

  •   代码提交前,插件会⾏⼀次增量代码扫描,依据Aladdin站点的开关⾏提醒拦截,以代体验清晰Aladdin拦截
  •   在违规代码,插件会提问题类型和个数,其中包括:语错误问题,P3C问题,⾼⻛险件问题,看详情效可在问题列表中看具体问题,对应,代码⾏,以修复建议

4.插户安使

⾃插件上线,研中⼼整体效⽤户安装量覆盖100%

代码问题拦截:Gitlab代码准⼊拦截插件拦截换情况如下

周统计,随着插件⽤户使⽤⼈数增多,插件拦截问题数步增⻓,Gitlab代码准⼊拦截问题数步降低,代码问题拦截转移到代码开阶段

5.总结&展望

Aladdin研插件提供了⼀码规范和最佳实动检代码中潜在问题并出警或错误提使⽤插件以下好处 :

  • ⼀代码规:基于Alibaba码规范条款,中⼼件使⽤规提供⼀套认码规
  •    置发潜在问题:插件代码中潜在问题,助于提⾼代码和稳定性,将量问题拦截左,降低拦截打的成
  • 范编码习惯:插件提供很多实⽤的代码提示和建议,从写出健壮的代码
  • 通过扫描新引⼊依包:拦截⻛险件,防⽌引⼊新的⻛险

在未来的规划中,我们将继续对插件升级

  1. 定制化代码规,如定制化⽇志规范等步优化完适合我们⼰的⼀套规
  2. 增加本地的增量试覆盖率功,实现增量代码覆盖率分析等

介绍

  • swj,信也后端专家,主要负责基⽯
  • zhx,信也后端专家,主要负责aladdinrubiktcm
  • 21
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值