You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何借助Bitbucket实现Pull Request的增量代码覆盖率检测

为Bitbucket PR实现变更代码覆盖率检测与阈值拦截

以下是在Jenkins多分支流水线中实现需求的具体步骤:

1. 获取PR中变更的Java文件

首先需要拿到当前PR分支与目标分支(如main)的差异Java文件,用Git命令即可实现:

# 利用Jenkins自动注入的CHANGE_TARGET变量获取目标分支,过滤出Java文件
git diff origin/${CHANGE_TARGET:-main} --name-only | grep "\.java$" > changed_files.txt

如果PR没有变更Java文件,创建空文件避免后续流程报错:

[ -s changed_files.txt ] || echo "" > changed_files.txt

2. 让JaCoCo仅针对变更文件生成覆盖率报告

JaCoCo支持通过includes参数指定要检测的文件路径,结合Maven/Gradle动态传入变更文件列表:

Maven 配置

在Jenkins流水线中读取变更文件列表,转成JaCoCo支持的格式后传入构建命令:

def changedFiles = readFile('changed_files.txt').split('\n').collect { "**/${it}" }.join(',')
sh "./mvnw clean test jacoco:report -Djacoco.includes=${changedFiles}"

Gradle 配置

先在build.gradle中添加JaCoCo任务的参数支持:

jacocoTestReport {
    dependsOn test
    reports {
        xml.required = true // 必须生成XML报告用于后续解析
        html.required = true
    }
    // 读取流水线传入的includes参数
    if (project.hasProperty('jacoco.includes')) {
        includes = project.property('jacoco.includes').split(',')
    }
}

然后在Jenkins流水线中执行构建:

def changedFiles = readFile('changed_files.txt').split('\n').collect { "**/${it}" }.join(',')
sh "./gradlew test jacocoTestReport -Pjacoco.includes=${changedFiles}"

3. 解析覆盖率并校验阈值

生成XML报告后,用Groovy脚本解析变更文件的覆盖率数据,对比设定的阈值:

def jacocoXml = new XmlSlurper().parse(new File('target/site/jacoco/jacoco.xml')) // Maven路径,Gradle为build/reports/jacoco/test/jacocoTestReport.xml
def totalLines = 0
def coveredLines = 0

readFile('changed_files.txt').split('\n').each { filePath ->
    // 将文件路径转为类全限定名
    def className = filePath.replace('.java', '').replace('/', '.')
    def classNode = jacocoXml.classes.class.find { it.@name == className }
    if (classNode) {
        def lineCounter = classNode.counter.find { it.@type == 'LINE' }
        totalLines += lineCounter.@total.toInteger()
        coveredLines += lineCounter.@covered.toInteger()
    }
}

def coverageRate = totalLines > 0 ? (coveredLines / totalLines) * 100 : 100
echo "变更代码覆盖率: ${String.format('%.2f', coverageRate)}%"

// 设定最低覆盖率阈值,比如80%
def minCoverage = 80
if (coverageRate < minCoverage) {
    error "变更代码覆盖率 ${String.format('%.2f', coverageRate)}% 低于阈值 ${minCoverage}%,构建终止"
}

4. 完整Jenkins流水线示例

把上述步骤整合到Jenkinsfile中,适配PR触发场景:

pipeline {
    agent any
    environment {
        MIN_COVERAGE = 80 // 可根据需求调整阈值
    }
    stages {
        stage('拉取代码') {
            steps {
                checkout scm
            }
        }
        stage('获取变更Java文件') {
            steps {
                sh """
                    git diff origin/${CHANGE_TARGET:-main} --name-only | grep "\\.java$" > changed_files.txt
                    [ -s changed_files.txt ] || echo "" > changed_files.txt
                """
            }
        }
        stage('构建并生成覆盖率') {
            steps {
                script {
                    def changedFiles = readFile('changed_files.txt').split('\n').collect { "**/${it}" }.join(',')
                    if (changedFiles) {
                        // 这里替换为你的构建命令,Maven或Gradle
                        sh "./mvnw clean test jacoco:report -Djacoco.includes=${changedFiles}"
                    } else {
                        echo "无变更Java文件,跳过覆盖率生成"
                    }
                }
            }
        }
        stage('校验覆盖率阈值') {
            steps {
                script {
                    def changedFiles = readFile('changed_files.txt').split('\n')
                    if (changedFiles && changedFiles[0]) {
                        // 根据构建工具调整XML路径
                        def jacocoXmlPath = 'target/site/jacoco/jacoco.xml' 
                        def jacocoXml = new XmlSlurper().parse(new File(jacocoXmlPath))
                        def totalLines = 0
                        def coveredLines = 0

                        changedFiles.each { filePath ->
                            def className = filePath.replace('.java', '').replace('/', '.')
                            def classNode = jacocoXml.classes.class.find { it.@name == className }
                            if (classNode) {
                                def lineCounter = classNode.counter.find { it.@type == 'LINE' }
                                totalLines += lineCounter.@total.toInteger()
                                coveredLines += lineCounter.@covered.toInteger()
                            }
                        }

                        def coverageRate = totalLines > 0 ? (coveredLines / totalLines) * 100 : 100
                        echo "变更代码覆盖率: ${String.format('%.2f', coverageRate)}%"

                        if (coverageRate < MIN_COVERAGE.toInteger()) {
                            error "变更代码覆盖率未达标: ${String.format('%.2f', coverageRate)}% < ${MIN_COVERAGE}%"
                        }
                    } else {
                        echo "无变更Java文件,跳过阈值校验"
                    }
                }
            }
        }
    }
    post {
        always {
            // 可选:在Jenkins中展示全量覆盖率报告
            jacoco()
        }
    }
}

关键注意点

  • 确保已安装Jenkins Bitbucket分支源插件,它会自动注入CHANGE_TARGET等PR相关环境变量。
  • 如果需要排除测试类,可在grep命令中添加-v "Test.java$"过滤。
  • Gradle的JaCoCo XML报告路径默认是build/reports/jacoco/test/jacocoTestReport.xml,需要根据实际情况调整。

内容的提问来源于stack exchange,提问作者Shubham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 01:32:19