How to avoid Jenkins Job DSL to overwrite triggers in pipelines - jenkins-pipeline

Let's have a seed job in Jenkins Job DSL Groovy which generates a pipeline this way:
pipeline {
agent any
stages {
stage("DSL") {
steps {
jobDsl scriptText: """pipelineJob("test-pipeline-generated") {
definition {
cps {
script 'pipeline { agent(any); triggers{ cron("H/2 * * * *") }; stages { stage("Some") { steps { echo "Working ..." } } } }'
}
}
}
"""
}
}
}
}
It generates a pipeline job which has to be triggered manually for the first time to process triggers definition and everything works fine. However, when the seed job is triggered again it overwrites the pipeline job definition and triggers are gone again. This means that seed job disables all such jobs.
Does anybody know if there is any solution for it?
We would like to keep triggers there and if there are changes in the pipeline it will be processed after the first run. We would like to not specify triggers in job DSL because users are used to specify them in the pipeline definition.

There is a switch for not regenerating existing jobs/views:
DSL API:
ignoreExisting()

If you put the triggers inside the template pipeline (as well as in the seed job), it will remain for the next runs as well.

Related

How to know inside jenkinsfile / script that current build is a replay?

As in subject - is there any way to verify if current build is effect of using 'Replay' button?
I found the following solution using the rawBuild instance from currentBuild. Since we can't get the class of the causes, we just verify its string value.
def replayClassName = "org.jenkinsci.plugins.workflow.cps.replay.ReplayCause​"
def isReplay = currentBuild.rawBuild.getCauses().any{ cause -> cause.toString().contains(replayClassName) }
This solution works for Jenkins with Blue-Ocean. References to how to get to this answer is Jenkins declarative pipeline: find out triggering job
Update
Using this a step condition works like a charm!
You can define a shared library like jenkins.groovy
def isBuildAReplay() {
// https://stackoverflow.com/questions/51555910/how-to-know-inside-jenkinsfile-script-that-current-build-is-an-replay/52302879#52302879
def replyClassName = "org.jenkinsci.plugins.workflow.cps.replay.ReplayCause"
currentBuild.rawBuild.getCauses().any{ cause -> cause.toString().contains(replyClassName) }
}
You can reuse it in a Jenkins pipeline
stage('Conditional Stage') {
when {
expression { jenkins.isBuildAReplay() }
}
steps {
...
...
}
}

Gradle Task . "(type: Copy)" and <doLast> can't both work

task simpleTask{
print("simpleTask is reach");
}
task copySomeFile(type: Copy){
print("copySomeFile is reach");
from baseProjectPath;
into toProjectPath;
appendXML();
}
def appendXML(){
//modify a.txt
}
//i just want to run "simpleTask" only, but when "gradle simpleTask", the task"copySomeFile" will be run also ! I know beacuse gradle initialization.
but if write like this
task copySomeFile(type: Copy)<<{
}
the "copySomeFile" will not work.
it seems like "(type: Copy)" can't work with the "<<" or "doLast{}"?
i just want "--gradle simpleTask" "--gradle copySomeFile" can run alone.
You have to read about Gradle build lifecycle.
There are 2 phases you should note - Configuration and Execution. All tasks are always been configured on every build, but only some of them are really executed as the Execution phase.
What you see is that copySomeFile task was configured during the configuration phase. It doesn't copy anything, but it has to be configured. And everything within a tasks closure is task's configuration, that is why you see results of the print("copySomeFile is reach"); in the output.
<< or doLast are used to run something at the Execution phase, but your task of type Copy will not be configured if you place all it's configuration into doLast section or add << to the task definition - that is the reason why copy doesn't work.
Yeh, i got it. How much I appreciate both of you. SHARE THE CODE:
task simpleTask {
print("\nsimpleTask is configured"); // executed during the configuration plase, always
doLast {
print("\nsimpleTask is executed"); // executed during the execution plase, only if the simpleTask is executed
}
}
task copySomeFile(type: Copy) {
print("\ncopySomeFile is configured"); // executed always,执行其他任务时,此代码也会执行
from "D:/a.txt";// not executed. 执行其他任务时,此代码不会执行
into "D:/b.txt";// not executed. 执行其他任务时,此代码不会执行
doLast {
appendXML(); //only this task executed, the appendXML executed. 只有此task执行时,才会执行.比如(gradle copySomeFile);
}
}
def appendXML(){
print("\nappendXML");
}

Conditional logic in gradle build to only execute certain code when a specific task is running

So, I am using a plugin in my gradle build (the plugin is org.flywaydb.flyway but that is not really relevant). I want to validate the caller has passed in a runtime parameter when tasks from this plugin are executing but not when other tasks are executing.
I pass options to the flyway plugin based on a supplied parameter. I want an error to be returned when a flywayTask is being executed and no parameter is supplied. When a non-flyway task is being run, I do not want to validate if the parameter is supplied.
gradle -PmyParam=myValue flywayMigration
=> should run code and there should be no error
gradle flywayMigration
=> should run code and should produce error (as no parameter supplied)
gradle jar
=> should not run code and no error should be produced
I have been reading about gradle configuration and execution which is fine but I still can't find a way to only run the code when the flyway plugin is bveing executed OR specific flyway tasks are being executed.
This is my current code:
if(gradle.taskGraph.hasTask("flywayMigrate")) {
flyway {
def dbCode, dbUser, dbPassword, dbUrl
if (!project.hasProperty("db_env")) {
throw new GradleException("Expected db_env property to be supplied for migration task. Can be passed" +
" at command line e.g. [gradle -Pdb_env=ex1 flywayMigrate]")
} else {
// do stuff
}
user = balh
password = blah
url = blah
driver = 'oracle.jdbc.OracleDriver'
cleanDisabled = true
baselineOnMigrate = true
baselineVersion = '1.0.0'
}
}
To be clear, I only want this code:
if (!project.hasProperty("db_env")
to run for flyway tasks.
The code above throws this error:
Task information is not available, as this task execution graph has not been populated.
I've tried a few things here, any advice would be appreciated.
It's not really clear to me, what exactly do you want to do in case if this property is provided, but I think, you can do it without accesing task graph, just try to use doFirst Closure of the flywayMigrate task. Just something like this:
flywayMigrate.doFirst {
if(!project.hasProperty("db_env")) {
throw ...
} else {
//Do something
}
}
And leave your plugin configuration free of any additional logic.
As for exception, have you tried to wait until graph is ready? It's usualy done as follows:
gradle.taskGraph.whenReady {taskGraph ->
if(gradle.taskGraph.hasTask("flywayMigrate")) {
...
}
}
Update: to answer the question from the comments
if I can attach doFirst to multiple tasks?
Yes, you can use somthing like:
//declare task names
def names = ["taskA", "taskB", "taskC"]
tasks.findAll {it ->
//filter tasks with names
if (it.name in names)
return it
}.each { it ->
//add some extra logic to it's doFirst closure
it.doFirst {
println 'hello'
}
}
Just check, that all the tasks are exists before this configuration.

How to make Gradle run tasks in certain order?

Let's say that I have created separate tasks for running integration and acceptance tests in my gradle build script. When I run the build task I want to run testing tasks before in this order: unit tests(test task), integration tests (intergationTest task) and acceptance tests (acceptanceTest task). Is this possible and how?
You are looking for "should run after" described in Gradle documentation - http://www.gradle.org/docs/current/userguide/more_about_tasks.html
Here's how you can do it without creating artificial dependencies:
https://caffeineinduced.wordpress.com/2015/01/25/run-a-list-of-gradle-tasks-in-specific-order/
TLDR; version:
//--- build aliases : define a synonym here if you want a shortcut to run multiple targets
def buildAliases = [
'all' : ['clean', 'assemble', 'runProvisioner', 'stopTomcat', 'installTomcat', 'deployToTomcat', 'startTomcat'],
'rebuild' : ['clean', 'assemble']
]
def expandedTaskList = []
gradle.startParameter.taskNames.each {
expandedTaskList << (buildAliases[it] ? buildAliases[it] : it)
}
gradle.startParameter.taskNames = expandedTaskList.flatten()
println "\n\n\texpanded task list: ${gradle.startParameter.taskNames }\n\n"
This is what I did on my projects.
check.dependsOn integTest
integTest.mustRunAfter test
tasks.withType(Pmd) {
mustRunAfter integTest // Pointing to a task
}
tasks.withType(FindBugs) {
mustRunAfter tasks.withType(Pmd) // Pointing to a group of tasks under Pmd
}
tasks.withType(Checkstyle) {
mustRunAfter tasks.withType(FindBugs)
}
It helped me to order tasks by group.
I created this helper method based on a solution that I found on Gradle forum.
Task.metaClass.runFirst = { Task... others ->
delegate.dependsOn(others)
delegate.mustRunAfter(others[0])
for (def i=0; i < others.size() - 1; i++) {
def before = others[i]
def after = others[i+1]
after.mustRunAfter(before)
}
}
Then you can create tasks X, A, B and C and use like this:
X.runFirst A, B, C
The first answer in the list turned out to be great for me. I used
X.shouldRunAfter Y
UPDATE: While using this "solution" for a short while i found out, that it does not work 100% as intended. I don't know why though. Any help to make it work would be appreciated. Maybe it does not work properly when there is more than one task in the Set?! While testing i did add some dummy-Tasks which only printed a text inbetween each of the other tasks and all seemed to be ok.
After some attempts with other solutions i came up with this solution:
It uses the mustRunAfter command to chain Sets of Tasks into the required order.
I'm working with Sets instead of individual Tasks, because i got circular dependency issues otherwise, since some tasks already depended on each other.
Also important to note: the isNotEmpty() check was essential, as it would otherwise break the enforced ordering if an ampty set was passed to the method.
tasks.register("cleanAndGenerate") {
var lastTasks: Set<Task> = setOf()
fun findTasks(taskName: String): Set<Task> {
return if (taskName.startsWith(":")) { // task in specific sub-project
setOf(tasks.findByPath(taskName)!!)
} else { // tasks in all (sub-)projects
getTasksByName(taskName, true)
}
}
fun dependsOnSequential(taskName: String) {
val tasks = findTasks(taskName)
tasks.forEach { task -> task.mustRunAfter(lastTasks) }
dependsOn(tasks)
if (tasks.isNotEmpty()) {
lastTasks = tasks
}
}
dependsOnSequential(":project1:clean") // task in specific sub-project
dependsOnSequential(":project2:clean")
dependsOnSequential("task1") // tasks in all (sub-)projects
dependsOnSequential("task2")
// add more as needed
}

Custom conditional configuration for Gradle project

Having an extract from https://github.com/gradle/gradle/blob/master/build.gradle:
ext {
isDevBuild = {
gradle.taskGraph.hasTask(developerBuild)
}
}
task developerBuild {
description = 'Builds distributions and runs pre-checkin checks'
group = 'build'
dependsOn testedDists
}
When I used this approach to create custom configuration in my project I discovered that:
isDevBuild === true
i.e. it's always true because task 'developerBuild' is inside my build.gradle project, and hence in graph. They have a couple of "different" configs (isCIBuild, isCommitBuild, isFinalReleaseBuild, ...) so I suppose I got something wrong here.
Can someone explain how to make this configs conditional based on some external parameter?
taskGraph.hasTask() tells if a task is in the task execution graph, that is whether it will get executed. Because the task execution graph is only created after the configuration phase, this method has to be called from a whenReady callback (or in the execution phase):
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(developerBuild)) {
// do conditional configuration
}
}
To make this more readable, we can introduce a new method:
def onlyFor(task, config) {
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(task)) {
project.configure(project, config)
}
}
}
Now we can write:
onlyFor(developerBuild) { ... }
onlyFor(ciBuild) { ... }
Another, simpler way to solve this problem is to check whether a particular task name is contained in gradle.startParameter.taskNames. However, this has two limitations: First, it compares task names, which can make a difference in multi-project builds. Second, it will only find tasks that have been specified directly (e.g. on the command line), but not dependencies thereof.
PS.: In your code, isDevBuild always holds because a (non-null) closure is true according to Groovy truth. (In contrast to isDevBuild(), isDevBuild won't call the closure.)

Resources