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

如何在Jenkins/Groovy中实现多行正则匹配提取变更日志?

Jenkins中提取CHANGELOG指定版本内容的问题与解决方法

问题背景

本地Groovy代码可正常读取CHANGELOG.md并提取指定版本(如0.1.0)的变更日志,但在Jenkins中因安全限制无法使用java.nio.file和Pattern类,修改为Jenkins友好代码后,始终匹配失败,提示Could not parse latest changelog.,同时面临本地调试困难的问题。


一、本地使用Jenkins库调试方法

可以借助jenkins-pipeline-unit框架模拟Jenkins环境,在本地调试Pipeline代码:

  • 添加依赖:在项目构建文件(如build.gradle或pom.xml)中引入jenkins-pipeline-unit依赖
  • 编写测试用例:
    1. 模拟Jenkins的readFile方法,加载本地的CHANGELOG.md文件
    2. 复制Jenkins中使用的代码逻辑到测试类
    3. 运行测试验证正则匹配、字符串处理等逻辑是否正常
  • 示例测试代码片段:
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.Before
import org.junit.Test

class ChangelogTest extends BasePipelineTest {
    @Before
    void setUp() {
        super.setUp()
        helper.registerAllowedMethod('readFile', [Map], { map ->
            new File(map.file).text
        })
    }

    @Test
    void testExtractChangelog() {
        def currentTag = '0.1.0'
        def changelogText = readFile(file: 'CHANGELOG.md')
        def pattern = "(?s)(?m)^## ${currentTag}.*?(?=\n\n## |\\z)"
        def matcher = (changelogText =~ pattern)
        assert matcher.find() : "Failed to find tag ${currentTag}"
        println(matcher.group().strip())
    }
}

二、修复Jenkins中的正则匹配问题

原代码失败的核心原因是:Groovy的=~操作符默认不启用多行模式(MULTILINE),正则中的^只会匹配整个字符串的开头,无法匹配每行的开头。解决方法是在正则中添加内联模式修饰符(?m):

修正后的代码

def changelogFileName = 'CHANGELOG.md'
def currentTag = '0.1.0'

def changelogText = readFile(file: changelogFileName)
// (?m)启用多行模式,(?s)启用单行模式(让.匹配换行符)
def pattern = "(?s)(?m)^## ${currentTag}.*?(?=\n\n## |\\z)"
def matcher = (changelogText =~ pattern)

if (matcher.find()) {
    def latestChangelog = matcher.group().strip()
    println(latestChangelog)
} else {
    println('Could not parse latest changelog.')
}

关键说明

  • (?m):等价于Pattern.MULTILINE,让^匹配每行的开头
  • (?s):等价于Pattern.DOTALL,让.匹配包括换行符在内的所有字符,确保能跨行匹配版本下的所有变更内容

三、其他可行替代方案

如果正则匹配仍有问题,可尝试更直观的逐行解析方案:

方案1:逐行遍历解析CHANGELOG

def changelogFileName = 'CHANGELOG.md'
def currentTag = '0.1.0'

def changelogLines = readFile(file: changelogFileName).split('\n')
def targetSection = []
boolean isInTargetSection = false

changelogLines.each { line ->
    def trimmedLine = line.trim()
    if (trimmedLine.startsWith("## ${currentTag}")) {
        isInTargetSection = true
        targetSection.add(line)
    } else if (isInTargetSection && trimmedLine.startsWith('## ')) {
        // 遇到下一个版本,停止收集
        isInTargetSection = false
    } else if (isInTargetSection) {
        targetSection.add(line)
    }
}

if (!targetSection.isEmpty()) {
    println(targetSection.join('\n').strip())
} else {
    println('Could not parse latest changelog.')
}

该方法逻辑清晰,避免正则模式的坑,更容易调试和维护。

方案2:使用Jenkins插件简化操作

如果已安装Pipeline Utility Steps插件,可直接使用插件提供的readChangelog方法(需确认插件版本支持),无需手动编写解析逻辑。


内容的提问来源于stack exchange,提问作者Michael van de Waeter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:42:00