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

Groovy writeFile触发NotSerializableException: java.lang.ProcessImpl求助

Jenkins共享库序列化异常原因及解决办法

异常原因

Jenkins流水线依赖序列化/反序列化实现断点续跑与分布式执行,所有在流水线流程中留存的对象必须实现Serializable接口。你通过commandToRun.execute()获取的Process对象(底层由ProcessImpl实现)本身不可序列化,当代码执行到writeFile步骤时,Jenkins需要序列化当前流水线上下文,这个不可序列化的Process对象就会触发NotSerializableException。

解决办法

1. 提取结果后立即丢弃Process对象

不要让Process对象留在流水线上下文里,执行命令后立刻提取所需的stdout、stderr和状态码,转为字符串或基本类型,让Process对象被垃圾回收:

def runCommand(String commandToRun) {
    def process = commandToRun.execute()
    def stdout = process.in.text
    def stderr = process.err.text
    process.waitFor()
    def exitCode = process.exitValue()
    // 仅返回可序列化的结果,不返回Process对象
    return [stdout: stdout, stderr: stderr, exitCode: exitCode]
}

// 调用示例
def cmdResult = runCommand(commandToRun)
writeFile file: 'command_output.log', text: """
STDOUT: ${cmdResult.stdout}
STDERR: ${cmdResult.stderr}
EXIT CODE: ${cmdResult.exitCode}
"""

2. 规范使用@NonCPS注解

之前用@NonCPS出现警告,大概率是因为注解方法内调用了Jenkins流水线步骤(比如writeFile),或让不可序列化对象流出方法。正确用法是将Process相关逻辑完全封装在@NonCPS方法内,仅返回可序列化的结果:

@NonCPS
def runCommandNonCPS(String commandToRun) {
    def process = commandToRun.execute()
    def stdout = process.in.text
    def stderr = process.err.text
    process.waitFor()
    def exitCode = process.exitValue()
    // 返回可序列化的Map对象
    return [stdout: stdout, stderr: stderr, exitCode: exitCode]
}

// 流水线主逻辑中调用
def cmdResult = runCommandNonCPS(commandToRun)
writeFile file: 'command_output.log', text: """
STDOUT: ${cmdResult.stdout}
STDERR: ${cmdResult.stderr}
EXIT CODE: ${cmdResult.exitCode}
"""

这样@NonCPS方法内的不可序列化对象不会进入流水线上下文,警告会自动消失。

3. 改用Jenkins原生sh步骤(推荐)

你之前尝试的sh步骤其实可以直接获取输出和状态码,无需手动调用Process。这种方法完全符合Jenkins序列化要求,是最稳妥的方案:

方式一:分离捕获stdout和状态码

def runCommandWithSh(String commandToRun) {
    def shResult = sh(
        script: commandToRun,
        returnStdout: true,
        returnStatus: true
    )
    return [
        stdout: shResult.stdout.trim(),
        exitCode: shResult.status
    ]
}

方式二:同时捕获stdout和stderr

通过临时文件存储stderr:

def runCommandWithSh(String commandToRun) {
    def stderrTempFile = "stderr_${UUID.randomUUID()}.log"
    def shResult = sh(
        script: "${commandToRun} 2>${stderrTempFile}",
        returnStdout: true,
        returnStatus: true
    )
    def stderr = readFile(stderrTempFile).trim()
    sh "rm -f ${stderrTempFile}" // 清理临时文件
    return [
        stdout: shResult.stdout.trim(),
        stderr: stderr,
        exitCode: shResult.status
    ]
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 15:13:37