Silently handle Gradle task failure - gradle

I have a project which should run a specific Gradle task (sonarqube task) only if a condition is met after build task is executed. This task can fail as it communicates with a remote server which is sometimes not available. If the server is unavailable, I'd like to silently handle the error an print some message to console instead of failing the whole build.
Currently I am able to do so with the following configuration:
build.doLast {
if ('desired.value' == System.properties['some.prop']) {
try {
tasks.sonarqube.execute()
} catch(e) {
...
}
}
}
However using this method I get deprecation messages. After reading up a bit I supposedly should not be using execute, so instead I came up with this:
if ('desired.value' == System.properties['some.property']) {
build.finalizedBy sonarqube
}
However in this case, if sonarqube task fails my whole build will fail as well. How could I handle sonarqube task failures in such case?
I am using Gradle 4.5.1.
Edit
The builds are ran using the following command:
./gradlew build
Modifying it like the following will cause Gradle to ignore not only sonarqube failures, but also build failures which is not what I want:
./gradlew build --continue

As you've discovered, once a Gradle task fails it's not possible to elegantly recover from that. Therefore you need to avoid that happening.
I can think of three ways to do that:
Find a way of executing the code you want to execute directly without it being wrapped by a Gradle task (eg via a command line or Java process). Then execute that code inside of a try-catch block in the normal fashion.
Activate an option in the sonarqube task which means that task does not fail when there is a problem but reports back to a parent task in another way (eg by setting a property on the Task object), which can in turn deal with the problem. (Having looked quickly it does not appear any such option exists, but you could request it or write a pull request.)
Run another task before the sonarqube task to check if the remote server is available first (and/or other potential failure conditions); then you can deal with the server not being available gracefully, including not running the sonarqube task.
The last option could work as follows by executing a new sonarqubeWrapper task:
tasks.register("sonarqubeWrapper") {
doFirst {
if (isServerAvailable()) {
// Deal with the server not being available
throw new StopExecutionException()
}
}
doLast('sonarqube')
}
def isServerAvailable() {
// Determine if server available
}

Related

Why copy task delete stale output on first build?

I have this kotlin gradle build script representing my use case.
I am working with Gradle 6.7.
This is my kotlin gradle build file:
plugins {
java
}
tasks.register("createFile"){
doLast{
projectDir.resolve("tmp.txt").createNewFile()
projectDir.resolve("tmp.txt").writeText("tmp")
}
dependsOn("assemble")
}
tasks.register("createExecFile", Exec::class){
workingDir(buildDir)
commandLine("cmd", "/c", "mkdir destdir\\subdir")
dependsOn("createFile")
}
tasks.register("copyBug", Copy::class){
from(projectDir.resolve("tmp.txt"))
into(buildDir.resolve("destDir"))
dependsOn("createExecFile")
}
Now run gradle copyBug -i.
The first time this will give you this output:
> Task :copyBug Deleting stale output file: E:\repo\BugCOpy\build\destDir Caching disabled for task ':copyBug' because: Build cache is disabled Task ':copyBug' is not up-to-date because: No history is available.
The copy task deletes the file created by the previous exec task.
Now if you rerun this command the copy task won't delete stale file.
So what are those stale file? How can I prevent those file to be deleted? My first build and is different than the other build.
Should I file a bug?
In your createExecFile task you produce output files without telling Gradle about them. So Gradle doesn’t know that this task and the copyBug task use the same output directory. Instead, Gradle believes that copyBug is the only task producing outputs under build/destdir and hence it’s safer to assume that any existing files in that directory should not be there (and are “stale”).
The solution is to tell Gradle that your createExecFile task outputs to build/destdir:
tasks.register("createExecFile", Exec::class) {
workingDir(buildDir)
commandLine("cmd", "/c", "mkdir destdir\\subdir")
// tell Gradle about the output directory
outputs.dir(buildDir.resolve("destdir"))
dependsOn("createFile")
}
See this release notes section for why this behavior was introduced and this explanation to a very similar issue if you want some more background information.
My build process looks like
run gradle (part 1)
do something else
run gradle (part 2)
For me annoyingly running gradle in step 3 would not just add a few files to the output directory but delete that folder as stale first. Even adding
outputs.dir(...)
did not prevent Gradle from removing it as stale. Working out the inputs and outputs of my task looked too tedious, but luckily I found a way to tell Gradle not to perform any up to date tracking:
Example 37. Ignoring up-to-date checks mentions to add
doNotTrackState("Comment why this is needed")
which ultimately helped me to keep the files from build step 1.

How to run a task automatically when a gradle script is called

I have a file that needs to be pushed into a specific directory when the project is built with gradle.
My solution so far is inside the build.gradle itself:
....
task copyTask(type: Copy) {
from 'filename'
into 'dirctoryname'
}
The other question on this website wonder, why their tasks are being executed automatically and are told that they need to add a closure inside the task (like doLast).
I however actually want the task to be executed automatically, so I am not using any closures.
When I run the task manually it works just fine and the file gets copied into the directory.
When I however rightclick -> run the build script in IntelliJ nothing happens.
How can I let the task get executed automatically any time the build script is executed?
I was thinking of something like "dependsOn this" but that just throws nasty errors.
And "build.dependsOn copyTask" doesn't throw errors, but also doesn't copy the file.
You have to link the creation of filename also into the deps. E.g.
copyTask.dependsOn myBuildStep
assemle.dependsOn copyTask

Calling Gradle commands and tasks with parameters from Gradle Task

I have a current setup in my build.gradle that I'm trying to understand. I need a number of tasks to go off in a very specific order and execute all with one task call. The setup I want looks like this:
1.) Run liquibase changeset into predefined database
2.) Run a number of tests against database
3.) Rollback all changes made with the previous changeset
I want the database in a 'clean' state every time I test it. It should have only the changes I expect and nothing else. The liquibase is set up with the Gradle plugin for it and the changeset is applied/updated. However, I don't want to call the command manually. This will be something that needs to run in continuous integration, so I need to script it so I simply have our CI call one task and it then runs each task, in order, until the end. I'm unsure of how to call the Gradle command-line task from inside of itself (ie inside the build.gradle file) and then also pass parameters to it (since I'll need to call some type of rollback command task to get the database to be what it was before calling the update).
Right now, all I'm doing is calling the command line tasks like this:
$ gradle update
$ gradle test
$ gradle rollbackToDate -PliquibaseCommandValue=2016-05-25
Again, I can't call them by the command line alone. I need a custom task inside Gradle so that I could just call something like:
$ gradle runDatabaseTests
...And I would have it do everything I expect.
There is no gradle way to invoke/call a task from another task directly. What you can do instead is to use dependsOn or finalizedBy to setup task dependencies which will force the prereq tasks to run first.
If you declare a task:
task runDatabaseTests(dependsOn: [update, test, rollbackToDate]) << {
println "I depend on update, test and rollbackToDate"
}
when you call
gradle runDatabaseTests -PliquibaseCommandValue=2016-05-25
it will force update, test and rollbackToDate first. You can control the order in which they're run, if you care about that, by using mustRunAfter and/or shouldRunAfter

What is the proper way to run tasks in succession in Gradle

I have a task like this in gradle: task startServer (dependsOn: [':backend:appengineStop', ':backend:appengineRun']). The problem is appengineStop is not run or doesn't seem to be run before appengineRun runs.
I am saying this because when the server is running and I execute this task, it should stop the server first (appengineStop) and start the server again (appengineRun), but this is not happening and build fails saying "Port already in use".
That is why I listed appengineStop first so it will stop the server. Can someone please explain.
Setting of deppendsOn of your task just saying, that both tasks on wich you depends (it's ':backend:appengineStop' and ':backend:appengineRun' in your case) will be executed before your dependent startServer task. The order of execution of this tasks is not determined. Probably, they'll be executed in alphabetical order, but it's a gradle implementation's behavior and you don't use to rely on it.
Gardle tasks have a capacity to determine their order using mustRunAfter method, which make a task running just after task specified as an argument is executed.
You can try to do it by adding to your build script somthing like:
task startServer (dependsOn: [':backend:appengineStop', ':backend:appengineRun']) {
tasks.getByPath(':backend:appengineRun').mustRunAfter ':backend:appengineStop'
}

How do I wrap a gradle task in another task?

I am trying to configure two different gradle test tasks that essentially just set some values and then run the built-in test task. I have read the gradle documentation and have been searching for a few hours with no luck. I'm guessing I just don't know how to word this question properly to find anything.
The scenario is that we have selenium tests that we might want to run locally or remotely. If we run them locally we want to configure how many threads it uses, and if we run them remotely we want to set a much higher number of threads and also a system property so that the test runner knows to run remotely.
Essentially here's what I'd like to do:
task testLocal {
maxParallelForks = 2
// now run the built-in test task
}
task testRemote {
maxParallelForks = 4
systemProperties "geb.env": "winxp-firefox"
// now run the built-in test task
}
Ideally I'd also like to be able to pass all the same arguments that the test task supports on the command line, like:
gradle testLocal --tests com.domain.tests.package*
What is the proper way to handle this scenario with gradle?
The proper way to handle this is to have two Test tasks. You'd typically use (and configure) the Java plugin's test task for the local testing, and additionally declare and configure a second testRemote task (task testRemote(type: Test) { ... }). (There is no way to "wrap" a task, or "call" a task from another task.)

Resources