Bitbucket Scriptrunner预接收钩子异常排查求助
推送时终端收到错误提示:
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

