How to create and use a gradle task factory function - gradle

How do I create a function that create function or maybe I should ask to call it
// cant use this method see comment tagged ERROR further down
def webRollbackFactory(parentTask) {
tasks.create(name: "webRollback.${parentTask.name}", type: Copy, dependsOn: [webBackup]) {
onlyIf { patentTask.state.failure != null }
from(zipTree("${webPublishPath}/../${getWebBackupName()}")) { include '*' }
into webPublishPath
doLast {
println "\nwebRollback.${parentTask.name}.doLast"
}
}
}
// this task which do the same works with out problems
task webRollback_webPublish(type: Copy) {
onlyIf { webPublish.state.failure != null }
from(zipTree("${webPublishPath}/../${getWebBackupName()}")) { include '*' }
into webPublishPath
doLast {
println "\nwebRollback.webPublish.doLast"
}
}
task webPublish(type: com.ullink.Msbuild) {
dependsOn msbuild, webBackup
projectFile = "${webPublishProjectDir}/${webPublishProjectFileName}"
targets = ['Publish']
parameters = [PublishProfile: webPublishProfile]
configuration = BUILD_TYPE
parameters.maxcpucount
doLast {
println '\nwebPublish.doLast'
}
// ERROR: fails with: Could not find method webRollbackFactoy() for arguments [task ':webAccessTest'] on task ':webAccessTest' of type org.gradle.api.tasks.Exec.
//finalizedBy webRollbackFactory(webPublish)
// the version that works
finalizedBy webRollback_webPublish
}
I am on Gradle 4.8

The reason you get that error is because the closure being evaluated does not find the function declared in the main file.
Try changing your function to a closure as a variabe reference and then it should work.
webRollbackFactory = { parentTask ->
tasks.create(name: "webRollback.${parentTask.name}", type: Copy, dependsOn: [webBackup]) {
onlyIf { patentTask.state.failure != null }
from(zipTree("${webPublishPath}/../${getWebBackupName()}")) { include '*' }
into webPublishPath
doLast {
println "\nwebRollback.${parentTask.name}.doLast"
}
}
}

Related

Stop execution of finalizedBy task, or only execute follow-up task on a condition

I'm using the com.google.cloud.tools.appengine gradle plugin, which has a task appengineDeploy.
I have two tasks that configure the appengineDeploy task before executing it. My current solution looks something like that:
task deployTest() {
doFirst {
appengine.deploy {
version = 'test'
...
}
}
finalizedBy appengineDeploy
}
task deployProduction() {
doFirst {
appengine.deploy {
version = '7'
...
}
}
finalizedBy appengineDeploy
}
Now I wanted to add a security question before the deployProduction task is executed, like this:
println "Are you sure? (y/n)"
def response = System.in.newReader().readLine()
if (response != 'y') {
throw new GradleException('Task execution stopped')
}
The problem is, by defintion the finalizedBy task is executed even if my task throws an exception, and that is exactly the opposite of what I want.
I can't change it to appengineDeploy dependsOn deployTest and call appengineDeploy as I have two tasks with different configuration.
And I can't change the appengineDeploy task as it comes from a plugin.
Is there any other way I can either stop the execution of appengineDeploy, or use something other than finalizedBy to execute that task after my deploy task?
One option is to leverage onlyIf to decide whether to execute the task, for example by examining a project property.
Here's a litte demo, given that task appengineDeploy is a task contributed by a plugin (see comment for details):
plugins {
id 'base'
}
ext {
set('doDeploy', false)
}
appengineDeploy.configure {
onlyIf {
project.ext.doDeploy
}
}
task deployTest() {
doFirst {
println 'deployTest()'
project.ext.doDeploy = true
}
finalizedBy appengineDeploy
}
task deployProduction() {
doFirst {
println 'deployProduction()'
println "Are you sure? (y/n)"
def response = System.in.newReader().readLine()
if (response == 'y') {
project.ext.doDeploy = true
}
}
finalizedBy appengineDeploy
}
Another option is to disable the task, which goes like this:
task deployProduction() {
doFirst {
println 'deployProduction()'
println "Are you sure? (y/n)"
def response = System.in.newReader().readLine()
if (response != 'y') {
project.tasks.appengineDeploy.enabled = false
}
}
finalizedBy appengineDeploy
}
I modified the answer from thokuest a bit (thanks for the help!), to prevent executing any tasks inbetween.
I created it as extension method since I needed it more than once:
ext.securityQuestion = { project, task ->
println "Are you sure you want to execute ${project.name}:${task.name}? (y/n)"
def response = System.in.newReader().readLine()
if (response != 'y') {
project.tasks.each {
if (it != task)
it.enabled = false
}
throw new GradleException("Execution of ${project.name}:${task.name} aborted")
}
}
and my task now looks like this:
task deployProduction() { task ->
doFirst {
securityQuestion(this, task)
}
finalizedBy appengineDeploy
}

Is it possible to divide a Gradle build script into separate source files?

Consider this toy build.gradle file:
plugins {
id "java"
}
import org.gradle.internal.os.OperatingSystem;
apply plugin: 'java'
repositories {
mavenLocal()
mavenCentral()
}
def pathExists(pathname) {
// Returns true iff pathame is an existing file or directory
try {
// This may throw an error for a Windows pathname, c:/path/to/thing
if (file(pathname).exists()) {
return true;
}
} catch (GradleException e) {
// I don't care
}
if (OperatingSystem.current().isWindows()) {
try {
// If we're on Windows, try to make c:/path/to/thing work
if (file("file:///${pathname}").exists()) {
return true;
}
} catch (GradleException e) {
// I don't care
}
}
return false
}
def someVariable = "absent"
if (pathExists("/tmp")) {
someVariable = "present"
}
task someCommonTask() {
doLast {
println("Did some setup stuff: ${someVariable}")
}
}
task someATask(dependsOn: ["someCommonTask"]) {
doLast {
println("A: ${someVariable}")
}
}
task someBTask(dependsOn: ["someCommonTask"]) {
def otherVariable = someVariable == "absent" || pathExists("/etc")
doLast {
println("B: ${otherVariable}")
}
}
Is it possible to reorganize this build file so that someATask is in a.gradle and someBTask is in b.gradle? I've made some attempts to use apply from: without success and, if the answer is on this page: https://docs.gradle.org/current/userguide/organizing_gradle_projects.html it eludes me.
In the real project, there would be a couple of dozen tasks in each of those subordinate files and the goal of dividing up the build.gradle file isn't to have them be subprojcts, per se, they're just logical groupings of tasks.
This split can be done using apply from: ... to separate the files.
E.g.
build.gradle:
plugins {
id "java"
}
apply plugin: 'java'
repositories {
mavenLocal()
mavenCentral()
}
ext {
someVariable = "absent" // XXX
}
apply from: "setup.gradle" // XXX
apply from: "common.gradle" // XXX
task someATask(dependsOn: ["someCommonTask"]) {
doLast {
println("A: ${someVariable}")
}
}
setup.gradle:
task setup() {
someVariable = "present"
}
common.gradle:
task someCommonTask(dependsOn: ["setup"]) {
doLast {
println("Did some setup stuff: ${someVariable}")
}
}
# gradle someATask
> Task :someCommonTask
Did some setup stuff: present
> Task :someATask
A: present
BUILD SUCCESSFUL in 403ms
My guess, the problems you are facing are with toplevel code and not
using properties.

Execute task.dependsOn only on a Condition in Gradle

I have two tasks Task-A and Task-B
This is my Task-A
task Task-A () {
doLast {
def fileName = _.property('fileName')
if (fileName !=null) {
println 'success'
}
}
}
My Task-B is dependent on Task-A and I should make it dependent only on the Condition that _.property('fileName') should exist and should not be null
So I wrote my Task-B this way
task Task-B () {
doFirst {
def fileName = _.property('fileName')
if (fileName !=null) {
dependsOn 'Task-A'
}
}
}
It throws an Error
Cannot call Task.dependsOn(Object...) on task ':Task-B' after task has started execution.
How to execute dependsOn on a condition ?
You must set dependsOn directives during the configuration phase
try :
task Task-B () {
def fileName = _.property('fileName')
if (fileName !=null) {
dependsOn 'Task-A'
}
}

Change global ext property in gradle

I'm trying to write a build script for different environments. But the global property is not updated for specific tasks.
Here is the script:
ext {
springProfilesActive = 'development'
angularAppBasePath = '/test/'
}
task setActiveProfiles {
doLast {
if (project.hasProperty('activeProfiles')) {
springProfilesActive = project.property('activeProfiles')
}
}
}
task setProperties(dependsOn: ':setActiveProfiles') {
doLast {
if (springProfilesActive != 'development') {
angularAppBasePath = '/'
}
println springProfilesActive
println angularAppBasePath
}
}
task buildAngular(type: Exec, dependsOn: ':setProperties') {
workingDir angularPath
commandLine 'cmd', '/c', "npm run build.prod -- --base ${angularAppBasePath}"
}
If I run buildAngular -PactiveProfiles=integration the properies are correctly set. But the angularAppBasePath is still the old /test/ value in the npm command. Output:
Executing external task 'buildAngular -PactiveProfiles=integration'...
:setActiveProfiles
:setProperties
integration
/
:buildAngular
> angular-seed#0.0.0 build.prod C:\myapp\src\main\angular
> gulp build.prod --color --env-config prod --build-type prod "--base" "/test/"
Why the propery are changed in the setProperties task but remains the old value in the buildAngular task?
Try to rewrite your setActiveProfiles and setProperties tasks as follows:
task setActiveProfiles {
if (project.hasProperty('activeProfiles')) {
springProfilesActive = project.property('activeProfiles')
}
}
task setProperties(dependsOn: ':setActiveProfiles') {
if (springProfilesActive != 'development') {
angularAppBasePath = '/'
}
println springProfilesActive
println angularAppBasePath
}
This behavior is caused by different build lifecycles. You modify your variable during execution phase (within doLast closure), but use it at the configuration phase, which happens just before the execution.You can read about build lifecycle in the Gradle official user guide.

gradle tasks.withType does not find tasks defined in other file

I have two gradle files, setup.gradle and tests.gradle; each having two gradle tasks of custom type 'EMTest';
test.gradle applies the 'setup.gradle' as apply from: 'setup.gradle'
I want to configure all tasks of type EMTest; For that I added below code at end of tests.gradle
tasks.withType(EMTest) {
println it.name
}
But this only prints the name of tasks in the tests.gradle;
When I run
tasks.all {
println it.name + " " + it.class
}
It however lists the tasks name defined in setup.gradle and the type as EMTest_Decorated (for all 4 types)
NOTE: I use gradle 1.11 (no control over upgrade); What is the issue here ?
UPDATE On 09/Jun
Here is the main file :
apply plugin: 'java';
apply plugin: 'maven'
apply from: 'emcpsrvs_3n_setup.gradle'
buildscript {
repositories {
maven {
url = "${artifactory_contextUrl}/repo" }
}
dependencies {
classpath group:"com.mycompany.myprod.mymodule", name: "TestInfraPlugin", version: "${testinfraVersion}", transitive: true
classpath group: 'org.codehaus.jackson', name: 'jackson-mapper-asl', version:'1.9.13'
classpath group: 'com.mycompany.myprod', name: 'common',version:'0.1'
classpath group: 'org.codehaus.jackson', name: 'jackson-core-asl', version:'1.9.13'
classpath group: 'com.oracle.weblogic',name: 'jettison-1.1', version: '12.1.2-0-0'
}
}
repositories {
/* To check if the jar is available in local maven repository */
mavenLocal()
maven {
url = "${artifactory_contextUrl}/repo"
}
}
apply plugin: 'TestInfraPlugin'
import com.mycompany.myprod.gradle.testinfra.tasks.EMTest;
repositories {
maven {
url = "${artifactory_contextUrl}/repo"
}
}
dependencies {
testConfig group:'com.mycompany.myprod',name:'ui-integ-tests', version: '1.+'
testConfig group: 'com.mycompany.myprod', name: 'emaas-platform-tenant-sdk', version: '0.1+'
}
task unitTests(type: EMTest){
}
// Three tests are disabled due to JIRA-900
task tenantMgmtUITests(type: EMTest,dependsOn: [cleanSmall_deploy_3n_block,small_deploy_3n_block]) {
useWebdriver = true
small_deploy_3n_block.mustRunAfter ([cleanSmall_deploy_3n_block])
options.suiteXmlBuilder().suite('parallel': 'none','name': 'TenantManagementUI') {
test('name': 'TenantManagementUI') {
classes([:]) {
'class'('name': 'com.mycompany.package.MyTest')
}
}
}
}
small_deploy_3n_cleanup.mustRunAfter ([tenantMgmtUITests])
task emcpsrvs_tenant_mgmt_ui_3n(dependsOn: [tenantMgmtUITests,small_deploy_3n_cleanup])
Here is the 'emcpsrvs_3n_setup.gradle' which is being applied above
buildscript {
repositories {
maven {
url = "${artifactory_contextUrl}/repo"
}
}
dependencies {
classpath group: 'com.mycompany.myprod.emdi', name: 'TestInfraPlugin', version: "${testinfraVersion}", transitive: true
}
}
apply plugin: 'TestInfraPlugin'
repositories {
maven {
url = "${artifactory_contextUrl}/repo"
}
}
import com.mycompany.myprod.gradle.testinfra.tasks.EMTest;
ext.integDeployVersion='1.1+'
dependencies {
testConfig group: 'com.mycompany.myprod.test', name: 'smalldeployment', version: "${integDeployVersion}"
}
/* Setup EMaaS Small Deployment */
task small_deploy_3n_block(type: EMTest) {
outputs.upToDateWhen { false }
onlyIf {!System.env.SMALLDEPLOY_IGNORESETUP}
options.suiteXmlBuilder().suite('name': 'setup_3n_env') {
test('name': 'emaas_setup_small_deploy') {
classes([:]) {
'class'('name': 'mycompany.sysman.test.emaas.integ.EmaasSmallDeploy3n') {
methods([:]) {
'include' ('name': 'setupEmaasSmallDeploy')
}
}
}
}
}
useWebdriver = true
useRestAssured = true
}
/* Cleanup EMaaS Small Deployment */
task small_deploy_3n_cleanup(type: EMTest) {
onlyIf {!System.env.SMALLDEPLOY_IGNORESETUP}
options.suiteXmlBuilder().suite('name': 'setup_3n_env') {
test('name': 'emaas_setup_small_deploy') {
classes([:]) {
'class'('name': 'mycompany.sysman.test.emaas.integ.EmaasSmallDeploy3n') {
methods([:]) {
'include' ('name': 'logCollectionAndPostCleanup')
}
}
}
}
}
mustRunAfter ([small_deploy_3n_block])
}
And finally here is the snippet from TestInfraPlugin.groovy (The gradle plugin):
logger.debug "Configuring the EMTest task with default values."
project.afterEvaluate {
project.ext.testClassesDir = new File(project.properties['emdi.T_WORK'] + '/testClasses')
def testTasks = project.tasks.withType(EMTest)
if (testTasks != null && testTasks.size() == 0) {
logger.info "There are no tasks of type EMTest."
return
}
def extractTask = project.tasks.findByPath('extractTestClasses') ?:
project.task('extractTestClasses', type: ExtractConfiguration) {
configuration = project.configurations.testConfig
to = project.testClassesDir
}
/*
* 1. Adding the 'extractTask' to all EMTest, to ensure that 'extractTask' is run before any 'EMTest'.
* 2. For lazy evaluation of lrgConfig, we are NOT running the task here, but just adding as dependent task.
*/
testTasks.each { task ->
logger.debug "Adding dependsOn extractTask for task: ${task.name}"
task.dependsOn extractTask
}
} // end afterEvaluate
}
What is afterEvaluate{} block doing:
It checks if there are any tasks of type EMTest and if there are creates a task to extract a configuration (named testConfig). This extract task is added as dependency on all the tasks of type EMTest such that the extract task run as first task before running any other task.
What is happening
The extractTestClasses task is added as dependency to ONLY the two tasks unitTests and tenantMgmtUITests and thus the small_deploy_3n_block is getting executed before extractTestClasses resulting setup to fail, which in turn results test to fail.
When you reference tasks with no object it is implicitly using project.tasks.
tasks.withType(EMTest) {
println it.name
}
That is why you'll only get tasks from your current project.
To include all projects you can use:
allprojects {
tasks.withType(EMTest) { println it.name }
}
In this closure you are referencing it.tasks, where it loops over every individual project.
This will not work as expected since Gradle will probably not have loaded all of your sub-projects at this point, and not have reached every task define found during the full configuration phase. Therefore you must define the closure to run after all the projects are fully evaluated:
allprojects {
afterEvaluate {
tasks.withType(EMTest) { println it.name }
}
}

Resources