title: What is the relationship between Environment and Parameters in Jenkinsfile Parameterized Builds?
body: <p>I recently ran into something of a puzzler while working on some Jenkins builds with a coworker.  He's been using <code>params.VARIABLE</code> and <code>env.VARIABLE</code> interchangably and having no issues with it.  Meanwhile, I started getting null object errors on one of his calls to a parameter object through the environment on this line of code:</p>
  
  <pre><code>if(!deploy_environments.contains(env.ENVIRONMENT_NAME.trim()) || params.INVOKE_PARAMETERS ) {
  </code></pre>
  
  <p><code>ENVIRONMENT_NAME</code> here is a parameter.  I started getting this error:</p>
  
  <pre><code>java.lang.NullPointerException: Cannot invoke method trim() on null object
  </code></pre>
  
  <p>This build is executing as a child of another build.  The <code>ENVIRONMENT_NAME</code> parameter is passed down to the child from that parent build.</p>
  
  <p>He was not seeing this error at all on a different Jenkins master.  When I changed the reference above from <code>env.ENVIRONMENT_NAME</code> to <code>params.ENVIRONMENT_NAME</code> the issue went away. </p>
  
  <p>I could find no reference to <code>params == env</code> in the Jenkins documentation, so I created a build to try to clarify their relationship.</p>
  
  <pre><code>pipeline {
      agent {
          label 'jenkins-ecs-slave'
      }
      environment {
          ENV_VARIABLE = 'Environment'
      }
      parameters {
          string(description: 'Parameter', name: 'PARAMETER_VARIABLE', defaultValue: 'Parameter')
  
      }
      stages {
         stage('Output Parameters'){
            steps {
               script {
                   echo "Environment: ${env.ENV_VARIABLE}"
                   echo "Parameter: ${params.PARAMETER_VARIABLE}"
                   echo "Environment from params: ${params.ENV_VARIABLE}"
                   echo "Parameter from Env: ${env.PARAMETER_VARIABLE}"
                   echo "Inspecific reference ENV_VARIABLE: $ENV_VARIABLE"
                   echo "Inspecific reference PARAMETER_VARIABLE: $PARAMETER_VARIABLE"
                   sh 'echo "Shell environment: $ENV_VARIABLE"'
                   sh 'echo "Shell parameter: $PARAMETER_VARIABLE"'                  
               }
             }
         }
      }
  }
  </code></pre>
  
  <p>The first time I ran this on my Jenkins master, it only included the first four lines (<code>echo env.ENV</code>, <code>echo param.PARAM</code>, <code>echo env.PARAM</code>, <code>echo param.ENV</code>) it succeeded with the following output:</p>
  
  <pre><code>[Pipeline] {
  [Pipeline] withEnv
  [Pipeline] {
  [Pipeline] stage
  [Pipeline] { (Output Parameters)
  [Pipeline] script
  [Pipeline] {
  [Pipeline] echo
  Environment: Environment
  [Pipeline] echo
  Parameter: Parameter
  [Pipeline] echo
  Environment from params: null
  [Pipeline] echo
  Parameter from Env: null
  [Pipeline] }
  [Pipeline] // script
  [Pipeline] }
  [Pipeline] // stage
  [Pipeline] }
  [Pipeline] // withEnv
  [Pipeline] }
  [Pipeline] // node
  [Pipeline] End of Pipeline
  Finished: SUCCESS
  </code></pre>
  
  <p>And I thought, "Aha!" Problem solved.  They're not the same.</p>
  
  <p>However, that box promptly froze up on me afterwards and refused to queue anymore builds.  I haven't finished debugging it, but it's not out of line to wonder if that master is just messed up.</p>
  
  <p>So I went and ran it on a third Jenkins master we have hanging around. It's at this point I added the additional lines you see in the script above to further clarify. The first time I ran this script on that box, it failed on the "Inspecific reference to $PARAMETER_VARIABLE line" with the following output:</p>
  
  <pre><code>[Pipeline] {
  [Pipeline] withEnv
  [Pipeline] {
  [Pipeline] stage
  [Pipeline] { (Output Parameters)
  [Pipeline] script
  [Pipeline] {
  [Pipeline] echo
  Environment: Environment
  [Pipeline] echo
  Parameter: Parameter
  [Pipeline] echo
  Environment from params: null
  [Pipeline] echo
  Parameter from Env: null
  [Pipeline] echo
  Inspecific reference ENV_VARIABLE: Environment
  [Pipeline] }
  [Pipeline] // script
  [Pipeline] }
  [Pipeline] // stage
  [Pipeline] }
  [Pipeline] // withEnv
  [Pipeline] }
  [Pipeline] // node
  [Pipeline] End of Pipeline
  groovy.lang.MissingPropertyException: No such property: PARAMETER_VARIABLE for class: groovy.lang.Binding
  </code></pre>
  
  <p>Okay, so far so good.  This makes sense.  They aren't the same.  You can reference Environment variables in echos and shells with out specifically referencing the environment object, but can't do the same with parameters. Consistent, reasonable, I'm good with this.</p>
  
  <p>So then I removed the two lines doing the "inspecific reference" and the script succeeded with the following output:</p>
  
  <pre><code>[Pipeline] {
  [Pipeline] withEnv
  [Pipeline] {
  [Pipeline] stage
  [Pipeline] { (Output Parameters)
  [Pipeline] script
  [Pipeline] {
  [Pipeline] echo
  Environment: Environment
  [Pipeline] echo
  Parameter: Parameter
  [Pipeline] echo
  Environment from params: null
  [Pipeline] echo
  Parameter from Env: Parameter
  [Pipeline] sh
  [Environment Testing] Running shell script
  + echo 'Shell environment: Environment'
  Shell environment: Environment
  [Pipeline] sh
  [Environment Testing] Running shell script
  + echo 'Shell parameter: Parameter'
  Shell parameter: Parameter
  [Pipeline] }
  [Pipeline] // script
  [Pipeline] }
  [Pipeline] // stage
  [Pipeline] }
  [Pipeline] // withEnv
  [Pipeline] }
  [Pipeline] // node
  [Pipeline] End of Pipeline
  Finished: SUCCESS
  </code></pre>
  
  <p>And now I'm completely confuddled.  What the hell?  I ran it a couple of times just to be sure, and got the same successful output as above, consistently.  </p>
  
  <p>Granted, none of the previous builds that showed <code>env.PARAM</code> as <code>null</code> had really succeeded in a clean environment (the one that succeeded was in an environment the promptly imploded on me afterwards).  So maybe if there's an error in the Jenkins pipeline, it short circuits the loading of parameters into the environment or something?  I tried adding <code>echo "$I_SHOULD_FAIL"</code> to the script to force an error in an attempt to reproduce what was I seeing.  No dice:</p>
  
  <pre><code>[Pipeline] {
  [Pipeline] withEnv
  [Pipeline] {
  [Pipeline] stage
  [Pipeline] { (Output Parameters)
  [Pipeline] script
  [Pipeline] {
  [Pipeline] echo
  Environment: Environment
  [Pipeline] echo
  Parameter: Parameter
  [Pipeline] echo
  Environment from params: null
  [Pipeline] echo
  Parameter from Env: Parameter
  [Pipeline] sh
  [Environment Testing] Running shell script
  + echo 'Shell environment: Environment'
  Shell environment: Environment
  [Pipeline] sh
  [Environment Testing] Running shell script
  + echo 'Shell parameter: Parameter'
  Shell parameter: Parameter
  [Pipeline] }
  [Pipeline] // script
  [Pipeline] }
  [Pipeline] // stage
  [Pipeline] }
  [Pipeline] // withEnv
  [Pipeline] }
  [Pipeline] // node
  [Pipeline] End of Pipeline
  groovy.lang.MissingPropertyException: No such property: I_SHOULD_FAIL for class: groovy.lang.Binding
  </code></pre>
  
  <p>So what's going on here?  What's the relationship between <code>environment</code> and <code>parameters</code> in Jenkins pipelines?  What is that relationship supposed to be and why does it seem to be inconsistent?</p>