Gradle custom task extends Exec is throwing error - gradle

My intention is to run a docker save command by creating a custom task in Gradle:
Below is my build.gradle file:
class SaveTask extends Exec {
#TaskAction
void saveImage() {
commandLine "bash","-c","docker save someimage:latest | gzip > someimage.tar.gz"
}
}
// Create a task using the task type
task save(type: SaveTask)
When I run the task it is giving me the below error:
Execution failed for task ':save'.
> execCommand == null!
Can someone suggest me where I am going wrong?

You probably don't need to create a custom task type at all, just use the regular Exec task type for your task save:
task save(type: Exec) {
commandLine "bash", "-c", "docker save someimage:latest | gzip > someimage.tar.gz"
}
The problem why your approach fails is that the Exec task type defines a #TaskAction internally. This #TaskAction runs the command defined by commandLine. In your SaveTask task type, another #TaskAction is defined, but it will run after the original #TaskAction. This is the reason why the commandLine is still null / empty for the original #TaskAction.
If you still want to create a custom task type, e.g. because you want to define a configuration interface that will be used by multiple tasks, use a doFirst closure to define the commandLine, as it will be executed before any task action:
class SaveTask extends Exec {
String image
SaveTask() {
doFirst {
commandLine "bash", "-c", "docker save ${image} | gzip > someimage.tar.gz"
}
}
}
task saveImageA(type: SaveTask) {
image = 'imageA:latest'
}
task saveImageB(type: SaveTask) {
image = 'imageB:latest'
}

I modified the above code as below and it's working for me
class SaveTask extends Exec {
SaveTask() {
commandLine "bash", "-c", "someimage:latest | gzip > someimage.tar.gz"
}
}
task saveImageA(type: SaveTask)

Related

Gradle set ext property in one task and use it in another

I have several tasks which executes different SQL scripts via psql.
task runScript1(type: Exec) {
commandLine 'cmd', '/c', "psql -f script1.sql"
}
task runScript2(type: Exec) {
commandLine 'cmd', '/c', "psql -f script2.sql"
}
Now I want to write one generic exec task and pass only the script name to it.
ext {
scriptName = ''
}
task runScriptGeneric(type: Exec) {
commandLine 'cmd', '/c', "psql -f ${scriptName}"
}
task runScript1() {
scriptName = 'script1.sql'
}
runScript1.finalizedBy runScriptGeneric
task runScript2() {
scriptName = 'script2.sql'
}
runScript2.finalizedBy runScriptGeneric
Unfortunately this approach is not working. The scriptName stays empty in the runScriptGeneric task.
I suspect this has to do with the Exec type because the following simple task working well.
ext {
scriptName = ''
}
task runScriptGeneric() {
doLast {
println "${scriptName}"
}
}
task runScript1() {
doLast {
scriptName = 'script1.sql'
}
}
runScript1.finalizedBy runScriptGeneric
task runScript2() {
doLast {
scriptName = 'script2.sql'
}
}
runScript2.finalizedBy runScriptGeneric
I do not want do pass it over as a command line parameter (-PscriptName), because I want to keep the individual tasks for each script. Is there any other way to pass the scriptName from each individual task to the generic task?
Actually, it's not an answer to my question, but it's a solution I can live with.
I've created a dynamic task instead of passing the variable to the executing script:
['Script1', 'Script2'].each {taskName ->
task "run${taskName}"(type: Exec) {
commandLine 'cmd', '/c', "psql -f ${taskName}"
}
}

Gradle, task type: Exec - commandLine not work in onLast

I want execute some command from command line in gradle task(e.g. print all files in dir):
task dir(type: Exec) {
def adbCommand = ["dir", "*.*"]
commandLine adbCommand
standardOutput = new ByteArrayOutputStream()
doLast {
println ("result = " + standardOutput)
}
}
It's work. OK. But when I put it on onLast section it's not work:
task dir(type: Exec) {
doLast {
def adbCommand = ["dir", "*.*"]
commandLine adbCommand
standardOutput = new ByteArrayOutputStream()
println ("result = " + standardOutput)
}
}
I get error:
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':app:dir'.
execCommand == null!
The reason is in the fact, that task of Exec should be configured during configuration phase of the build, otherwise your task will be not configured and fail.
In you first example everything works due to configuration happens at the configuratyion phase. Your second example tries to configure the task within doLast closure - right after the task is executed yet.
If you really need to execute something in doLast, you can use something like this, without creating special task:
task someTaskName {
doLast {
exec {
commandLine adbCommand
}
}
}
Here is exec-specification used to execute some command and it's configured and executed at the same time.

Gradle - How to set dependency tasks for Exec-type tasks?

Say, you have following task:
task commandA() {
doLast {
project.ext.ping = 'PING'
}
}
This will work:
task commandB() {
dependsOn commandA
doLast {
println ping
}
}
This will fail:
task commandC(type: Exec) {
dependsOn commandA
commandLine "echo", ping
}
With Could not find property 'ping' on task 'commandC'. error message.
So, how one can declare dependency for an exec-type task and set some variable in that dependency?
Just don't initialize the variable within the doLast block, since it's getting initialized at the execution phase, but commandLine "echo", ping is trying to get it at the configuration phase of the build.
So, you need something like that:
task commandA() {
project.ext.ping = 'PING'
}
Or even without task, as follows:
project.ext.ping = 'PING'
Because configuration of any task is always executed, even if the task's action won't be executed.
Another solution is to use exec-action, not exec-task, something like this:
task commandA() {
doLast {
project.ext.ping = 'PING'
}
}
task commandC {
dependsOn commandA
doLast {
exec {
commandLine ping, "192.168.100.1"
}
}
}
In this case, exec-closure will be done during execution phase wuth the ping variable already available.
You can read about build lifecycle in the official Gradle user guide

Gradle task should not execute automatically

I'm defining a task in gradle:
task releaseCandidate(type: Exec) {
commandLine 'git', 'checkout', 'develop'
// Increment version code in Manifest
String manifest = new File('AndroidManifest.xml').getText('UTF-8')
Pattern pattern = Pattern.compile('android:versionCode="([0-9]+)"')
Matcher matcher = pattern.matcher(manifest)
matcher.find()
int newVersionCode = Integer.parseInt(matcher.group(1)) + 1
manifest = manifest.replaceAll(
"android:versionCode=\"([0-9]+)\"", "android:versionCode=\"$newVersionCode\""
)
new File('AndroidManifest.xml').write(manifest, 'UTF-8')
commandLine 'git', 'diff'
}
Which I want to execute only when I explicitly call it as gradle releaseCandidate. However, when I run any other task, such as gradle assembleDebug, it also runs task releaseCandidate. I don't want that behaviour to happen. There is no task depending on releaseCandidate or vice-versa.
My project is an Android app, so I am using android gradle plugin.
A common pitfall. Add an action to the task otherwise code will run at configuration phase. Sample task with action:
task sample << {
}
As I see You'd rather need to write a custom task than using Exec type. I suppose it's not valid to define commandLine twice.
EDIT
You can read this post to get the general idea how it all works.
You are mixing Task configuration and groovy code. Everything that is part of the main body of a task definition will be executed in the configuration phase. The task task1 << { code } is a shorthand for
task task1 {
doLast {
code
}
}
commandLine is part of the Exec Task but your other code is not and should be wrapped into a doLast this will execute the commandline first and then execute your additional code. If you need another exec commandLine then you'll need another task.
task releaseCandidate(type: Exec) {
commandLine 'git', 'checkout', 'develop'
doLast {
// Increment version code in Manifest
String manifest = new File('AndroidManifest.xml').getText('UTF-8')
Pattern pattern = Pattern.compile('android:versionCode="([0-9]+)"')
Matcher matcher = pattern.matcher(manifest)
matcher.find()
int newVersionCode = Integer.parseInt(matcher.group(1)) + 1
manifest = manifest.replaceAll(
"android:versionCode=\"([0-9]+)\"", "android:versionCode=\"$newVersionCode\""
)
new File('AndroidManifest.xml').write(manifest, 'UTF-8')
}
}
Just to complete #Opal answer for cases when Exec is really used (for example CommandLine reference) :
task task1 << {
exec {
List<String> arguments = new ArrayList<String>()
//..
commandLine arguments
}
}

Run shell command in gradle but NOT inside a task

What I currently have is:
task myTask (type : Exec) {
executable "something.sh"
... (a lot of other things)
args "-t"
args ext.target
}
task doIt {
myTask.ext.target = "/tmp/foo"
myTask.execute();
myTask.ext.target = "/tmp/gee"
myTask.execute();
}
With this I thought I could run "myTask" with different parameters when I start "doIt". But only the first time the script is executed because gradle takes care that a task only runs once.
How can I rewrite the "myTask" so that I can call it more than once? It is not necessary to have it as a separate task.
You can do something like the following:
def doMyThing(String target) {
exec {
executable "something.sh"
args "-t", target
}
}
task doIt {
doLast {
doMyThing("/tmp/foo")
doMyThing("/tmp/gee")
}
}
The exec here is not a task, it's the Project.exec() method.

Resources