Jenkins dynamic pipeline parameters - jenkins

I have a jenkins pipeline which gives the user a list of keys from Consul, the user should choose one option (using active choice parameter), I need the pipeline to dynamically generate the list of "sub keys" (depends on the user first choice, for example: key/path/${user_choice} ) and let the user to choose a sub key
my current code his:
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = ['/bin/bash', '-c', 'consul kv get -keys --http-addr=X key/path/ | awk -F / \'{print $(NF-1)}\''].execute()
proc.consumeProcessOutput(sout, serr)
proc.waitFor()
return sout.tokenize()
It works fine till now, but "active choice reactive parameter" is not acting dynamically and refuse to relate to the user's first choice. I haven't found any other useful plugin
Any help?
thanks :)

From what I know you can't have an interactive command prompt in Jenkins. However, you can use the input step to get feedback and use it throughout the pipeline like so:
def keys = sh(script: 'consul kv get -keys --http-addr=X key/path/ | awk -F / \'{print $(NF-1)}\'', returnStdout: true).trim().tokenize('\n')
def choice = input message: 'Please choose a sub-key', parameters: [choice(choices: keys, description: '', name: 'Subkeys')]
println "You chose $choice"

Related

Use groovy-variable in Jenkins-Batchscript

I have this pipeline-script:
script {
def resultFile = "logs/Resharper-Warnings.out.xml"
bat (script:
'''
set PlotFrameworkVersion=v4.5.1
set ReferencePath='C:/lib'
call ".../InspectCode.exe" "Path/To/My.sln" -a -o="${resultFile}"
'''
)
recordIssues(
qualityGates: [[threshold: 1, type: 'TOTAL', unstable: true]],
tools: [resharperInspectCode(pattern: "${resultFile}")]
)
}
As you can see I want to use the variable resultFile within both the recordIssues-step as well as the bat-step. When I execute it, I get the following log:
Inspection report was written to D:\Workspace\${resultFile}
so the variable isn´t expanded correctly. The recorsIssues-step however does parse the variable, as seen in the log:
Searching for all files in 'D:\Workspace' that match the pattern 'logs/Resharper-Warnings.out.xml'
So how do I use the variable correctly within my bat-step?
It´s just about concatenating strings in groovy, which we can achieve using +. I ended up with this which isn´t the neatest way but it gets its job done:
''' set PlotFrameworkVersion=v4.5.1
set ReferencePath='C:/lib'
call ".../InspectCode.exe" "Path/To/My.sln" ''' + "-a -o=${resultFile}"

Jenkins Groovy Active Choice Reactive Parameter referenced parameter creates error

Okay, so right now I have an Active Choice Parameter, let's call it 'SKU' that is a list of SKUs a user may select from.
I have a Active Choices Reactive Parameter that is referencing the value of SKU and using that value to query files through the GitHub API to generate a list of titles.
When I run my script with a real, but hardcoded, value for the SKU it works.
When I run my script trying to call in the SKU parameter so that it is reactive to what the user originally picks -- using ${SKU} -- it goes to the fallback which I have set to just generate "error":
Groovy script (with hardcoding):
token = "value of token"
def command = """
curl -H 'Authorization: token ${token}' -H "Accept: application/vnd.github.VERSION.raw" https://api.github.com/repos/org/SKU/path/SKU-File.xml
"""
process = [ 'bash', '-c', command].execute()
parser = new XmlSlurper()
def rootNode = parser.parseText(process.text)
def count = rootNode.children().size() - 1
titles = []
for (i in 1..count) {
title = rootNode.children()[i].title
titles += title
}
return titles
Note: This works -- with the exception that instead of switching the list of titles depending on the SKU you select, it is always the same list from the hardcoded SKU.
Groovy script (without hardcoding):
token = "value of token"
def command = """
curl -H 'Authorization: token ${token}' -H "Accept: application/vnd.github.VERSION.raw" https://api.github.com/repos/org/${SKU}/path/${SKU}-File.xml
"""
process = [ 'bash', '-c', command].execute()
parser = new XmlSlurper()
def rootNode = parser.parseText(process.text)
def count = rootNode.children().size() - 1
titles = []
for (i in 1..count) {
title = rootNode.children()[i].title.toString()
titles += title
}
return titles
Note: This fails and defaults to the Fallback script.
Another important note: This is working when I try the script console, however, since the script console is not able to reference the other parameters from my project, I have to harcode in a SKU = "SKU" variable, so not sure it's as equivalent as I'd like it to be.
Fallback script:
return ["error"]
Note also, I have another Active Choice Reactive Parameter also pointing to the SKU and it is working just fine. But in that I'm not calling the SKU in a curl command. I can't quite figure out what's going wrong here, or if I'm referencing something inappropriately.
I've tried converting the ${SKU} parameter to a string as a variable before passing it to the command variable and that didn't change the outcome.
So here's the answer:
Not all of my SKUs were accurately pulling titles based on the same criteria as the SKU I was hardcoding.
It's a bit strange that an error on one SKU would pull apart the entire ability to get an accurate list -- e.g. if SKU1 had an error, toggling to SKU2 gave an error even if SKU2 was working while hardcoded. I've adjusted my script to work for both SKU1 and SKU2 and now it is working as intended.
This is a 'gotcha' to look out for!

String Manipulation in Groovy

I am writing a groovy script which will return me the list of Task-Definition in AWS ECS service,
Here is the code snippet for the same
def p = 'aws ecs list-task-definitions --family-prefix test'.execute() | 'jq .taskDefinitionArns[]'.execute()
p.waitFor()
print p.text
and the output is
"arn:aws:ecs:us-west-2:496712345678:task-definition/test:2"
"arn:aws:ecs:us-west-2:496712345678:task-definition/test:3"
Now I want to capture only the last part of the result, i.e test:2, test:3 and so on without double quotes
How can I do that using Groovy language as I have to use it in Jenkins's active choice reactive parameter plugin
Assuming:
​def text = "arn:aws:ecs:us-west-2:496712345678:task-definition/test:2" + "\n" + "arn:aws:ecs:us-west-2:496712345678:task-definition/test:3"​​​​​​​​​​​​
Try :
text​.split("\n")​​​​​​​​​​​​​.collect {c -> c.split("/").last()}​​​​​​
This prints a list of [test:2, test:3]
If you want it in one line and not in an list, use:
text​.split("\n")​​​​​​​​​​​​​.collect {c -> c.split("/").last()}​​​​​.join(",")​
This prints: test:2,test:3
Update
Due to OP's comment, the answer after all should look something like:
def p = 'aws ecs list-task-definitions --family-prefix test'.execute() | 'jq .taskDefinitionArns[]'.execute()
p.waitFor()
def text = p.text
println text​.split("\n")​​​​​​​​​​​​​.collect {c -> c.split("/").last()}​​​​​​
You can split using / and get the last element:
def p = "arn:aws:ecs:us-west-2:496712345678:task-definition/test:2"
def result = p.split("/").last()
Just adding another Style :
String.metaClass.getMyString{-> delegate.substring(delegate.lastIndexOf("/")+1, delegate.length()-1).replace(":", "")}
println p.text.readLines()*.getMyString().join(" ")
Happy Learning... ! :)

Azure DevOps Field Values from list of builds

We are using Azure DevOps 2019 on-prem in our firm, and I would like to create an option box field in our Bug work item, and I want it to be a combo-box where the values are builds from all the build definitions under the project.
From checking the documentation of the system, I did not find any option to how to do it, and ether if it would be better to query the System through the API, or Query the DB.
I don't think there is a built-in feature like this.
What you can do, is to create a string field that takes the values from the gloabllist, in the globallist create in the first time a globallist with the project name, for example:
<GLOBALLIST name="MyProject-builds">
</GLOBALLIST>
Now you can use PowerShell to get the build definitions for this project, and update this globallist with the values:
Param(
[string]$collection = "http://tfs-server:8080/tfs/collection",
[string]$project = "MyProject",
[string]$filePath = "C:\Globallist.xml"
)
$url = "$collection/$project/_apis/build/definitions?api-version=4.0"
$builds = (Invoke-RestMethod -Uri $url -Method Get -UseDefaultCredentials -ContentType application/json).value.name
witadmin exportgloballist /collection:$collection /f:$filePath
[xml]$gloabllist = Get-Content $filePath
$gloabllist.GLOBALLISTS.GLOBALLIST.Where({ $_.name -eq "$project-builds" }).LISTITEM | %{ $_.ParentNode.RemoveChild($_) | Out-Null }
$node = $gloabllist.GLOBALLISTS.GLOBALLIST.Where({ $_.name -eq "$project-builds" })
$builds.ForEach({
$child = $gloabllist.CreateElement("LISTITEM")
$att = $gloabllist.CreateAttribute("value")
$child.Attributes.Append($att)
$child.value = "$_"
$node.AppendChild($child)
})
$gloabllist.Save($filePath)
witadmin importgloballist /collection:$collection /f:$filePath
You can set a scheduled build that tun this script each day to be updated all the time.
You can also improve the script to get all the projects, itreate them, get the build definitions names and update the globallist file.

How can I force Jenkins Blue Ocean to display print output instead of "Print Message"?

In the below screenshot some debug entries display the output text (with - Print Message at the end) while others simply display Print Message. To view these you have to expand the step to see the output.
All lines are using the format print "TEXT HERE". I've tried using print, println, and echo. All have the same output.
Why do these sometimes display the message, while others force it into a collapsed section? Is it possible to configure this to always show? The normal non-Blue Ocean Jenkins interface displays fine but there is a lot of verbosity.
This seems to be a known issue:
https://issues.jenkins-ci.org/browse/JENKINS-53649
It looks like that BlueOcean does not handle the Groovy GStrings correctly. This is what I've observed:
A simple:
echo "hello world"
will work as expected and will display correctly.
Whereas a templated string with variables, like:
echo "hello ${some_variable}"
will hide the message under a "Print Message" dropdown.
See also this answer.
It appears that if echo uses a variable with value from params or environment (i.e. "params.*"), then step label gets "Print message" name instead of actual value being echoed. It does not matter if the variable itself is a String or not. Even explicitly converting the params value to String does not help.
String param_str
String text_var_2
parameters {
string(name: 'str_param', defaultValue: 'no value')
}
param_str = params.str_param.toString()
echo "string text in double quotes is ${param_str}"
echo "simple quoted string is here"
echo 'simple quoted string is here'
echo 'Single quoted with str ' + param_str + ' is here'
echo param_str
text_var_2 = 'Single quoted str ' + param_str + ' combined'
echo "GString global text2 is ${text_var_2}"
echo 'String global text2 is' + text_var_2
BlueOcean shows simple quoted strings in step label, but everything else as "Print message".
BlueOcean output
Note that 'normal' variables (strings, integers) are not included into this example, but they are also shown in the label normally. So if you have a code like this
def text_str = 'Some string'
def int_var = 1+2
echo text_str + ' is here'
echo int_var
These will be shown on the label.
And indeed it appears to be a known Jenkins issue as stated in a previous answer.
This is a known BlueOcean bug. The console output in the "classic" view interpolates variables correctly.
One workaround is to use the label parameter of the sh step:
def message = 'Hello World'
sh(script: "echo $message", label: message)
I tried lots of things and seems the moment an environment variable is going to be displayed, it uses Print Message instead the text.
Another workaround would be to split the multiline string into an array and iterate over it :-
String[] splitData = MULTI_LINE_STRING.split("\n");
for (String eachSplit : splitData) {
print(eachSplit);
}

Resources