2

Im trying to copy the resulting war file from one build directory to another using Jenkins pipeline script(groovy). I have tested the find/exec/cp command on the system itself as the jenkins user, from the same workspace as the script runs and it works fine (direct copy paste from console out).

String buildNumber = env.BUILD_NUMBER

def sout = new StringBuffer()
def serr = new StringBuffer()

//Create package directory in jenkins job folder
def packageDir = "${env.JENKINS_HOME}/jobs/Package_Deploy_Pipeline/builds/${buildNumber}/package/"
def command = "mkdir ${packageDir}"
def proc = command.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(3000)
println "out> $sout err> $serr"

command = "find ${env.JENKINS_HOME}/jobs/myJob/builds/lastSuccessfulBuild/archive/build/libs/ -name *.war -exec cp {} ${packageDir} \\;"
println command
proc = command.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(3000)
println "out> $sout err> $serr"

The error seen in Console Output is:

[Pipeline] echo
out>  err> 
[Pipeline] echo
find /var/lib/jenkins/jobs/myJob/builds/lastSuccessfulBuild/archive/build/libs/ -name *.war -exec cp {} /var/lib/jenkins/jobs/Package_Deploy_Pipeline/builds/23/package/ \;
[Pipeline] echo
out>  err> find: missing argument to `-exec'

Edit: I have also tried "*.war", '*.war', \; and ';'

luka5z
  • 7,525
  • 6
  • 29
  • 52
Stephen Nichols
  • 2,363
  • 5
  • 14
  • 19
  • Are there `.war` files in the current directory? Such that `*.war` might be getting globbed by the shell? Does quoting that argument help? (I wouldn't expect that to be the problem here but it might be and it is a potential problem that you should fix anyway.) (What's expanding `${env.JENKINS_HOME}` in that command?) Does escaping the `;` again help? – Etan Reisner May 04 '16 at 17:56
  • The only .war is in the cp source directory. I did try single quoting the '*.war' and got the same results. ${env.JENKINS_HOME} expands to /var/lib/jenkins/ and i know its correct from the Console Output. Im not sure what you mean by escaping again, can you elaborate? – Stephen Nichols May 04 '16 at 18:06
  • This is unrelated to the issue in general but `env.JENKINS_HOME` isn't a valid shell variable. The shell can't be expanding that. So what is doing that? And by escaping it again I meant perhaps whatever is executing the command is removing the backslash from `\;` and you should try to get `\\;` into the command string as a test. – Etan Reisner May 04 '16 at 18:09
  • Ah. the code above is groovy code. so the ${} is getting expanded in the groovy code. What is displayed in the Console Output that I posted is the actual shell command being recieved. Thats what I verified manually on the server. – Stephen Nichols May 04 '16 at 18:12
  • If you run that printed command on the server manually does it work? Because it looks fine. – Etan Reisner May 04 '16 at 18:15
  • Yup, logged in as the same user the script executes as from the same directory. Thats why im asking, Im stumped. – Stephen Nichols May 04 '16 at 18:16
  • Did you use `bash` for your tests? Does the build use `/bin/sh` by chance? Does trying it under `/bin/sh` change anything? `/bin/sh -c 'command'` or `/bin/sh -c "command"`? – Etan Reisner May 04 '16 at 18:20
  • Im not sure what groovy uses by default but both sh and bash worked on the system with the same command. – Stephen Nichols May 04 '16 at 18:39
  • If you try running `command="echo foo \\; echo bar"` and `command.execute()` with groovy like this what do you get as output? Then try with `set -x; echo ....` perhaps? – Etan Reisner May 04 '16 at 19:42

3 Answers3

3

Jenkins with a Declarative Pipeline only worked accordingly for me:

sh 'find . -ipath "*src/header.h" -exec rm {} ";"'

krissy
  • 81
  • 1
  • 5
0

Groovy

The problem for the error you are experiencing might be related with \; and the fact that you are escaping as for bash/sh command, when in fact you are not using bash/sh, but running find executable.

The same approach needs to be applied to -name parameter, where quoting should be dropped from search pattern.

Example:

command = "find src -name *.txt -exec cp {} dst/ ;"
println "[cmd] ${command}"
proc = command.execute()

def sout = new StringBuffer()
def serr = new StringBuffer()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "[cmd.out] ${sout}"
println "[cmd.err] ${serr}"

Jenkins

If you are running this code from Jenkins and Pipeline job, probably most convenient way would be to use built-in sh step.

sh: Shell Script

This step allows a Jenkins server or slave running on Linux or a Unix-like machine to execute a shell script.
Params:

script: String - the script to execute

sh "find src -name '*.txt' -exec cp {} dst/ \;"

More information on the Pipeline DSL can be found in the Pipeline tutorial.

Pure Groovy

And after all why not replace find with just Groovy code:

("src" as File).eachFileRecurse{
    if (it.name ==~ ".*\\.txt") {
        ...
    }
}

Isn't that more elegant?

luka5z
  • 7,525
  • 6
  • 29
  • 52
  • great input thank you. Do you know if the built in sh step allows me to check serr and sout, I didnt see any examples in the tutorial so I chose the groovy method. – Stephen Nichols May 05 '16 at 15:41
  • More information about how to obtain command status and output from `sh` step, http://stackoverflow.com/a/36958537/6128602. – luka5z May 05 '16 at 16:01
  • Thank you and noted. Since I have some more complex logic based on sterr and sout Ill need to stick with the groovy .execute() implementation for now. – Stephen Nichols May 05 '16 at 16:54
  • the jenkins proposed solution gives an error for me: unexpected char: '\' – YaP Jan 17 '22 at 01:01
-1

I didn't resolve why the command was failing but i'm going to assume its groovys conversion to sh breaking when chaining commands. My solution was to break up the find and copy command.

location = "find ${env.JENKINS_HOME}/jobs/myJob/builds/lastSuccessfulBuild/archive/build/libs/ -name *.war".execute().text
command = "cp $location ${packageDir}myJob.war"
proc = command.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(3000)
println "out> $sout err> $serr"
Stephen Nichols
  • 2,363
  • 5
  • 14
  • 19