Jenkinsfile: parameter precedence - jenkins

Say I have a Jenkinsfile with stages based on a conditional such as the following. Then, in the Jenkins UI for a particular Pipeline Project, I may or may not have a (Global Variable String Parameter) parameter named CITY defined for a particular job.
If the CITY parameter is defined in the Jenkins project in the UI, I'd like it to use whatever city the user inputs. If the user doesn't input anything, I'd like it to default to a value in "Global Properties" (in "Manage Jenkins > Configure System > Global Properties")
If the CITY parameter is NOT defined in the UI (like if someone forgot to define that parameter in the Pipeline Project UI), I'd like it to somehow default to a value defined in the Jenkinsfile.
Simply put, I'd like to define a default in the Jenkinsfile such that if someone forgets to configure a Global Variable String Parameter in the Job UI, the default will be used.
I'm pretty new to Jenkins and Groovy, I'm not quite sure how to do this. How can I go about defining a default parameter in a Jenkinsfile that can be overridden by user input in the UI or a default from "Global Properties"? Any advice is appreciated.
pipeline {
agent any
stages {
stage('Always') {
steps {
script {
sh 'echo Welcome CITY=$CITY'
}
}
stage('Chicago') {
when {
expression {
params.CITY == "CHICAGO"
}
}
steps {
script {
sh 'echo Welcome to Chicago "CITY=$CITY"'
}
}
}
stage('NYC') {
when {
expression {
params.CITY == "NYC"
}
}
steps {
script {
sh 'echo Welcome to NYC "CITY=$CITY"'
}
}
}
}
}

there is no need to declare each city just add a string parameter with a default value and create a single stage that will take the default parameter if the user did not enter one
if this works for you please mark answer as correct :-)
pipeline {
agent any
parameters {
string(name: 'CITY', defaultValue: 'TEL_AVIV', description: 'this is the default if user dosnt enter parameter in the UI')
}
stages {
stage('CITY') {
steps {
script {
sh 'echo Welcome $CITY'
}
}
}
}
}

Related

Can Jenkins pipelines have variable stages?

From my experience with Jenkins declarative-syntax pipelines, I'm aware that you can conditionally skip a stage with a when clause. E.g.:
run_one = true
run_two = false
run_three = true
pipeline {
agent any
stages {
stage('one') {
when {
expression { run_one }
}
steps {
echo 'one'
}
}
stage('two') {
when {
expression { run_two }
}
steps {
echo 'two'
}
}
stage('three') {
when {
expression { run_three }
}
steps {
echo 'three'
}
}
}
}
...in the above code block, there are three stages, one, two, and three, each of whose execution is conditional on a boolean variable.
I.e. the paradigm is that there is a fixed superset of known stages, of which individual stages may be conditionally skipped.
Does Jenkins pipeline script support a model where there is no fixed superset of known stages, and stages can be "looked up" for conditional execution?
To phrase it as pseudocode, is something along the lines of the following possible:
my_list = list populated _somehow_, maybe reading a file, maybe Jenkins build params, etc.
pipeline {
agent any
stages {
if (stage(my_list[0]) exists) {
run(stage(my_list[0]))
}
if (stage(my_list[1]) exists) {
run(stage(my_list[1]))
}
if (stage(my_list[2]) exists) {
run(stage(my_list[2]))
}
}
}
?
I think another way to think about what I'm asking is: is there a way to dynamically build a pipeline from some dynamic assembly of stages?
For dynamic stages you could write either a fully scripted pipeline or use a declarative pipeline with a scripted section (e. g. by using the script {…} step or calling your own function). For an overview see Declarative versus Scripted Pipeline syntax and Pipeline syntax overview.
Declarative pipeline is better supported by Blue Ocean so I personally would use that as a starting point. Disadvantage might be that you need to have a fixed root stage, but I usually name that "start" or "init" so it doesn't look too awkward.
In scripted sections you can call stage as a function, so it can be used completely dynamic.
pipeline {
agent any
stages {
stage('start') {
steps {
createDynamicStages()
}
}
}
}
void createDynamicStages() {
// Stage list could be read from a file or whatever
def stageList = ['foo', 'bar']
for( stageName in stageList ) {
stage( stageName ) {
echo "Hello from stage $stageName"
}
}
}
This shows in Blue Ocean like this:

How to run jenkins pipeline on all or some servers based

I have a jenkins pipeline that copy file to a server. In job, i have defined 3 servers with the IPs.
What i need to achieve is that
A user can choose on which server to deploy the copy by typing yes or no under the depoly_on_server_x.
In my original pipeline, i'm using a list of IP - But the request is as I mentioned above
How can I define the request?
Thanks
server_1_IP - '1.1.1.1'
server_2_IP - '1.1.1.2'
server_3_IP - '1.1.1.3'
deploy_on_server_1 = 'yes'
deploy_on_server_2 = 'yes'
deploy_on_server_3 = 'no'
pipeline {
agent { label 'client-1' }
stages {
stage('Connect to git') {
steps {
git branch: 'xxxx', credentialsId: 'yyy', url: 'https://zzzz'
}
}
stage ('Copy file') {
when { deploy == yes }
steps {
dir('folder_a') {
file_copy(server_list)
}
}
}
}
}
def file_copy(list) {
list.each { item ->
sh "echo Copy file"
sh "scp 11.txt user#${item}:/data/"
}
}
How about using checkboxes instead?
You can use the Extended Choice Parameter to create a checkbox list based on the server values, when the user builds the job he selects the relevant servers, this list of selected servers is propagated to the job with the selected values, which you can then use for your logic.
Something like:
pipeline {
agent { label 'client-1' }
parameters {
extendedChoice(name: 'Servers', description: 'Select servers for deployment', multiSelectDelimiter: ',',
type: 'PT_CHECKBOX', value: '1.1.1.1,1.1.1.2 ,1.1.1.3', visibleItemCount: 5)
}
stages {
stage('Connect to git') {
steps {
git branch: 'xxxx', credentialsId: 'yyy', url: 'https://zzzz'
}
}
stage ('Copy files') {
steps {
dir('folder_a') {
script{
params.Servers.split(',').each { server ->
sh "echo Copy file to ${server}"
sh "scp 11.txt user#${server}:/data/"
}
}
}
}
}
}
}
In the UI it will look like:
You can also use a multi select select-list instead of checkboxes, or if you want to allow only a single value you can use radio buttons or a single select select-list.
If you want the user to see different values then those that will be used in the code it is also possible because you have the ability to manipulate the input value using groovy before using it.
For example if you want the user options to be <Hostname>-<IP> you can update the parameter value to be something like value: 'server1-1.1.1.1,server2-2.2.2.2', then in your code extract the relevant ip from the given values:
script {
params.Servers.split(',').each { item ->
server = item.split('-').last()
sh "echo Copy file to ${server}"
sh "scp 11.txt user#${server}:/data/"
}
}

Pass variable to JobDsl seed job (Jenkins) in scriptText?

I am working on a project and i have to configure a jenkins using JCasC (config as code plugin).
I have to create a job BUT i can't pass variables in the script.
My code:
freeStyleJob("SEED") {
parameters {
stringParam("MY_PARAMETER", "defaultValue", "A parameter")
}
steps {
jobDsl {
scriptText('''
job("seedJOB") {
displayName('${MY_PARAMETER}') // don't work
description("${MY_PARAMETER}") // don't work
//description("$MY_PARAMETER") // don't work
//description('$MY_PARAMETER') // don't work
// i tried to use triple full quotes instead of triple single quote but it's not working...
... here the job...
'''.stripIndent())
}
}
EDIT: BEST SOLUTION HERE:
i'm writing groovy code in """ quotes so if I want to evaluate variable : I don't have to put ${} just write your variable name:
With the solution:
freeStyleJob("SEED") {
parameters {
stringParam("MY_PARAMETER", "defaultValue", "A parameter")
}
steps {
jobDsl {
scriptText('''
job("seedJOB") {
displayName('MY_PARAMETER) // solution
... here the job...
'''.stripIndent())
}
}
easy!
May you could write it to a file ? You'll get something like that in your step:
steps {
shell('echo $DISPLAY_NAME > display_name.txt')
jobDsl {
scriptText('''
job("seedjob") {
String jobname = readFileFromWorkspace('display_name.txt').trim()
displayName(jobname)
}
'''.stripIndent())
}
}
You could also use a .properties file to do it more properly.

How to dynamically set environment variables in scripted jenkins pipeline?

I'm trying to dynamically set environment variables in the jenkins pipeline script.
I'm using a combination of .groovy and .jenkinsfile scripts to generate the stage{} definitions for a pipeline as DRY as possible.
I have a method below:
def generateStage(nameOfTestSet, pathToTestSet, machineLabel, envVarName, envVarValue)
{
echo "Generating stage for ${nameOfTestSet} on ${machineLabel}"
return node("${machineLabel}") {
stage(nameOfTestSet)
{
/////// Area of interest ////////////
environment {
"${envVarName} = ${envVarValue}"
}
/////////////////////////////////////
try {
echo "Would run: "+pathToTestSet
} finally {
echo "Archive results here"
}
}
}
}
There's some wrapper code running this, but abstracting away we'd have the caller essentially use:
generateStage("SimpleTestSuite", "path.to.test", "MachineA", "SOME_ENV_VAR", "ENV_VALUE")
Where the last two parameters are the environment name (SOME_ENV_VAR) and the value (ENV_VALUE)
The equivalent declarative code would be:
stage("SimpleTestSuite")
{
agent {
label "MachineA"
}
environment = {
SOME_ENV_VAR = ENV_VALUE
}
steps {
echo "Would run" + "path.to.test"
}
post {
always {
echo "Archive results"
}
}
}
However, when running this script, the environment syntax in first code block doesn't seem to affect the actual execution at all. If I echo the ${SOME_ENV_VAR} (or even echo ${envVarName} in case it took this variable name as the actual environment value) they both return null.
I'm wondering what's the best way to make this environment{} section as DRY / dynamic as possible?
I would prefer it if there's an extendable solution that can take in a list of environmentName=Value pairs, as this would be more general case.
Note: I have tried the withEnv[] solution for scripted pipelines, however this seems to have the same issue.
I figured out the solution to this.
It is to use the withEnv([]) step.
def generateStage(nameOfTestSet, pathToTestSet, machineLabel, listOfEnvVarDeclarations=[])
{
echo "Generating stage for ${nameOfTestSet} on ${machineLabel}"
return node("${machineLabel}") {
stage(nameOfTestSet)
{
withEnv(listOfEnvVarDeclarations) {
try {
echo "Would run: "+pathToTestSet
} finally {
echo "Archive results here"
}
}
}
}
}
And the caller method would be:
generateStage("SimpleTestSuite", "path.to.test", "MachineA", ["SOME_ENV_VAR=\"ENV_VALUE\""])
Since the withEnv([]) step can take in multiple environment variables, we can also do:
generateStage("SimpleTestSuite", "path.to.test", "MachineA", ["SOME_ENV_VAR=\"ENV_VALUE\"", "SECOND_VAR=\"SECOND_VAL\""])
And this would be valid and should work.

Jenkins Declarative Pipeline detect first run and fail when choice parameters present

I often write Declarative Pipeline jobs where I setup parameters such as "choice". The first time I run the job, it blindly executes using the first value in the list. I don't want that to happen. I want to detect this case and not continue the job if a user didn't select a real value.
I thought about using "SELECT_VALUE" as the first item in the list and then fail the job if that is the value. I know I can use a 'when' condition on each stage, but I'd rather not have to copy that expression to each stage in the pipeline. I'd like to fail the whole job with one check up front.
I don't like the UI for 'input' tasks because the controls are hidden until you hover over a running stage.
What is the best way to validate arguments with a Declarative Pipeline? Is there a better way to detect when the job is run for the first time and stop?
I've been trying to figure this out myself and it looks like the pipeline runs with a fully populated parameters list.
So, the answer to your choice option is to make the first item a value like "please select option" and have your code use when to check that
For example
def paramset = true
pipeline {
parameters {
choice(choices: ['select','test','proof', 'prod'], name: 'ENVI')
}
stages {
stage ('check') {
when { expression { return params.choice.ENVI == 'select' }
steps {
script {
echo "Missing parameters"
paramset = false
}
}
}
stage ('step 1') {
when { expression { return paramset }
steps {
script {
echo "Doing step 1"
}
}
}
stage ('step 2') {
when { expression { return paramset }
steps {
script {
echo "Doing step 2"
}
}
}
stage ('step 3') {
when { expression { return paramset }
steps {
script {
echo "Doing step 3"
}
}
}
}
}

Resources