31

A Jenkins pipeline project is configured to fetch its Jenkinsfile from a Git repo:

Pipeline Def

If I change the list of parameters, for example, from:

properties([
        parameters([
                string(name: 'FOO', description: 'Choose foo')
        ])
])

to:

properties([
        parameters([
                string(name: 'FOO', description: 'Choose foo'),
                string(name: 'BAR', description: 'Choose bar')
        ])
])

And run the build, the first run does not show the newly added BAR parameter:

Parameter list not updated

As the updated Jenkins file expects the BAR parameter to be present, this causes the first build after the change to fail as the user is not presented with an input to enter this value.

Is there a way to prevent this? To make sure the Jenkinsfile is up-to-date before showing the parameter entry page?

Behrang
  • 46,888
  • 25
  • 118
  • 160
  • How are you (were you, this is from '17) calling `BAR`? I have found what seems like the same issue and the solution for me was to test `params.BAR`. `if (params.BAR == null) { def BAR = "baz" } else { echo "yes, BAR is set" }` – Phil Jul 01 '19 at 05:16
  • There is some discussion about this issue on the Jenkins bug tracker here: https://issues.jenkins-ci.org/browse/JENKINS-41929 – HeroCC Jun 18 '20 at 19:26

6 Answers6

20

Short answer: No. It would be nice if there was some facility for parsing and processing the Jenkinsfile separate from the build, but there's not.

Jenkins does not know about the new parameters until it retrieves, parses, and runs the Jenkinsfile, and the only way to do that is to...run a build.

In effect, the build history will always be "one run behind" the Jenkinsfile; when you change something in the Jenkinsfile, the next build will run with the "old" Jenkinsfile, but pick up and process the new Jenkinsfile for the build after that.

dolphy
  • 6,218
  • 4
  • 24
  • 32
  • This is the right answer, and well-said. What I've done recently to work around this is to ask for my parameters after the run has started, with an `input` step. This way you have access to all the real-time values and can do whatever logic you want on them. See: https://stackoverflow.com/a/73737359/556078 – Max Cascone Sep 20 '22 at 19:25
4

The only solution to this problem afaik is to manually add an "skip_run" boolean parameter, than add a when{} clause to every stage of the job.

    properties([
        parameters([
                BooleanParameter(name: 'skip_run', description: 'Skips all stages. Used to update parameters in case of changes.', default: False)
        ])
    ])

...

stage('Doing Stuff') {
        when {
            expression { return params.skip_run ==~ /(?i)(N|NO|F|FALSE|OFF|STOP)/ }
        }
        steps {
            ...
        }
    }

This is, of course, very prone to error.

Alternatively, you could add a single stage as the very beginning of the pipeline and fail the build on purpose.

stage('Update Build Info only') {
        when {
            expression { return params.skip_run ==~ /(?i)(Y|YES|T|TRUE|ON|RUN)/ }
        }
        steps {
            error("This was done deliberately to update the build info.")
        }
    }

UPDATE: Thanks to Abort current build from pipeline in Jenkins, i came up with this solution:

To prevent the build from actually appearing red, you could wrap this with a try - catch and exit the build gracefully.

final updateOnly = 'updateOnly'     
try {
      stage('Update Build Info only') {
            when {
                expression { return params.skip_run ==~ /(?i)(Y|YES|T|TRUE|ON|RUN)/ }
            }
            steps {
                error(updateOnly)
            }
        }
...
//other stages here
...
    } catch (e) {
      if (e.message == updateOnly) {
        currentBuild.result = 'ABORTED'
        echo('Skipping the Job to update the build info')
        // return here instead of throwing error to keep the build "green"
        return
      }
      // normal error handling
      throw e
    }
Mohl
  • 405
  • 2
  • 16
2

I have a function that skips the build unless the job has all the required parameters, something like:

if (job.hasParameters(['FOO', 'BAR'])) {
    // pipeline code
}
ctran
  • 21
  • 3
1

An issue was reported a few years ago in Jenkins related with this issue https://issues.jenkins-ci.org/browse/JENKINS-41929

Still open, so no elegant solution yet.

julian-alarcon
  • 297
  • 3
  • 9
0

The idea of following approach is to define the default value once and do not fail the pipeline if the parameter is not there yet - which is the case on the very first run.

  • Define the default value above the pipeline.
  • Use it in your parameter definition
  • override the parameter in the environment section and use the environment variable throughout the pipeline

Example:

NEW_PARAM_DEFAULT = 'the default value'

pipeline {

  parameters {
    string(
        name: 'NEW_PARAM',
        description: 'Safely introduce a new parameter without a failure on the first run.',
        defaultValue: NEW_PARAM_DEFAULT)
  }
  
  environment {
     NEW_PARAM_VALUE = params.getOrDefault('NEW_PARAM', NEW_PARAM_DEFAULT)
     // safely use NEW_PARAM_VALUE everywhere
  }

  stages {
    ...
  }
}
baumato
  • 358
  • 3
  • 13
-1

try..

    parameters {
        string(name: 'GRADLE_ARGS', defaultValue: '--console=plain', description: 'Gradle arguments')
    }

    environment{
        GRADLE_ARGS = "${params.GRADLE_ARGS}"
    }
  • 2
    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 07 '21 at 09:40
  • This shows how to add parameters in a pipeline, not how to cause them to show up in the Build with Parameters page. As such, it does not answer the question. – Trisped Dec 29 '22 at 00:36