5

I'm trying to add a dynamic choice parameter to a Jenkinsfile with declarative syntax, using something like this:

def myChoices = listBranchesFromGithub(MY_REPO)

pipeline {
    parameters {
        choice(name: 'mychoice', choices: myChoices)
    }
}

but listBranchesFromGithub(MY_REPO) is evaluated once (when the Jenkinsfile is processed), not everytime I run the job.

Is there a way to populate choices everytime the job is triggered?

Albert
  • 61
  • 1
  • 7
  • did you ever figure this out? – Mike Aug 29 '17 at 13:42
  • No, the plugin is no longer distributed so it's unlikely to be supported in jenkins 2. Maybe [Extended choice parameter](https://wiki.jenkins.io/display/JENKINS/Extended+Choice+Parameter+plugin) plugin [can do something similar](https://stackoverflow.com/a/46122406/2029354), but only with scripted pipelies (not declarative) and [it doesn't play well with blue ocean](https://gist.github.com/jgraglia/44a7443847cff6f0d87387a46c7bb82f#gistcomment-2158077) – Albert Oct 05 '17 at 10:27

1 Answers1

0

You have two options.

In your jenkinsfile, call properties again after processing.

#!groovy
@Library('shared') _

properties()

// Do pipeline

properties()

def properties() {
    // Set Jenkins job properties
    properties([
        buildDiscarder(logRotator(numToKeepStr: '20')),
        parameters([//params])
    ])
}

Pro's, simple? Cons: Only works if the steps succeed and do not exit early.

Alternative: Dynamically adjust during the build (We put these functions in a shared library and call them whenever we need to update a param, such as an artifact list). Note: may required script security approvals

/**
 * 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))
    $build().addOrReplaceAction($build().getAction(ParametersAction.class).createUpdated(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 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(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);
}

In the above situations, these methods live under /vars within the shared library. /vars/jobParams.groovy. So from Jenkinsfile or another shared library, you can invoke these using the following syntax:

jobParams.addChoice("ARTIFACT_NAME", "da-name")
metalisticpain
  • 2,698
  • 16
  • 26
  • Do you call setParam() and addChoice() from Jenkinsfile somehow? – Andrew Gray Mar 05 '18 at 05:19
  • If so, how? Could you provide an example Jenkinsfile detailing how to do this? – Andrew Gray Mar 05 '18 at 05:30
  • yep, so setParam() and addChoice() live in another shared lib under /vars (/vars/jobParams.groovy). From jenkinsfile or another shared library just call jobParams.addChoice("ARTIFACT_NAME", "the-name") – metalisticpain Mar 06 '18 at 04:47
  • WHERE in the Jenkinsfile do you "just call jobParams.addChoice("ARTIFACT_NAME", "the-name")"??? – Andrew Gray Aug 02 '18 at 04:59
  • Anywhere you like. It becomes a custom 'step' essentially. So for instance we build our artefact and publish it in a build stage. Before we finish the build stage, we invoke this function to add the new artefact we built to the list. We then continue on to deploy. But once you have these methods defined, you can invoke them at any point in your pipeline. Make sure you have shared libs setup and have the _ present `@Library('your-share-lib-name-set-in-jenkins-settings') _` The underscore auto loads each lib under vars/ as a global singleton. – metalisticpain Aug 02 '18 at 05:41
  • Can you please provide the jenkinsfile that demonstrate this working. I want to be able to set the parameters dynamically BEFORE the CURRENT build starts. Pipeline lifecycle doesn't currently see to support this. – Andrew Gray Aug 02 '18 at 05:59
  • No this solution is for dynamically adjusting during a build. My suggestions are for ways of handling due to pipeline limitations. You can set these during the build to ensure the next build has the correct values. For instance an artefact list (choice list) where you build the artefact. You need to add that to the choices immediately to ensure its available in the list for the very next build. Otherwise you need ot run a 2nd build to get the list to populate. – metalisticpain Aug 02 '18 at 06:12
  • Won't do it for me. I need to dynamically set a choice parameter for the CURRENT build BEFORE the build starts. i.e. After I click Build Parameters – Andrew Gray Aug 02 '18 at 06:23