Groovy function call in a closure - jenkins

How can I make a function call in a closure in Groovy? Currently trying this but it results in all iterations using the values from the last array element:
def branches = [:]
for (int i = 0; i < data.steps.size(); i++) {
branches["${data.steps.get(i).name}"] = {
myFunc(data.steps.get(i))
}
}
parallel branches

That's a common gotcha
This should work:
def branches = data.steps.collectEntries { step ->
[step.name, { myFunc(step) }]
}
parallel branches
Or
def branches = data.steps.inject([:]) { map, step ->
map << [(step.name): { myFunc(step) }]
}

Related

List of filenames Sorting in Jenkins piepline based on file extension

I have a list which contains file names(change log) in Jenkins pipeline. I am looking to sort that list based on file extension.
example - lines = [a.yaml, b.sql, c.json, d.py, e.txt]
I would like to sort this list as below-
lines = [c.json, d.py, b.sql, e.txt, a.yaml]
Thanks in advance.
You can use Groovy Sort with a custom comparator. Please refer to the following sample.
pipeline {
agent any
stages {
stage('Test') {
steps {
script {
def lines = ["a.yaml", "b.sql", "c.json", "d.py", "e.txt"]
echo "Not sorted: $lines"
sortFiles(lines)
echo "Sorted: $lines"
}
}
}
}
}
#NonCPS
def sortFiles(def files) {
return files.sort { s1, s2 -> s1.substring(s1.lastIndexOf('.') + 1) <=> s2.substring(s2.lastIndexOf('.') + 1) }
}
Update
Bringing files without a extension to the top
#NonCPS
def sortFiles(def files) {
return files.sort { s1, s2 ->
def s1Index = s1.lastIndexOf('.')
def s2Index = s2.lastIndexOf('.')
if((s1Index == -1)) { // S1 doesn't have an extension, S1 comes first
return -1
} else if (s2Index == -1) { // S1 have an extension but S2 doesn't so s2 comes first
return 1
} else {
return s1.substring(s1Index + 1) <=> s2.substring(s2Index + 1)
}
}
}

How to run a single jenkinsfile for specific github components

I want to be able to execute specific components that I organized in Files in the repository with only one main jenkinsfile.
For example I have this repo structure:
And I have three different components: Topic_A, Topic_B, Topic_C (same type of components but will be created for different teams).
I want to be able to modify only Topic_A and C and after I push the branch I want my jenkinsfile to able to execute just those changes instead also redeploying Topic_B which it was not modified.
My question is if this possible? Could it be done with a jenkins pipeline? or any other component? (script)
Thank you.
There is a changeset directive that allows you to check whether a file has changed in the Git repository. But it doesn't support checking directories, hence you can do something like the below.
pipeline {
agent any
stages {
stage('Cloning') {
steps {
// Get some code from a GitHub repository
git branch: 'main', url: 'https://github.com/xxxx/sample.git'
}
}
stage('TOPIC_A') {
when { expression { isChanged("Topic_A") } }
steps {
echo 'Doing something for Topic A'
}
}
stage('TOPIC_B') {
when { expression { isChanged("Topic_B") } }
steps {
echo 'Doing something for Topic B'
}
}
}
}
def isChanged(dirName) {
def changeLogSets = currentBuild.changeSets
def folderName = dirName
for (int i = 0; i < changeLogSets.size(); i++) {
def entries = changeLogSets[i].items
for (int j = 0; j < entries.length; j++) {
def entry = entries[j]
def files = new ArrayList(entry.affectedFiles)
for (int k = 0; k < files.size(); k++) {
def file = files[k]
if(file.path.contains(folderName)){
return true
}
}
}
}
return false
}

Jenkins pipeline script created dynamically

I am using a jenkins pipeline project. In the script I would like to write the parallel block in a dynamic way, since the number of nodes can change. For instance, from this:
parallel(
node1: {
node(){
stage1()
stage2()
...
}
},
node2: {
node(){
stage1()
stage2()
...
}
},
...
)
to something like this
for (int i = 0; i < $NODE_NUMBER; i++) {
"node${i}": {
node (’namenode-' + ${i}) {
something()
}
}
but this way doesn’t work, Groovy/Jenkins is not happy about this syntax. Can someone suggest a better way for doing this?
You can define node map like branches first, and then execute them as parallel branches.
def numNodes = 4
def branches = [:]
for(int i = 0; i < numNodes; i++) {
branches["node${i}"] = {
node("namenode-${i}") {
something()
}
}
}
parallel branches

Generating parallel branches causes job to hang

I'm new to groovy and the workflow plugin, so perhaps this is something obvious. Q1: I'm try to run jobs read under a view in parallel. I do like this:
jenkins = Hudson.instance
parallel getBranches()
#NonCPS def getBranches() {
def jobBranches = [:]
for (int i = 0; i < getJobs().size(); i++) {
jobBranches["branch_${i}"] = {
build job : getJobs()[i]
}
}
return jobBranches
}
#NonCPS def getJobs() {
def jobArray = []
jenkins.instance.getView("view_A").items.each{jobArray.add(it.displayName)}
return jobArray
}
I got:
But if I wrote it like this:
jenkins = Hudson.instance
def jobBranches = [:]
for (int i = 0; i < getJobs().size(); i++) {
jobBranches["branch_${i}"] = {
build job : getJobs()[i]
}
}
parallel jobBranches
#NonCPS def getJobs() {
def jobArray = []
jenkins.instance.getView("view_A").items.each{jobArray.add(it.displayName)}
return jobArray
}
Then I got something like this:
What am I doing wrong? Or Is there another way to accomplish the same thing.
Q2: BTW, If there are three jobs, like j1, j2, j3. j1 and j2 are executed first and in parallel, when one of them are finished, j3 will be executed. so how to do this?
I figured out why.
for (int i = 0; i < getJobs().size(); i++) {
def j=i
jobBranches["branch_${i}"] = {
build job : getJobs()[j]
}
Then it will work!

Criteria building in GORM

if (params.filters) {
def o = JSON.parse(params.filters);
def groupOp = o.groupOp
def fields = o.rules.field
def values = o.rules.data
def op = o.rules.op
println fields
println values
if(groupOp == "AND") {
fields.eachWithIndex {a, i ->
println op[i]
if(op[i].equals( "eq")) {
and{ eq(fields[i], values[i])}
}
if(op[i].equals("ne")) {
and{ ne(fields[i], values[i])}
}
if(op[i].equals("ge")) {
def valu = Double.valueOf( values[i]);
and{ ge(fields[i], valu)}
}
}
}
if(groupOp == "OR") {
fields.eachWithIndex {a, i ->
println op[i]
if(op[i].equals( "eq")) {
println 'eq';
or{ eq(fields[i], values[i])}
}
if(op[i].equals("ne")) {
println 'ne';
or{ ne(fields[i], values[i])}
}
if(op[i].equals("ge")) {
def valu = Double.valueOf( values[i]);
or{ ge(fields[i], valu)}
}
}
}
}
where params.filters is following JSON text.
{
"groupOp":"OR",
"rules":[
{
"field":"foo1",
"op":"le",
"data":"9.5"
},
{
"field":"foo2",
"op":"eq",
"data":"12345-123"
},
{
"field":"foo3",
"op":"cn",
"data":"IDM"
}
]
}
This data is coming from JQuery data grid.
Is there a better way of doing this?
In the code I have just listed only 3 operators, but in real I have 14 operations.
You can use String as Criteria operation, like:
A.withCriteria {
'eq' (id, 1)
}
so you might come to something like
A.withCriteria {
(groupOp) {
for (???) {
(op[i]) (fields[i], parsedVals[i])
}
}
}
Anyway you'll need to sanitize the web-submitted query for only allowed subset of operations. You don't want to receive end execute arbitrary sqlRestriction, right? :D So the code is going to be more complex then this anyway.
Note: wrapping and{} or or {} around single statement has no point, you need to put it around whole block of if-s.
I suggest that you have a look at the source code of the FilterPane plugin. Its service does essentially what you are doing and may give you some ideas for enhancements.

Resources