Using a temp file is an ugly workaround which has at least these obvious disadvantages: 1. You never have 100% guarantee that you don't break parallelism. Every time you introduce one more parallel "axe" (such as release/debug conf, branch, os whatever) you must take care that the temp file has the corresponding suffix. 2. This method won't work in some cases if a command already contains output redirection. For example, on Linux this works: rm 123 > temp.txt 2>&1. I am not sure if we can do the same on Windows. There may be more complex cases with tricky double/single quote combinations and multiple output redirections, of a command which consists of several commands, semicolon separated. At the end we always loose generality and platform-independency if we use a temp file. 3. You must remove the temp file after the command execution, but how do you do it on the node if the pipeline stuff is executed on master? You cannot use Java method File.createTempFile() for this, because it creates the temp file on master. It means that the code which generates the file name must run on master and then you pass this name to the "sh" or "bat" when you remove it. Distinguishing to "sh" and "bat" is also losing of platform-independency by the way. My current solution with which tried looks like this:
def getTempDirOnNode() {
if (isUnix()) {
return env.TMPDIR != null ? env.TMPDIR : '/tmp'
} else {
return env.TEMP
}
}
/* May not work if "cmd" already contains output redirection or more complex shell syntax. */
def runCmdOnNodeSavingExitCodeAndStdout(cmd) {
def rc = 0
def stdout = null
print("Using temp directory: " + getTempDirOnNode())
def tempFileName = 'runIgnoreExitCodeStdout_' + UUID.randomUUID() + '.txt'
def tempFilePath = getTempDirNode() + getOSPathSep() + tempFileName
def tempFileHandle = new File(tempFilePath)
print("Using temp file: " + tempFilePath)
if (isUnix()) {
rc = sh(script: cmd + ' > ' + tempFilePath, returnStatus: true)
} else {
rc = bat(script: cmd + ' > ' + tempFilePath, returnStatus: true);
}
stdout = readFile(tempFilePath).trim()
// Delete temporary file from the node
if (isUnix()) {
sh(script: 'rm -f ' + tempFilePath, returnStatus: true)
} else {
bat(script: 'del /q ' + tempFilePath, returnStatus: true);
}
return [ rc, stdout ]
}
This workaround looks super ugly and of course I would want just say response = sh(cmd); response.getStatus() etc. without the tones of lines. I vote +1 for this feature. It is such a basic thing that I am surprised what else can be discussed here. just must be. If pipeline maintainers refuse to implement it we will probably need to write a plugin. I tried to implement a Java function which executes another arbitrary Java code of the given node, but it does not work directly from the pipeline script due to another limitation (see Pipeline scripts fails to call Java code on slave: Failed to deserialize the Callable object, however from a plugin it must work. But I still hope this feature will be implemented, because it is in high demand and I don't see any reason to not have it. |