• Icon: Improvement Improvement
    • Resolution: Fixed
    • Icon: Minor Minor
    • workflow-cps-plugin
    • None

      Consider the following workflow script:

      echo "bool=$bool"
      echo bool.getClass().toString()

      Output:

      Started by user Jos Backus
      Running: Print Message
      bool=false
      Running: Print Message
      class java.lang.String
      Running: End of Workflow
      Finished: SUCCESS

      I would expect the name of the class to be java.lang.Boolean, not java.lang.String. This forces one to add lines such as:

      bool = bool == 'true'

          [JENKINS-27295] Boolean parameters injected as String

          Jesse Glick added a comment -

          Cf. CpsScript.$initialize.

          Jesse Glick added a comment - Cf. CpsScript.$initialize .

          Code changed in jenkins
          User: Jesse Glick
          Path:
          src/main/java/org/jenkinsci/plugins/workflow/cps/ParamsVariable.java
          src/main/resources/org/jenkinsci/plugins/workflow/cps/ParamsVariable/help.jelly
          src/test/java/org/jenkinsci/plugins/workflow/cps/ParamsVariableTest.java
          http://jenkins-ci.org/commit/workflow-cps-plugin/ac463507fcfb8e12b34ed6b45af49c2ae204a773
          Log:
          [FIXED JENKINS-27295] Offering a new global variable params for typed access to ParametersAction.

          SCM/JIRA link daemon added a comment - Code changed in jenkins User: Jesse Glick Path: src/main/java/org/jenkinsci/plugins/workflow/cps/ParamsVariable.java src/main/resources/org/jenkinsci/plugins/workflow/cps/ParamsVariable/help.jelly src/test/java/org/jenkinsci/plugins/workflow/cps/ParamsVariableTest.java http://jenkins-ci.org/commit/workflow-cps-plugin/ac463507fcfb8e12b34ed6b45af49c2ae204a773 Log: [FIXED JENKINS-27295] Offering a new global variable params for typed access to ParametersAction.

          Jos Backus added a comment -

          Thanks, @jglick!

          Jos Backus added a comment - Thanks, @jglick!

          Thomas Gimpel added a comment -

          With the update of pipeline plugin "Pipeline: Groovy" from version 2.17 to version 2.18, which includes the fix for issue JENKINS-27295 the following Jenkins file does not work any longer:

          /*
           * Make booleans from string parameters
           */
          skipVs2012 = skipVs2012.toBoolean();
          skipUbuntu32 = skipUbuntu32.toBoolean();
          
          class Slave {
              String name;
              Boolean skip;
          };
          stage('Build') {
              Slave[] slaves = [
                  [
                      name: 'vs2012',
                      skip: skipVs2012
                  ], [
                      name: 'ubuntu1404-32',
                      skip: skipUbuntu32
                  ]
              ];
              
              for (int i = 0; i < slaves.size(); i++) {
                  Slave slave = slaves.get(i);
                  echo "checking slave ${slave.name}";
                  echo "${slave.name}: ${slave.skip}";
                  if (!slave.skip) {
                      echo "!slave.skip works for ${slave.name}";
                  }
                  if (slave.skip==false) {
                      echo "slave.skip==false works for ${slave.name}";
                  }
              }
          }
          

          Obviously the slave.skip member is now considered to be a string and not a boolean.

          Thomas Gimpel added a comment - With the update of pipeline plugin "Pipeline: Groovy" from version 2.17 to version 2.18, which includes the fix for issue JENKINS-27295 the following Jenkins file does not work any longer: /* * Make booleans from string parameters */ skipVs2012 = skipVs2012.toBoolean(); skipUbuntu32 = skipUbuntu32.toBoolean(); class Slave { String name; Boolean skip; }; stage( 'Build' ) { Slave[] slaves = [ [ name: 'vs2012' , skip: skipVs2012 ], [ name: 'ubuntu1404-32' , skip: skipUbuntu32 ] ]; for ( int i = 0; i < slaves.size(); i++) { Slave slave = slaves.get(i); echo "checking slave ${slave.name}" ; echo "${slave.name}: ${slave.skip}" ; if (!slave.skip) { echo "!slave.skip works for ${slave.name}" ; } if (slave.skip== false ) { echo "slave.skip== false works for ${slave.name}" ; } } } Obviously the slave.skip member is now considered to be a string and not a boolean.

          Jesse Glick added a comment -

          git not sure exactly what “does not work any longer”. Unqualified parameter names are now taken from the environment and would continue to be of type String. If you have a minimal, self-contained, reproducible test case please file a separate issue with details and mark it as blocking this one.

          Jesse Glick added a comment - git not sure exactly what “does not work any longer”. Unqualified parameter names are now taken from the environment and would continue to be of type String . If you have a minimal, self-contained, reproducible test case please file a separate issue with details and mark it as blocking this one.

          Thomas Gimpel added a comment -

          Thank you for the explanation. I understand now: global variables initialized by environment variables cannot change their type when a new value is assigned. In former plugin versions they could change e.g. from string to boolean. Now I have to assign the boolean to a different variable. Otherwise the boolean type would be ignored.
          I can live with that change, but it was a bit confusing, because within the Groovy script one cannot distinguish between a global variable and an unqualified parameter. The difference becomes clear only within the context of the pipeline configuration.

          Thomas Gimpel added a comment - Thank you for the explanation. I understand now: global variables initialized by environment variables cannot change their type when a new value is assigned. In former plugin versions they could change e.g. from string to boolean. Now I have to assign the boolean to a different variable. Otherwise the boolean type would be ignored. I can live with that change, but it was a bit confusing, because within the Groovy script one cannot distinguish between a global variable and an unqualified parameter. The difference becomes clear only within the context of the pipeline configuration.

          David Karr added a comment -

          I would appreciate a clear summary of the current and desired state for this situation.

          The context is a pipeline job with a parameter clearly defined as a "Boolean Parameter". In my experience, this is provisioned as a string, which with no other information, is just confusing.

          So, is this going to be fixed, or is this considered to be acceptable? I'm having trouble discerning a clear message on this. If this isn't going to change, at the least, the job configuration page should make it clear that it will be provisioned as a String type. Otherwise, every single person who discovers this for the first time is going to assume this is a bug.

          David Karr added a comment - I would appreciate a clear summary of the current and desired state for this situation. The context is a pipeline job with a parameter clearly defined as a "Boolean Parameter". In my experience, this is provisioned as a string, which with no other information, is just confusing. So, is this going to be fixed, or is this considered to be acceptable? I'm having trouble discerning a clear message on this. If this isn't going to change, at the least, the job configuration page should make it clear that it will be provisioned as a String type. Otherwise, every single person who discovers this for the first time is going to assume this is a bug.

          Patrick Wolf added a comment - - edited

          dkarr This is my understanding based on the comments above and the release notes of the Pipeline Groovy Plugin

          Parameters are passed into the run as environment variables and are captured in a params object. This means there are three references to that parameter value:

          env.foo
          foo
          params.foo
          

          The first two return that environment variable that was set. This has to be a string because it is an environment variable. The third method retains the parameter type set because it is not used in the environment. In this case:

          This functions as expected because it is a boolean param

          properties([parameters([booleanParam(defaultValue: false, description: '', name: 'foo')])])
          if (params.foo) {
              echo "true: $foo"
          }
          else echo "false: $foo"
          

          But this will not because it is a string from environment variable:

          properties([parameters([booleanParam(defaultValue: false, description: '', name: 'foo')])])
          if (foo) {
              echo "true: $foo"
          }
          else echo "false: $foo"
          

          Patrick Wolf added a comment - - edited dkarr This is my understanding based on the comments above and the release notes of the Pipeline Groovy Plugin Parameters are passed into the run as environment variables and are captured in a params object. This means there are three references to that parameter value: env.foo foo params.foo The first two return that environment variable that was set. This has to be a string because it is an environment variable. The third method retains the parameter type set because it is not used in the environment. In this case: This functions as expected because it is a boolean param properties([parameters([booleanParam(defaultValue: false , description: '', name: ' foo')])]) if (params.foo) { echo " true : $foo" } else echo " false : $foo" But this will not because it is a string from environment variable: properties([parameters([booleanParam(defaultValue: false , description: '', name: ' foo')])]) if (foo) { echo " true : $foo" } else echo " false : $foo"

          David Karr added a comment -

          So then, in general, if the name of a pipeline parameter specified in the job configuration is "foo", then referencing it in the body of the pipeline script as "foo" will get a variable of a type equivalent to what is specified in the job configuration, EXCEPT if the type in the job configuration is "Boolean Parameter", which will result in a String type. In that case, referencing it as "params.foo" will always guarantee that it will be provisioned as a type that is equivalent to what is specified in the job configuration, including a "Boolean Parameter". Under those circumstances, would you conclude that it would be reasonable to document this understanding in the pipeline documentation?

          David Karr added a comment - So then, in general, if the name of a pipeline parameter specified in the job configuration is "foo", then referencing it in the body of the pipeline script as "foo" will get a variable of a type equivalent to what is specified in the job configuration, EXCEPT if the type in the job configuration is "Boolean Parameter", which will result in a String type. In that case, referencing it as "params.foo" will always guarantee that it will be provisioned as a type that is equivalent to what is specified in the job configuration, including a "Boolean Parameter". Under those circumstances, would you conclude that it would be reasonable to document this understanding in the pipeline documentation?

          Patrick Wolf added a comment - - edited

          I would say that it is a best practice to always use the params object if you want the ensure that the type is consistent. Referencing the parameter as either foo or env.foo returns the value as it was injected into an environment variable and will always be of type String.

          properties([parameters([booleanParam(defaultValue: false, description: '', name: 'foo')])])
          
          echo "foo: " + foo.getClass().toString()
          echo "env.foo: " +  env.foo.getClass().toString()
          echo "params.foo: " + params.foo.getClass().toString()
          

          returns:

          [Pipeline] echo
          foo: class java.lang.String
          [Pipeline] echo
          env.foo: class java.lang.String
          [Pipeline] echo
          params.foo: class java.lang.Boolean
          [Pipeline] 
          

          Patrick Wolf added a comment - - edited I would say that it is a best practice to always use the params object if you want the ensure that the type is consistent. Referencing the parameter as either foo or env.foo returns the value as it was injected into an environment variable and will always be of type String . properties([parameters([booleanParam(defaultValue: false , description: '', name: ' foo')])]) echo "foo: " + foo.getClass().toString() echo "env.foo: " + env.foo.getClass().toString() echo "params.foo: " + params.foo.getClass().toString() returns: [Pipeline] echo foo: class java.lang. String [Pipeline] echo env.foo: class java.lang. String [Pipeline] echo params.foo: class java.lang. Boolean [Pipeline]

            jglick Jesse Glick
            josb Jos Backus
            Votes:
            1 Vote for this issue
            Watchers:
            15 Start watching this issue

              Created:
              Updated:
              Resolved: