6
pipeline {
  agent any
  stages {
    stage("foo") {
        steps {
            script {
                env.RELEASE_SCOPE = input message: 'User input required', ok: 'Release!',
                        parameters: [choice(name: 'RELEASE_SCOPE', choices: 'patch\nminor\nmajor', 
                                     description: 'What is the release scope?')]
            }
            echo "${env.RELEASE_SCOPE}"
        }
    }
  }
}

In this above code, The choice are hardcoded (patch\nminor\nmajor) -- My requirement is to dynamically give choice values in the dropdown. I get the values from calling api - Artifacts list (.zip) file names from artifactory In the above example, It request input when we do the build, But i want to do a "Build with parameters"

Please suggest/help on this.

daspilker
  • 8,154
  • 1
  • 35
  • 49
Springhills
  • 360
  • 1
  • 5
  • 12
  • What api do u use or where are the api values stored? – ErikWe Mar 28 '18 at 06:07
  • I am using artifactory api and values are stored in the file - and calling them to a variable by reading the file. I am trying to learn and need a better way. I need to populate values from repo to choice/Active choice parameter – Springhills Mar 28 '18 at 15:18
  • Does this answer your question? [How to call REST from jenkins workflow](https://stackoverflow.com/questions/34682099/how-to-call-rest-from-jenkins-workflow) – mfaani May 05 '20 at 14:55

6 Answers6

6

Depends how you get data from API there will be different options for it, for example let's imagine that you get data as a List of Strings (let's call it releaseScope), in that case your code be following:

...
script {
    def releaseScopeChoices = ''
    releaseScope.each {
        releaseScopeChoices += it + '\n'
    }

    parameters: [choice(name: 'RELEASE_SCOPE', choices: ${releaseScopeChoices}, description: 'What is the release scope?')]
}
...


hope it will help.

BigGinDaHouse
  • 1,282
  • 9
  • 19
  • Thanks a lot. May be that is what i need. Here is my api call and how i used to store the list of values I have a artifactory repo, and it has list of zip file which i want to populate in the choice/active choice parameter Below is the code i used – Springhills Mar 28 '18 at 15:14
  • pipeline { agent { label 'Linuxnode' } stages { stage("call api") { steps { script { sh '''#!/usr/bin/sh VAR=$(curl -s -X POST -k -u uname:pwd \'https://artifactory.com/artifactory/api/search/aql\' -d \'items.find({"repo":"datasources-local"})\') echo $VAR | python -c \'import json,sys; obj=json.load(sys.stdin); for i in range(len(obj["results"])): print obj["results"][i]["name"]\' >> k.txt''' env.WORKSPACE = pwd() def p = readFile "${env.WORKSPACE}/k.txt" println(p) } } – Springhills Mar 28 '18 at 15:16
  • in the above code p - variable will have the list of zip files - i want to populate them to the choice parameter.. please help on this. – Springhills Mar 28 '18 at 15:17
  • you are trying to do all in shell scriptiing, that's wonderful, but let me make your life a bit easier - Jenkins Pipeline let you use Groovy , so consider using that, which in your case will be much easier nicer code and all in the same place and you will not need to use system vars. – BigGinDaHouse Mar 28 '18 at 15:49
  • also, please, if the answer is correct mark it correct – BigGinDaHouse Mar 28 '18 at 15:50
  • Sure.. that helps if you can give something for Jenkins pipeline. Marked correct :) – Springhills Mar 28 '18 at 15:57
  • Hi BigGinDaHouse -- Did you get chance to look into this and give a better script in groovy... – Springhills Mar 28 '18 at 21:43
  • Nope, I did not, sorry – BigGinDaHouse Mar 29 '18 at 12:17
5

This is a cutdown version of what we use. We separate stuff into shared libraries but I have consolidated a bit to make it easier.

Jenkinsfile looks something like this:

#!groovy
@Library('shared') _
def imageList = pipelineChoices.artifactoryArtifactSearchList(repoName, env.BRANCH_NAME)
imageList.add(0, 'build')
properties([
    buildDiscarder(logRotator(numToKeepStr: '20')),
    parameters([
        choice(name: 'ARTIFACT_NAME', choices: imageList.join('\n'), description: '')
    ])
])

Shared library that looks at artifactory, its pretty simple. Essentially make GET Request (And provide auth creds on it) then filter/split result to whittle down to desired values and return list to Jenkinsfile.

import com.cloudbees.groovy.cps.NonCPS
import groovy.json.JsonSlurper
import java.util.regex.Pattern
import java.util.regex.Matcher   

List artifactoryArtifactSearchList(String repoKey, String artifact_name, String artifact_archive, String branchName) {
    // URL components
    String baseUrl = "https://org.jfrog.io/org/api/search/artifact"
    String url = baseUrl + "?name=${artifact_name}&repos=${repoKey}"

    Object responseJson = getRequest(url)

    String regexPattern = "(.+)${artifact_name}-(\\d+).(\\d+).(\\d+).${artifact_archive}\$"

    Pattern regex = ~ regexPattern
    List<String> outlist = responseJson.results.findAll({ it['uri'].matches(regex) })
    List<String> artifactlist=[]

    for (i in outlist) {
        artifactlist.add(i['uri'].tokenize('/')[-1])
    }

    return artifactlist.reverse()
}

// Artifactory Get Request - Consume in other methods
Object getRequest(url_string){

    URL url = url_string.toURL()

    // Open connection
    URLConnection connection = url.openConnection()

    connection.setRequestProperty ("Authorization", basicAuthString())

    // Open input stream
    InputStream inputStream = connection.getInputStream()
    @NonCPS
    json_data = new groovy.json.JsonSlurper().parseText(inputStream.text)
    // Close the stream
    inputStream.close()

    return json_data
}

// Artifactory Get Request - Consume in other methods
Object basicAuthString() {
    // Retrieve password
    String username = "artifactoryMachineUsername"
    String credid = "artifactoryApiKey"
    @NonCPS
    credentials_store = jenkins.model.Jenkins.instance.getExtensionList(
        'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
        )
    credentials_store[0].credentials.each { it ->
        if (it instanceof org.jenkinsci.plugins.plaincredentials.StringCredentials) {
            if (it.getId() == credid) {
                apiKey = it.getSecret()
            }
        }
    }
    // Create authorization header format using Base64 encoding
    String userpass = username + ":" + apiKey;
    String basicAuth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(userpass.getBytes());

    return basicAuth

}
metalisticpain
  • 2,698
  • 16
  • 26
  • I am trying the above script you gave.. but i dont have admin access to put in the library and i am getting "peer not authenticated" error when trying to list artifacts using groovy. I previously tried curl with -k option. That would be great if you can give me a script without library and for Active choice parameters – Springhills Apr 02 '18 at 21:01
  • Just define the same methods in your jenkinsfile, then you don't need to call out to shared libs. – metalisticpain Apr 03 '18 at 08:45
  • @metalisticpain will you be kind enough to explain where the `body.artifacts` come in `choices: body.artifacts.join('\n')`. Also `pipelineChoices` and `imageList.add(0, 'build')`. I could not understand these. Thank you – Manoj Sep 01 '20 at 23:43
  • Looking at this code a few years later and I think you have picked up a typo in my example. Looks like I pieced it together to create a small example but some refs arnt correct. `body.artifacts.join('\n')` should be `imageList.join('\n')` `pipelineChoices` is the filename of the shared library that `artifactoryArtifactSearchList` function lives in. Thats just a call to it. – metalisticpain Sep 02 '20 at 04:47
  • `imageList.add(0, 'build')` adds `build` to the returned list. So say my imageList returns `['image1', 'image2']`, then I add `build` at index 0. So it becomes `['build', 'image1', 'image2']`. I do this because the 'default' option for a choice param is the 1st value. – metalisticpain Sep 02 '20 at 04:52
3

I could achieve it without any plugin:

With Jenkins 2.249.2 using a declarative pipeline, the following pattern prompt the user with a dynamic dropdown menu (for him to choose a branch):

(the surrounding withCredentials bloc is optional, required only if your script and jenkins configuration do use credentials)

node {

    withCredentials([[$class: 'UsernamePasswordMultiBinding',
                  credentialsId: 'user-credential-in-gitlab',
                  usernameVariable: 'GIT_USERNAME',
                  passwordVariable: 'GITLAB_ACCESS_TOKEN']]) {
        BRANCH_NAMES = sh (script: 'git ls-remote -h https://${GIT_USERNAME}:${GITLAB_ACCESS_TOKEN}@dns.name/gitlab/PROJS/PROJ.git | sed \'s/\\(.*\\)\\/\\(.*\\)/\\2/\' ', returnStdout:true).trim()
    }
}
pipeline {

    agent any

    parameters {
        choice(
            name: 'BranchName',
            choices: "${BRANCH_NAMES}",
            description: 'to refresh the list, go to configure, disable "this build has parameters", launch build (without parameters)to reload the list and stop it, then launch it again (with parameters)'
        )
    }

    stages {
        stage("Run Tests") {
            steps {
                sh "echo SUCCESS on ${BranchName}"
            }
        }
    }
}

The drawback is that one should refresh the jenkins configration and use a blank run for the list be refreshed using the script ... Solution (not from me): This limitation can be made less anoying using an aditional parameters used to specifically refresh the values:

parameters {
        booleanParam(name: 'REFRESH_BRANCHES', defaultValue: false, description: 'refresh BRANCH_NAMES branch list and launch no step')
}

then wihtin stage:

stage('a stage') {
   when {
      expression { 
         return ! params.REFRESH_BRANCHES.toBoolean()
      }
   }
   ...
}
user1767316
  • 3,276
  • 3
  • 37
  • 46
1

this is my solution.

def envList
def dockerId
node {
    envList = "defaultValue\n" + sh (script: 'kubectl get namespaces --no-headers -o custom-columns=":metadata.name"', returnStdout: true).trim()
}
pipeline {
    agent any
    parameters {
        choice(choices: "${envList}", name: 'DEPLOYMENT_ENVIRONMENT', description: 'please choose the environment you want to deploy?')
        booleanParam(name: 'SECURITY_SCAN',defaultValue: false, description: 'container vulnerability scan')
    }
0

The example of Jenkinsfile below contains AWS CLI command to get the list of Docker images from AWS ECR dynamically, but it can be replaced with your own command. Active Choices Plug-in is required.

Note! You need to approve the script specified in parameters after first run in "Manage Jenkins" -> "In-process Script Approval", or open job configuration and save it to approve automatically (might require administrator permissions).

properties([
  parameters([[
    $class: 'ChoiceParameter',
    choiceType: 'PT_SINGLE_SELECT',
    name: 'image',
    description: 'Docker image',
    filterLength: 1,
    filterable: false,
    script: [
      $class: 'GroovyScript',
      fallbackScript: [classpath: [], sandbox: false, script: 'return ["none"]'],
      script: [
        classpath: [],
        sandbox: false,
        script: '''\
          def repository = "frontend"
          def aws_ecr_cmd = "aws ecr list-images" +
                            " --repository-name ${repository}" +
                            " --filter tagStatus=TAGGED" +
                            " --query imageIds[*].[imageTag]" +
                            " --region us-east-1 --output text"
          def aws_ecr_out = aws_ecr_cmd.execute() | "sort -V".execute()
          def images = aws_ecr_out.text.tokenize().reverse()
          return images
        '''.stripIndent()
      ]
    ]
  ]])
])

pipeline {
  agent any

  stages {  
    stage('First stage') {
      steps {
        sh 'echo "${image}"'
      }
    }
  }
}
Oleksandr Shmyrko
  • 1,720
  • 17
  • 22
  • I had to use $class: 'ChoiceParameterDefinition' instead of ,$class: 'ChoiceParameter', to avoid the following error: java.lang.UnsupportedOperationException: no known implementation of class hudson.model.ParameterDefinition is named ChoiceParameter ( .... even If I have for now no value yet in my combo using def proc = 'git for-each-ref --format=\'%(refname:short)\' refs/heads/'.execute() to retrieve branch list) Also I found no script available for aproval in the dedicated admin page – user1767316 May 20 '21 at 16:00
  • is there a way for it to update a textbox (multiline) with information selected? – mike01010 May 18 '23 at 16:53
-4
choiceArray = [ "patch" , "minor" , "major" ]

properties([
    parameters([
            choice(choices: choiceArray.collect { "$it\n" }.join(' ') ,
                    description: '',
                    name: 'SOME_CHOICE')

    ])
])