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.
Related
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
}
This is my build.gradle which has three tasks (one for downloading the zip, one for unzipping it and other for executing the sh file). These tasks are dependent on each other. I am using Gradle 6. As these are dependent tasks I have used following command:
gradlew C
I get the error:
Skipping task 'B' as it has no source files and no previous output files.
build.gradle:
task A {
description = 'Rolls out new changes'
doLast {
def url = "https://${serverName}/xxx/try.zip"
def copiedZip = 'build/newdeploy/try.zip'
logger.lifecycle("Downloading $url...")
file('build/newdeploy').mkdirs()
ant.get(src: url, dest: copiedZip, verbose: true)
}
}
task B (type: Copy, dependsOn: 'A') {
doLast {
def zipFile = file('build/newdeploy/try.zip')
def outputDir = file("build/newdeploy")
from zipTree(zipFile)
into outputDir
}
}
task C (type: Exec, dependsOn: 'B') {
doLast {
workingDir 'build/newdeploy/try/bin'
executable 'sh'
args arguments, url
ext.output = { return standardOutput.toString() }
}
}
I tried following and it worked. Rather than adding a separate task to copy, added the copy function in the task A itself
task A {
description = 'Rolls out new changes'
doLast {
def url = "https://${serverName}/xxx/try.zip"
def copiedZip = 'build/newdeploy/try.zip'
logger.lifecycle("Downloading $url...")
file('build/newdeploy').mkdirs()
ant.get(src: url, dest: copiedZip, verbose: true)
def outputDir = file("build/newdeploy")
copy {
from zipTree(zipFile)
into outputDir
}
}
}
task C (type: Exec, dependsOn: 'A') {
doLast {
workingDir 'build/newdeploy/try/bin'
executable 'sh'
args arguments, url
ext.output = { return standardOutput.toString() }
}
}
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"
}
}
}
task executeScript() {
doFirst {
exec {
ignoreExitValue true
commandLine "sh", "process.sh"
}
}
doLast {
if (execResult.exitValue == 0) {
print "Success"
} else {
print "Fail"
}
}
}
I'm getting the following error
> Could not get unknown property 'execResult' for task ':core:executeScript' of type org.gradle.api.DefaultTask.
If I move the commandLine to configuration part everything works fine. But I want commandLine to be in action block so it wont run every time we execute some other gradle tasks.
Use gradle type for your task
task executeScript(type : Exec) {
commandLine 'sh', 'process.sh'
ignoreExitValue true
doLast {
if(execResult.exitValue == 0) {
println "Success"
} else {
println "Fail"
}
}
}
This will work for you...
You can read more about the Exec task here
Alternative syntax to execute external command and get its return code:
doLast {
def process = "my command line".execute()
process.waitFor()
println "Exit code: " + process.exitValue()
}
I want a clean build, where you can see exactly what happened, but all information is preserved - so essentially for every task, I want it to write the output to a file, and only display it if the task fails.
I've tried to achieve this in gradle - but am being defeated because doLast doesn't run if the task fails. Here's my "almost" working pattern:
task fakeTask( type: Exec ) {
standardOutput = new ByteArrayOutputStream()
commandLine 'cmd', '/c', 'silly'
doLast {
new File("build.log") << standardOutput.toString()
if (execResult.exitValue != 0) println standardOutput.toString()
}
}
is there any alternative to doLast that will run any time? Any other way of doing this? - especially as I want to do this for every single task I have?
this is my final solution:
tasks.withType( Exec ) {
standardOutput = new ByteArrayOutputStream()
ignoreExitValue = true
doLast {
new File("gradle.log") << standardOutput.toString()
if (execResult.exitValue != 0)
{
println standardOutput.toString()
throw new GradleException(commandLine.join(" ") + " returned exitcode of " + execResult.exitValue )
}
}
}
Add a ignoreExitValue true to your task definition to suppress throwing of an exception when the exit value is non-zero.
You can use the task graph as documented at https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:task_execution to execute code after a task has run.
gradle.taskGraph.afterTask { task, state ->
if (task instanceof ExecTask && state.failure) {
file('gradle.log') << standardOutput.toString()
}
}