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

Jenkins Groovy执行curl调用GitHub API时转义失效报错问题

Jenkins流水线调用GitHub API设置PR标签转义异常修复

问题场景

在Jenkins流水线Groovy脚本中通过curl调用GitHub API设置PR标签,代码如下:

def res = null

withCredentials([string(credentialsId: 'my-github-token', variable: 'GITAPITOKEN')]) {
    withEnv(["REPO=${repo}", "PRID=${prId}", "LABEL=${label}"]) {
        res = sh (script: 'curl -X PUT -H \"Authorization: token $GITAPITOKEN\" -d \"{\\\"labels\\\":[\\\"$LABEL\\\"]}\" https://api.github.com/repos/my-user/$REPO/issues/$PRID/labels', returnStdout: true).trim()
    }
}

println("${res}")

Jenkins构建日志打印的执行命令如下:

curl -X PUT -H "Authorization: token ****" -d "{\"labels\":[\"my-label\"]}" https://api.github.com/repos/my-user/my-repo/issues/1/labels

该命令格式在本地执行可正常运行,但在Jenkins中执行时报错:

curl: (6) Could not resolve host: token
curl: (6) Could not resolve host: ****"
curl: (3) unmatched close brace/bracket in URL position:
my-label\"]}"
          ^

同时GitHub接口返回404响应:

{
"message": "Not Found",
"documentation_url": "https://docs.github.com/rest/reference/issues#set-labels-for-an-issue"
}

根因分析

  • 核心问题是Groovy语法、Jenkins sh步骤、Shell解释器三层的转义规则不匹配:Groovy单引号字符串中编写的多层反斜杠转义,经过逐层解析后,没有生成符合Shell预期的参数字符串。
  • 从报错可反推实际传给curl的参数被空格错误拆分:-H后的请求头参数被拆成"Authorization:、token、****"三个独立段,curl默认将无标识符的段作为请求目标地址,因此抛出主机解析失败错误;同理-d后的JSON参数也被错误拆分,触发括号不匹配的语法报错。
  • Jenkins构建日志打印的命令是经过转义渲染后的可视化结果,和实际传入Shell执行的原始字符串存在差异,不能直接以日志显示的格式作为转义正确的判断依据。
  • 接口返回Not Found是参数解析错误的附带结果:请求未正确携带鉴权头、请求体残缺,最终发送到不符合预期的路径,触发404响应。

修复方案

优先选择规避多层转义的写法,从根源上避免转义冲突:

方案1:优化curl参数写法,减少转义层级

使用Groovy三单引号定义Shell脚本(Groovy三单引号字符串不会对内部的$、反斜杠做额外解析,仅需遵循Shell本身的转义规则),同时Shell层用单引号包裹JSON结构,仅通过字符串拼接插入动态变量,完全消除多层转义需求:

def res = null
withCredentials([string(credentialsId: 'my-github-token', variable: 'GITAPITOKEN')]) {
    withEnv(["REPO=${repo}", "PRID=${prId}", "LABEL=${label}"]) {
        res = sh (
            script: '''
                curl -s -X PUT \
                -H "Authorization: token $GITAPITOKEN" \
                -H "Accept: application/vnd.github.v3+json" \
                -d '{"labels":["'"$LABEL"'"]}' \
                "https://api.github.com/repos/my-user/$REPO/issues/$PRID/labels"
            ''',
            returnStdout: true
        ).trim()
    }
}
println("${res}")

说明:添加-s参数屏蔽curl无关的进度输出,添加GitHub API要求的版本请求头避免兼容问题。

方案2:绕开Shell,直接用Groovy原生方法发请求

完全不依赖curl命令,使用Groovy原生HTTP客户端发起请求,彻底避免Shell层转义问题:

import groovy.json.JsonOutput
def res = null
withCredentials([string(credentialsId: 'my-github-token', variable: 'GITAPITOKEN')]) {
    // 自动构造JSON请求体,无需手动拼接转义
    def reqBody = JsonOutput.toJson([labels: [label]])
    def conn = new URL("https://api.github.com/repos/my-user/${repo}/issues/${prId}/labels").openConnection()
    conn.setRequestMethod("PUT")
    conn.setRequestProperty("Authorization", "token ${GITAPITOKEN}")
    conn.setRequestProperty("Accept", "application/vnd.github.v3+json")
    conn.setDoOutput(true)
    conn.getOutputStream().write(reqBody.getBytes("UTF-8"))
    res = conn.getInputStream().getText("UTF-8").trim()
}
println(res)

验证注意事项

  • 修复后首先确认执行日志中不再出现主机解析、括号不匹配的curl语法错误
  • 提前确认使用的GitHub Token具备目标仓库的Issue/PR写权限,无权限也会导致接口返回404
  • 确认传入的仓库名、PR ID和实际仓库信息一致,路径错误同样会触发404

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 23:33:30