Groovy脚本execute的waitFor在Jenkins中挂起,命令行运行正常求助
Hey there! I’ve run into this exact issue before—your script works fine in the command line but hangs on waitFor() in Jenkins, right? Let’s break down why this happens and how to fix it.
The Root Cause
When you launch a subprocess with execute() in Groovy, the child process (your svn export command) writes output to stdout and stderr. These streams have limited buffer sizes. If the subprocess generates enough output to fill these buffers, it will stop executing and wait for the parent process (your Jenkins Groovy script) to read the buffered data.
In your current code, you’re calling proc.waitFor() first—this means your script is waiting for the subprocess to finish, but the subprocess is stuck waiting for your script to read its output. That’s a classic deadlock scenario!
Fix 1: Use Groovy’s consumeProcessOutput (Simplest Solution)
Groovy has a built-in method consumeProcessOutput() that spins up background threads to read stdout and stderr as they’re generated. This prevents the buffers from filling up and blocking the subprocess. Here’s how to modify your code:
//checkoutObjects def command ="" for (i = 0; i < numOfObj; i++) { command = "svn export -r ${DeploySetArray[i][1]} ${DeploySetArray[i][0]} D:/Jenkins/workspace/mmb.database.deploy" def proc = command.execute() // Capture output and error in buffers using background threads def stdoutBuffer = new StringBuilder() def stderrBuffer = new StringBuilder() proc.consumeProcessOutput(stdoutBuffer, stderrBuffer) // Now wait for the process to finish safely proc.waitFor() println "Process exit code: ${proc.exitValue()}" println "Std Err: ${stderrBuffer.toString()}" println "Std Out: ${stdoutBuffer.toString()}" }
Fix 2: Manual Threaded Output Reading
If you prefer more control over how output is handled, you can start separate threads to read stdout and stderr before calling waitFor():
//checkoutObjects def command ="" for (i = 0; i < numOfObj; i++) { command = "svn export -r ${DeploySetArray[i][1]} ${DeploySetArray[i][0]} D:/Jenkins/workspace/mmb.database.deploy" def proc = command.execute() // Start threads to read output immediately def stdoutThread = Thread.start { proc.in.eachLine { line -> println "Std Out: $line" } } def stderrThread = Thread.start { proc.err.eachLine { line -> println "Std Err: $line" } } // Wait for the process and threads to finish proc.waitFor() stdoutThread.join() stderrThread.join() println "Process exit code: ${proc.exitValue()}" }
Bonus: Use Jenkins Native SVN Steps (Most Reliable)
Instead of rolling your own svn export command in Groovy, consider using Jenkins’ built-in SVN plugin steps. These are designed to handle output buffering and integrate better with Jenkins’ logging and job status. Here’s an example:
for (i = 0; i < numOfObj; i++) { def svnUrl = DeploySetArray[i][0] def revision = DeploySetArray[i][1] def targetDir = "D:/Jenkins/workspace/mmb.database.deploy" svn( url: svnUrl, revision: revision, checkoutDir: targetDir, additionalArgs: "--export" // Use export instead of checkout ) }
Just make sure you have the Jenkins SVN plugin installed and configured properly for your environment.
内容的提问来源于stack exchange,提问作者Matt Kimball

