56

I am studying capabilities of Jenkins Pipeline:Multibranch. It is said that a recently introduced properties step might be useful there, but I can't catch how it works and what is its purpose.

Its hint message doesn't seem to be very clear:

Updates the properties of the job which runs this step. Mainly useful from multibranch workflows, so that Jenkinsfile itself can encode what would otherwise be static job configuration.

So I created a new Pipeline with this as a script (pasted directly into Jenkins not in SCM):

properties [[$class: 'ParametersDefinitionProperty',
   parameterDefinitions: [[$class: 'StringParameterDefinition',
       defaultValue: '', description: '', name: 'PARAM1']]
]]

I ran it and nothing happened, job didn't received a new parameter and even if it did I don't get why I might need this. Could anyone please explain?

UPDATE1: I tried putting a dummy Pipeline with properties step into my git repo, then configured a multibranch job.

println 1
properties [[$class: 'ParametersDefinitionProperty', parameterDefinitions: [[$class: 'StringParameterDefinition', defaultValue: 'str1', description: '', name: 'PARAM1']]], [$class: 'RebuildSettings', autoRebuild: false, rebuildDisabled: false]]
println 2

It found my branch, created a job but the build failed with:

groovy.lang.MissingPropertyException: No such property: properties for class: groovy.lang.Binding
at groovy.lang.Binding.getVariable(Binding.java:62)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:185)
at org.kohsuke.groovy.sandbox.impl.Checker$4.call(Checker.java:241)
at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:238)
at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.getProperty(SandboxInvoker.java:23)
at com.cloudbees.groovy.cps.impl.PropertyAccessBlock.rawGet(PropertyAccessBlock.java:17)
at WorkflowScript.run(WorkflowScript:2)
at ___cps.transform___(Native Method)
at com.cloudbees.groovy.cps.impl.PropertyishBlock$ContinuationImpl.get(PropertyishBlock.java:62)
at com.cloudbees.groovy.cps.LValueBlock$GetAdapter.receive(LValueBlock.java:30)
at com.cloudbees.groovy.cps.impl.PropertyishBlock$ContinuationImpl.fixName(PropertyishBlock.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
at com.cloudbees.groovy.cps.Next.step(Next.java:58)
at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:154)
at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:19)
at org.jenkinsci.plugins.workflow.cps.SandboxContinuable$1.call(SandboxContinuable.java:33)
at org.jenkinsci.plugins.workflow.cps.SandboxContinuable$1.call(SandboxContinuable.java:30)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:106)
at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:30)
at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:164)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:277)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$000(CpsThreadGroup.java:77)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:186)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:184)
at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:47)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:112)
at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)

UPDATE2: when I put the same script (as in UPD1) back into Jenkins and runs it, it asked for new permission method groovy.lang.GroovyObject getProperty java.lang.String. I approved it, the build went green, however, still no changes to the job configuration appeared.

My env is: Jenkins 1.625.3, Pipeline+Multibranch 1.13

Christopher Orr
  • 110,418
  • 27
  • 198
  • 193
izzekil
  • 5,781
  • 2
  • 36
  • 38

3 Answers3

79

Using properties with explicit method syntax will work, i.e.:
properties( [ ... ] ) rather than properties [ ... ]

Alternatively, it will work without if you specify the parameter name, e.g.:

properties properties: [ ... ]

For example defining three properties is as easy as :

properties([
  parameters([
    string(name: 'submodule', defaultValue: ''),
    string(name: 'submodule_branch', defaultValue: ''),
    string(name: 'commit_sha', defaultValue: ''),
  ])
])

/* Accessible then with : params.submodule, params.submodule_branch...  */
daminetreg
  • 9,724
  • 1
  • 23
  • 15
Christopher Orr
  • 110,418
  • 27
  • 198
  • 193
  • 3
    Thanks, surprisingly it works. I mean that Snippet Generator generates an incorrect statement and at the same time groovy should allow to omit parentheses if there is only one parameter given. Do you know why it behaves like this? – izzekil Feb 18 '16 at 02:04
  • @izzekil Yeah, it does seem weird that a step with only one parameter doesn't work as expected. I don't know why that is. You could try [filing a bug](https://wiki.jenkins-ci.org/display/JENKINS/How+to+report+an+issue) against the `workflow-plugin` component and specify the `multibranch` label. – Christopher Orr Feb 21 '16 at 12:43
  • 1
    Long filed as JENKINS-29711. – Jesse Glick Feb 22 '16 at 15:51
  • 4
    And **do not** approve `method groovy.lang.GroovyObject getProperty java.lang.String`, it is not safe. More recent _Script Security_ releases will warn you sternly before doing that. – Jesse Glick Feb 22 '16 at 15:52
  • 5
    The snippet generator has since been fixed. – Jesse Glick Nov 23 '16 at 18:23
  • 1
    @ChristopherOrr Is it possible to update only specific build parameters using properties? – NITHESH K Sep 05 '17 at 12:18
  • Just keep the parameters you want to update and remove the others. Already existing parameters will not get deleted. – katrash Jun 11 '18 at 21:03
  • 4
    Yeah, resurrecting this post. katrash - you are wrong, calling "properties()" is an overwrite, not an edit, there is a documented issue open against Jenkins: https://issues.jenkins-ci.org/browse/JENKINS-43758 ...including an ugly and cumbersome workaround. @ChristopherOrr I am also looking for ways to modify existing properties (scenario - I write a library a developer calls. The developer's code has already set some properties, e.g. "parameters". I need to add a disallowConcurrentBuilds() - if I call "properties([disallowConcurrentBuilds()]) the developer-set properties are wiped out. – Mike Rysanek Feb 28 '19 at 21:38
  • @ChristopherOrr Hello, can I know where this code is supposed to be added? I believe its this one - https://www.jenkins.io/doc/pipeline/steps/workflow-multibranch/#properties-set-job-properties? – Nevin Madhukar K May 11 '22 at 05:26
5

There is an excellent example of parameters usage in the official Jenkins doc. Check the pipeline below:

pipeline {
    agent any
    parameters {
        string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')

        text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')

        booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')

        choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')

        password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
    }
    stages {
        stage('Example') {
            steps {
                echo "Hello ${params.PERSON}"

                echo "Biography: ${params.BIOGRAPHY}"

                echo "Toggle: ${params.TOGGLE}"

                echo "Choice: ${params.CHOICE}"

                echo "Password: ${params.PASSWORD}"
            }
        }
    }
}

Thanks to e.g. boolean parameter which generates checkbox in Jenkins GUI you can conditionally run / skip tests:

pipeline {
    agent any
    parameters {
        booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Should we run tests before deployment?')
    }

    stages {
        stage('Test') {
            when {
                expression {
                    return params.RUN_TESTS
                }
            }

            steps {
                sh '${FABRIC} test'
            }
        }

        stage('Deploy') {
            steps {
                sh '${FABRIC} deploy'
            }
        }
    }
}
michal-michalak
  • 827
  • 10
  • 6
4

Multiple choice in Jenkins scripted pipeline

properties([
  parameters([
        choice(choices: 'sprint_6\nsprint_7\nsprint_8\nSprint_9', description: 'Select branch to Build', name: 'Branch'),
        choice(choices: 'No\nYes', , name: 'choice2'),
        choice(choices: 'No\nYes', name: 'choice3')
  ])
])