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

Bitbucket Scriptrunner预接收钩子异常排查求助

问题:Bitbucket Scriptrunner预接收钩子验证Conanfile.py失败但钩子显示正常

推送时终端收到错误提示:

Total 13 (delta 8), reused 0 (delta 0), pack-reused 0
remote: Push blocked
remote: Conanfile.py lines starting with "self.requires" should match regex
To https://bitbucket.ecatools.io/scm/ttdo/test_branch.git
! [remote rejected] testscript -> testscript (pre-receive hook declined)

但Scriptrunner后台显示钩子运行正常,以下是预接收钩子代码(省略导入语句):

@ShortTextInput(label = 'regex', description = 'Regex expression used to verify file content')
String regexValue
def pattern = Pattern.compile(".*requires\(\"[^\d|_|\-](\S)+/\[(\d)(\.\d)*\]@(xxx|xxxxx)/stable\"")

def commitService = ComponentLocator.getComponent(CommitService)
def contentService = ComponentLocator.getComponent(ContentService)

def commits = refChanges.getCommits(repository) as List<Commit>

// filePaths to commit Id, if file was modified in multiple commits, take the last commit id
def filePaths = [:] as Map<String, String>
commits.reverseEach { commit ->
    def changesRequest = new ChangesRequest.Builder(repository, commit.id).build()
    commitService.streamChanges(changesRequest) { Change change ->
        if (change.path.toString().endsWith("conanfile.py")) {
            filePaths[change.path.toString()] = commit.id
        }
        true
    }
}

def result = filePaths.keySet().collect { filePath ->
    def fileStream = new ByteArrayOutputStream()
    contentService.streamFile(repository, filePaths[filePath], filePath) {
        fileStream
    }
    fileStream.toString()
}.every {
    it.split("\n").any { line ->
        line.startsWith("self.requires") && pattern.matcher(line).find()
    }
}

result ? RepositoryHookResult.accepted('push approved') : RepositoryHookResult.rejected('Push blocked', 'Conanfile.py lines starting with "self.requires" should match regex')
排查与修复方案

1. 正则表达式转义错误

Groovy双引号字符串会解析转义字符,导致你的正则未正确转义(、[等特殊字符,无法匹配目标格式;同时字符类中的|是多余的,会被当作普通字符排除。

修正后的正则(推荐单引号写法避免双重转义):

def pattern = Pattern.compile('.*requires\\("[^\\d_\\-]\\S+/\\[(\\d)(\\.\\d)*\\]@(xxx|xxxxx)/stable"')

另外你定义了regexValue参数但未使用,建议改为从配置读取正则,方便后续修改:

def pattern = Pattern.compile(regexValue)

2. 文件内容读取逻辑错误

原代码中contentService.streamFile的回调仅返回fileStream,未实际将输入流写入输出流,导致读取到的文件内容为空,触发验证失败。

修复读取逻辑:

contentService.streamFile(repository, filePaths[filePath], filePath) { inputStream ->
    fileStream << inputStream
}

3. 验证逻辑缺陷

原逻辑every + any要求每个Conanfile.py至少有一行符合正则,但如果文件中没有self.requires行、或存在不符合正则的self.requires行,都会触发拒绝。正确逻辑应该是所有self.requires行都符合正则,无不符合的行即可通过。

修正验证逻辑:

def result = filePaths.keySet().collect { filePath ->
    def fileStream = new ByteArrayOutputStream()
    contentService.streamFile(repository, filePaths[filePath], filePath) { inputStream ->
        fileStream << inputStream
    }
    fileStream.toString()
}.every { content ->
    // 兼容Windows/Linux换行符
    content.split(/\r?\n/).every { line ->
        // 要么不是self.requires开头,要么符合正则
        !line.startsWith("self.requires") || pattern.matcher(line).find()
    }
}

4. 额外调试建议

可以在钩子中添加日志输出,确认读取的文件内容和正则匹配结果,方便定位问题:

// 读取文件后添加日志
def content = fileStream.toString()
log.debug("读取文件内容:{}", content)
// 匹配时添加日志
def matchResult = pattern.matcher(line).find()
log.debug("行{}匹配结果:{}", line, matchResult)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 10:25:21