Groovy/Jenkins如何将sh调用curl的逻辑重构为原生URL请求
问题根源
你遇到的java.io.NotSerializableException报错和字符串转换无关,是Jenkins流水线的CPS执行引擎的特性导致的:
Jenkins默认会将流水线执行过程中的所有中间变量序列化存储,用于支持流水线暂停、断点续跑等能力。你代码中创建的HttpsURLConnectionImpl对象(也就是req变量)本身属于不可序列化的JDK内置类,只要它存在于流水线步骤的作用域内,就会触发序列化异常。
正确实现方案
方案1:使用@NonCPS注解屏蔽序列化检查
你可以将纯Groovy原生的HTTP请求逻辑封装到标注了@NonCPS的方法中,Jenkins对@NonCPS标注的方法不会做CPS转换和序列化检查,方法内的临时变量不会被持久化,就可以规避序列化报错。
注意:@NonCPS方法内不能调用任何Jenkins流水线原生步骤(比如withCredentials、sh等),需要把依赖的参数从外部传入。
重构后的代码如下:
import groovy.json.JsonOutput // 标注@NonCPS的纯HTTP请求方法 @NonCPS def sendBitbucketBuildStatus(String url, String authToken, Map bodyData) { def req = new URL(url).openConnection() req.with { requestMethod = "POST" doOutput = true setRequestProperty("Content-Type", "application/json") setRequestProperty("Authorization", "Bearer ${authToken}") def msg = JsonOutput.toJson(bodyData).getBytes("UTF-8") outputStream.write(msg) return responseCode } } def notifyBitbucketBuildStatus(cred_id, url_base, commit, build_state, build_info_name, build_info_number, viz_url, log) { def rest_path = "rest/build-status/1.0/commits" def dict = [:] dict.state = build_state dict.key = "${build_info_name}_${build_info_number}" dict.url = viz_url def fullUrl = "${url_base}/${rest_path}/${commit}" // 流水线步骤放在@NonCPS方法外部执行 withCredentials([string(credentialsId: cred_id, variable: 'auth_token')]) { def respCode = sendBitbucketBuildStatus(fullUrl, auth_token, dict) if (200 != respCode) { log.warn("Failed updating build status with Bitbucket") } } }
方案2:使用Jenkins httpRequest插件(更推荐)
如果你的Jenkins环境安装了HTTP Request插件,直接用插件提供的httpRequest方法即可,它原生适配Jenkins流水线的序列化机制,不需要处理低层级的连接逻辑,代码更简洁:
import groovy.json.JsonOutput def notifyBitbucketBuildStatus(cred_id, url_base, commit, build_state, build_info_name, build_info_number, viz_url, log) { def rest_path = "rest/build-status/1.0/commits" def dict = [:] dict.state = build_state dict.key = "${build_info_name}_${build_info_number}" dict.url = viz_url def fullUrl = "${url_base}/${rest_path}/${commit}" def reqBody = JsonOutput.toJson(dict) withCredentials([string(credentialsId: cred_id, variable: 'auth_token')]) { def response = httpRequest url: fullUrl, httpMode: 'POST', contentType: 'APPLICATION_JSON', customHeaders: [[name: 'Authorization', value: "Bearer ${auth_token}"]], requestBody: reqBody, failOnError: false if (200 != response.status) { log.warn("Failed updating build status with Bitbucket") } } }
内容的提问来源于stack exchange,提问作者StoneThrow
相关产品推荐
相关产品推荐

