1

I have the following (simplified) Jenkins pipeline code.

jobParams.groovy

List get(Object paramVars = {}) {

    def params = []


    params += [
        choice(
            choices: ['branch', 'tag'],            
            name: 'RELEASE_TYPE'
        ),
        string(
            defaultValue: '',
            name: 'VERSION'
        ),
    ]

    return params
}

pipeline.groovy

def call() {

    properties([
        parameters(
            jobParams.get()
        )
    ])

    pipeline {
        agent { label 'deploy-slave' }

        stages {
            stage('Prepare') {
                steps {
                    script {
                        // Do some logic here and set a job parameter?
                    }
                }
            }
        }
    }
}

This works fine. When the pipeline starts the job parameters are set and available for the next time the job runs.

However, is it also possible to set job parameters dynamically after some logic in a pipeline step?

Pieter Vogelaar
  • 365
  • 2
  • 4
  • 16

4 Answers4

4

It turned out to be pretty easy!

I created a jobProperties.groovy file in my shared pipeline library, which composes the parameter list and calls the properties() function.

def call() {
    params = [
        string(
            defaultValue: '',
            description: 'Version to deploy',
            name: 'VERSION'
        ),
    ]

    if (env.HANDLER == 'ansible') {
        params += [
            string(
                defaultValue: '',
                description: 'DEPLOY_ARGS | Ad hoc "ansible-playbook" args. Example to limit hosts to' +
                    ' deploy to "-l somehost"',
                name: 'DEPLOY_ARGS'
            ),
        ]
    } else if (env.HANDLER == 'capistrano') {
        params += [
            string(
                defaultValue: '',
                description: 'DEPLOY_ARGS | Ad hoc "cap" args. Example to limit hosts to' +
                    ' deploy to "-z somehost"',
                name: 'DEPLOY_ARGS'
            ),
        ]
    }

    properties([
        parameters(
            params
        )
    ])
}

pipeline.groovy

def call() {
    pipeline {
        agent { label 'deploy-slave' }

        stages {
            stage('Prepare') {
                steps {
                    script {
                        jobProperties()
                    }
                }
            }
        }
    }
}

I think that if you don't have a shared pipeline library, the code of jobParams.groovy can be also put directly in the script {} wrapper of the pipeline.

Pieter Vogelaar
  • 365
  • 2
  • 4
  • 16
  • Pieter i am trying to implement this exact same thing - do you have a more complete working example as its just not doing anything for me. – PingCrosby Dec 31 '19 at 12:00
  • @PingCrosby are you using a shared library? Otherwise you can just ignore the `def call` and `jobProperties()` and put the body of the call-function from jobProperties.groovy inside your pipeline script wherever you want to replace the . – Moberg Jan 16 '20 at 10:17
  • Why do you need params for this? You can easily set env variables during a build. `params` are intended to be selected/inserted by the user, who start the job. Please refer to this answer: https://stackoverflow.com/a/68405966/9338441 – artegen Aug 16 '21 at 10:22
  • This is interesting and something I'm looking for that could be simpler than the [ActiveChoices](https://plugins.jenkins.io/uno-choice/) plugin. But if i understand correctly, this would have to be run at least once for the parameters to be created in the UI, yes? – Max Cascone Oct 20 '21 at 20:00
  • @MaxCascone That's correct, after one run it will appear in the UI – Pieter Vogelaar Oct 22 '21 at 10:43
  • dont you need a return in the jobProperties.groovy? – mike01010 May 18 '23 at 15:04
1

It is, but there are some complications as params is an immutable map.

We use a shared library function we wrote when we want to change our params during job execution.

This will probably require admin for script approvals.

The first function is for setting a new string param, or updating an existing one with a new value. The second and third jobs are just interfaces for adding a new option to a choice param for either your current job, or a different job. The fourth is the main grunt for this choice adding logic. (not called directly)

The organizationFolder is based on us using the Github branch source plugin.

    /**
     * Change param value during build
     *
     * @param paramName new or existing param name
     * @param paramValue param value
     * @return nothing
     */
    def setParam(String paramName, String paramValue) {
        List<ParameterValue> newParams = new ArrayList<>();
        newParams.add(new StringParameterValue(paramName, paramValue))
        try {
            $build().addOrReplaceAction($build().getAction(ParametersAction.class).createUpdated(newParams))
        } catch (err) {
            $build().addOrReplaceAction(new ParametersAction(newParams))
        }
    }

    /**
     * Add a new option to choice parameter for the current job
     *
     * @param paramName parameter name
     * @param optionValue option value
     * @return nothing
     */
    def addChoice(String paramName, String optionValue) {
        addChoice($build().getParent(), paramName, optionValue)
    }

    /**
     * Add a new option to choice parameter to the given job
     *
     * @param paramName parameter name
     * @param optionValue option value
     * @return nothing
     */
    def addChoice(String jobName, String paramName, String optionValue) {
        List jobNames = jobName.tokenize("/")
        Job job = ((OrganizationFolder)Jenkins.getInstance().getItem(jobNames[0])).getItem(jobNames[1]).getItem(jobNames[2])

        addChoice(job, paramName, optionValue)
    }

    /**
     * Add a new option to choice parameter to the given job
     * Will be added as the first (default) choice
     * @param job job object
     * @param paramName parameter name
     * @param optionValue option value
     * @return
     */
    def addChoice(Job job, String paramName, String optionValue) {
        ParametersDefinitionProperty paramsJobProperty = job.getProperty(ParametersDefinitionProperty.class);
        ChoiceParameterDefinition oldChoiceParam = (ChoiceParameterDefinition)paramsJobProperty.getParameterDefinition(paramName);
        List<ParameterDefinition> oldJobParams = paramsJobProperty.getParameterDefinitions();
        List<ParameterDefinition> newJobParams = new ArrayList<>();

        for (ParameterDefinition p: oldJobParams) {
            if (!p.getName().equals(paramName)) {
                newJobParams.add(0,p);
            }
        }

        List<String> choices = new ArrayList(oldChoiceParam.getChoices());


        choices.add(0,optionValue);

        ChoiceParameterDefinition newChoiceParam = new ChoiceParameterDefinition(paramName, choices, oldChoiceParam.getDefaultParameterValue().getValue(), oldChoiceParam.getDescription());
        newJobParams.add(newChoiceParam);

        ParametersDefinitionProperty newParamsJobProperty = new ParametersDefinitionProperty(newJobParams);
        job.removeProperty(paramsJobProperty);
        job.addProperty(newParamsJobProperty);
    }
metalisticpain
  • 2,698
  • 16
  • 26
1

As mentioned the params map is immutable, however, as described here Jenkins also creates an environment variable for each build param

So the option I've used to override a build param is to use the environment var rather than the value in the params map eg:

if (environment == "PRD") {
    env.vpc_id = 'vpc-0fc6d952bbbf0000'
}
// Now run your script or command that refers to the environment var
sh './script.sh'
Iain Hunter
  • 4,319
  • 1
  • 27
  • 13
0

I Guess you are talking about deciding downstream parameters dynamically for a job. It surely can be done the way its depicted in the code below.

@Library("shared-library") _
Map buildDetails = [:]
downStreamParams = [["\$class: 'StringParameterValue', name: 'TARGET_WORKSPACE', value: 'prod'"]]
pipeline {
    agent {
        label 'builds'
    }
    stages {
        stage('Get Details'){
            steps {
                script {
                    buildDetails = [
                      "releaseType":"minor",
                      "workspace":"prod",
                      "featureType":"ENHANCEMENT",
                      "PARAMS=Jenkins_Controller_Image":true,
                      "PARAMS=APPLY":true,
                      "PARAMS=PLUGIN_CLEANUP":true,
                      "PARAMS=RESTART":true,
                    ]
                    buildDetails.each{ key, value ->
                        println("$key:$value")
                        if(key.contains("PARAMS=")){
                            "[\$class: 'BooleanParameterValue', name: \"${key.split('=')[1]}\", value: true]"
                        }
                    }
                }
            }
        }
        stage("Build"){
            steps{
                script{
                    job = "build( job: jobFullName,parameters: ${downStreamParams},propagate: true,wait: true)"
                    evaluate(job)
                }
            }
        }
    }
}
siyengar
  • 1
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 05 '22 at 08:57