Run a background job from Gradle - gradle

I've created a task starting a remote job like
task mytask(type: Exec) {
commandLine 'ssh'
args '-f -l me myserver ./start'.split(' ')
}
and it works, however, it seems to wait for the job to terminate. But it never terminates and it shouldn't.
Doing the same from the command line works: Because of the -f switch the ssh command gets executed in the background.
I've tried to add '>&' /dev/null (csh stdout and stderr redirect) to the command line, but without any success. Also the obvious & did nothing. I also extracted the command line into a script, and it's always the same: Gradle waits for termination.
Solution
I've solved it by using a script and redirecting both stdout and stderr in the script. My problem came from confusing redirections... by passing '>&' /dev/null I redirected the streams on the remote computer, but what was needed was a redirection on the local one (i.e., without putting the redirection operator in quotes).

The Exec task always waits for termination. To run a background job, use the Ant task 'Exec'
ant.exec(
executable: 'ssh',
spawn: true
) {
arg '-f'
arg '-l'
arg 'me'
arg 'myserver'
arg './start'
}

The Exec task always waits for termination. To run a background job, you need to write your own task, which could, for example, use the Java ProcessBuilder API.

As #peter-niederwieser suggests, ProcessBuilder might be the sollution. Something along the lines of Tomas Lins ExecWait might work to your winnings.
In short, it listens for a chosen word, and marks task as done when it hits.
From the page:
class ExecWait extends DefaultTask {
String command
String ready
String directory
#TaskAction
def spawnProcess() {
ProcessBuilder builder = new ProcessBuilder(command.split(' '))
builder.redirectErrorStream(true)
builder.directory(new File(directory))
Process process = builder.start()
InputStream stdout = process.getInputStream()
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout))
def line
while ((line = reader.readLine()) != null) {
println line
if (line.contains(ready)) {
println "$command is ready"
break;
}
}
}

The Gradle spawn plugin can launch a background process in a Gradle build and subsequently tear it down again. NB this does not work under Windows, but seems fine on Linux or MacOS X. If you find that it starts up the background process, but doesn't appear to detect when the background process has finished initialising so that integration testing can begin, you have to configure the task with the parameter "ready", which is a string that it looks for in the output of the started background process to determine when it is safe to proceed.

Related

Is it possible to run commandLine in gradle with bash -c?

I have a task in a build.gradle file, in which I'd like to run this command:
(export ENV_VAR=/my/path ; /path/to/build.sh)
I tried running this in gradle:
task myTask {
doLast {
exec {
commandLine ['bash', '-c', '"(export ENV_VAR=/my/path ; /path/to/build.sh)"']
}
}
}
Unfortunately, I have an error that says
Successfully started process 'command 'bash''
bash: (export ENV_VAR=/my/path ; /path/to/build.sh): No such file or directory
Now I'm sure the file exists and the specified paths are correct. Running this command manually in the terminal works.
Is there something in gradle that makes a bash -c like this not work? I can't think of another way to make an export like this otherwise.
Try without the extra quotes:
commandLine ['bash', '-c', '(export ENV_VAR=/my/path ; /path/to/build.sh)']
When you run that in the command line, your shell needs the quotes to pass to the command (which happens to be bash) as a single argument, but gradle is already doing that with that syntax, so bash is receiving literally one argument "(export ENV_VAR=/my/path ; /path/to/build.sh)" and since it does not recognize this as internal syntax, tries to run a command with this name.

Gradle: How to pipe into a Exec task

I need to provide a default "yes" to a command I try to execute with Gradle.
So the moment I run:
./gradlew mytask
it should execute something like:
yes | <path-to-script-or-command>
How would I do that?
If there is only one input to the command, you can do:
task mytask(type: Exec) {
commandLine "my-command"
standardInput = new ByteArrayInputStream("yes".getBytes())
}
If you need it to be interactive, use standardInput = System.in.
I am not aware of a way to provide multiple fixed inputs though (e.g. the command first asks for one input, and after that another).

Gradle exec commandLine does not work as expected

I'm currently working on a Gradle project in OSX
My function in a .gradle file looks like this
ext.MyFunction = {
def fastlaneCommand = [
'fastlane',
'-version'
]
def stdout = new ByteArrayOutputStream()
exec {
ignoreExitValue true
standardOutput stdout
workingDir PathsModel.instance.GetDeployerRoot()
commandLine fastlaneCommand
LOG.WARN("YOUR CLI COMMAND: " + commandLine)
}
println "Output:\n$stdout"
}
And then in 'build.gradle'
task jenkins_deploy() {
doFirst {
MyFunction()
}
}
When it comes time for commandLine to be executed
This outputs:
W A R N I N G: YOUR CLI COMMAND: [fastlane, -version]
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':jenkins_deploy'.
> A problem occurred starting process 'command 'fastlane''
I know for a fact that fastlane is in my path as '$HOME/.fastlane/bin' which is where the executable is located. And if I simply open Terminal and type
'fastlane -version'
from any directory, fastlane tools start-up and do what they're supposed to be doing.
I suppose my question is:
What are the possible differences between me opening a terminal and inputting the command manually, and me asking Gradle to do the exact same thing using 'exec'?
Am I misunderstanding what 'exec' and 'commandLine' actually do?
Some info on 'fastlane' is that it's using Ruby, which i don't know a lot about. This may prove relevant.
EDIT: I have attempted 'version' the 2nd element in the fastlaneCommand array, as both 'version' and '-version'
EDIT 2 (ACTUAL SOLUTION): Although the marked answer below is a definite workaround, the solution Actual solution has the full reason as to why this happens and why it works.
TL;DR
I suppose it should be:
['sh', 'fastlane', '-version']
Explanation:
Have not the link under my arm, but if you omit sh it would be executed as a script located in current directory (or directory configured as the second argument). If you prefix it with sh it will be executed with shell and $PATH variable.

test a bash script with gradle

we're using gradle as build tool and for our java and ansible projects. Now I would like to test a bash script from within gradle as well.
Do you have any tipps/resources or better even an example how I can test a bash script using gradle? It can be as simple as executing the script and having the "test" pass, if the return value of the bash script under test is 0 or if the stdout or stderr contain certain strings (or don't contain them).
Thanx a lot in advance for your help!
Here's an example of a Gradle task which stops tomcat server:
task stopTomcat(type:Exec) {
workingDir '../tomcat/bin'
//on windows:
commandLine 'cmd', '/c', 'stop.bat'
//on linux
commandLine './stop.sh'
//store the output instead of printing to the console:
standardOutput = new ByteArrayOutputStream()
//extension method stopTomcat.output() can be used to obtain the output:
ext.output = {
return standardOutput.toString()
}
}
It's also a good example because there are a few useful directives in it.
In your case it would be something like:
task testFile(type:Exec) {
workingDir '/home/user'
commandLine './test.sh'
}
More information can be found here.

How can I trigger a shell script and run in background (async) in Ruby?

I have a shell script named test.sh. How can I trigger the test.sh from Ruby?
I want test.sh to run as a background process, what means in Ruby it is a ansync call.
STDERR and STDOUT also need to be written to a specific file.
Any ideas?
#TanzeebKhalili's answer works, but you might consider Kernel.spawn(), which doesn't wait for the process to return:
pid = spawn("./test.sh")
Process.detach(pid)
Note that, according to the documentation, whether you use spawn() or manually fork() and system(), you should grab the PID and either Process.detach() or Process.wait() before exiting.
Regarding redirecting standard error and output, that's easy with spawn():
pid = spawn("./test.sh", :out => "test.out", :err => "test.err")
Process.detach(pid)
Try this:
Process.fork { system "./test.sh" }
Won't work on windows, for which you can use threading.
I think IO.popopen also deserves a mention here. Something like this would append the output from multiple runs of the command. to stdout.log and stderr.log
open('stdout.log', 'a') { |file|
file.puts(
IO.popen(["./test.sh"], :err => ["stderr.log", "a"]) { |result|
result.read
}
)
end

Resources