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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 05:54:03