Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-48315

Add env variable LAST_STAGE_NAME which will persist when stage ends prematurely

    • Icon: Improvement Improvement
    • Resolution: Unresolved
    • Icon: Minor Minor
    • None
    • Jenkins 2.73.3
      pipeline-stage-step-plugin 2.3

      Hi,

      The environment STAGE_NAME variable does not persist when the stage ends prematurely. here's an example:

       

      node('windows') {
          try {
              stage('MY STAGE') {
                  echo "before=" + env.STAGE_NAME
                  error('something bad happened')
                  echo "after=" + env.STAGE_NAME
              } 
          } catch (def e) {
              echo "after failure="+env.STAGE_NAME
              throw e
          }
      }

      console output:

      Started by user admin
      [Pipeline] node
      Running on SLAVE01 in F:\jenkins-remote\workspace\stagename
      [Pipeline] {
      [Pipeline] stage
      [Pipeline] { (MY STAGE)
      [Pipeline] echo
      before=MY STAGE
      [Pipeline] error
      [Pipeline] }
      [Pipeline] // stage
      [Pipeline] echo
      after failure=null
      [Pipeline] }
      [Pipeline] // node
      [Pipeline] End of Pipeline
      ERROR: something bad happened
      Finished: FAILURE
      

      While it makes sense for STAGE_NAME to be null when not in an actual stage, it would be nice if we had a build property or environment variable such as LAST_STAGE_NAME which would keep the last stage executed. This functionality is critical to my shared library logic in which I have a generic script runner and I want to know at which stage the script it ran failed (without having to modify each flow).

      It would also be useful if this property would be available when calling getBuildVariables() on a downstream job started by the build step.

          [JENKINS-48315] Add env variable LAST_STAGE_NAME which will persist when stage ends prematurely

          Richa Misra added a comment -

          Hi Mor L,

          If you don't mind, could you share an example of its usage in the library please?

          Thanks,
          Richa

          Richa Misra added a comment - Hi Mor L, If you don't mind, could you share an example of its usage in the library please? Thanks, Richa

          Mor L added a comment -

          Not sure what you mean. Assuming you have a member called script in your class which you initialize with "this" from the pipeline loading the library, I replaced all "script.stage('stagename')

          {... }

          " with "wrappedStage('stagename')

          {...}

          "

          Mor L added a comment - Not sure what you mean. Assuming you have a member called script in your class which you initialize with "this" from the pipeline loading the library, I replaced all "script.stage('stagename') {... } " with "wrappedStage('stagename') {...} "

          Richa Misra added a comment -

          Actually my pipeline looks something like this:

           

          @Library('jenkins-library')
          
          def stageName
          pipeline {
              agent { label 'rhel5' }
          
              stages {
                  stage("stage1") {
                      steps {
                          script {
                              //stageName = "${env.STAGE_NAME}"
                              echo "hello"
                              String buildVersion = new com.nagra.uex.jenkins.Utils(this).extractMvnBuildVersion('pom.xml')
                          }
                      }
                  }
                  stage("stage2") {
                      steps {
                          script {
                              //stageName = "${env.STAGE_NAME}"
                              echo "hello"
                              // sh 'exit 1'
                          }
                      }
                  }
              }
          }
          

          I have got various classes in my external global library(referenced above as jenkins-library) and I call the different methods as in the line below

          String buildVersion = new com.nagra.uex.jenkins.Utils(this).extractMvnBuildVersion('pom.xml')
          
          

          I can define a new method wrappedStage() in one of the classes e.g. Utils but I am not sure how I can modify the pipeline script above to use wrappedStage() instead of stage() as its fails on runtime with error that it expects stage

           

          Thanks,

          Richa

          Richa Misra added a comment - Actually my pipeline looks something like this:   @Library( 'jenkins-library' ) def stageName pipeline { agent { label 'rhel5' } stages { stage( "stage1" ) { steps { script { //stageName = "${env.STAGE_NAME}" echo "hello" String buildVersion = new com.nagra.uex.jenkins.Utils( this ).extractMvnBuildVersion( 'pom.xml' ) } } } stage( "stage2" ) { steps { script { //stageName = "${env.STAGE_NAME}" echo "hello" // sh 'exit 1' } } } } } I have got various classes in my external global library(referenced above as jenkins-library) and I call the different methods as in the line below String buildVersion = new com.nagra.uex.jenkins.Utils( this ).extractMvnBuildVersion( 'pom.xml' ) I can define a new method wrappedStage() in one of the classes e.g. Utils but I am not sure how I can modify the pipeline script above to use wrappedStage() instead of stage() as its fails on runtime with error that it expects stage   Thanks, Richa

          Richa Misra added a comment -

          Hi Mor L,

          Is your Pipeline script part of the global library class itself ?

          Thanks,
          Richa

          Richa Misra added a comment - Hi Mor L, Is your Pipeline script part of the global library class itself ? Thanks, Richa

          Mor L added a comment -

          The difference is that I'm not using declerative pipeline so I am free to do as I will.
          And yes, all of my code is within the same library.
          I haven't had much experience with declerative but I guess you could tutn the method into a dsl and then it would be recognized.

          Mor L added a comment - The difference is that I'm not using declerative pipeline so I am free to do as I will. And yes, all of my code is within the same library. I haven't had much experience with declerative but I guess you could tutn the method into a dsl and then it would be recognized.

          Richa Misra added a comment -

          It doesn't seem to be available as such in Declarative pipeline. Could this be added please as LAST_STAGE_NAME environment variable or perhaps FAILED_STAGE_NAME in the post section at the end of the pipeline so that it can provide more accurate information of the failed stage in outgoing messages:

          pipeline{
             agent { label 'rhel5' }
             stages{
               stage('Stage1'){
                  //Do something
               }
               stage('Stage2'){
                  //Do something else
               }
             }
             post{
                  always{
                        //If currentBuild.currentResult == 'FAILURE, send message failed at  stage <FAILED_STAGE_NAME>
                  }
             }
          }

          Thanks

          Richa Misra added a comment - It doesn't seem to be available as such in Declarative pipeline. Could this be added please as LAST_STAGE_NAME environment variable or perhaps FAILED_STAGE_NAME in the post section at the end of the pipeline so that it can provide more accurate information of the failed stage in outgoing messages: pipeline{ agent { label 'rhel5' } stages{ stage( 'Stage1' ){ //Do something } stage( 'Stage2' ){ //Do something else } } post{ always{ //If currentBuild.currentResult == 'FAILURE, send message failed at stage <FAILED_STAGE_NAME> } } } Thanks

          Aaron K added a comment -

          This would be very helpful for sending failure messages.  +1!

           

          Right now we have each stage in a try/catch block to be able to accurately reflect the stage name where something failed.  Having this var would allow us to get rid of dozens of lines of code and really simplify our pipelines.

          Aaron K added a comment - This would be very helpful for sending failure messages.  +1!   Right now we have each stage in a try/catch block to be able to accurately reflect the stage name where something failed.  Having this var would allow us to get rid of dozens of lines of code and really simplify our pipelines.

          Mark Han added a comment -

          Yea, this would be awesome for QOL

          Mark Han added a comment - Yea, this would be awesome for QOL

          Hokwang Lee added a comment -

          when unstable result (fail or abort, etc),

          I should send email about which commit, which job, which status, which stage.

          only thing I can not know for now is stage.

          abayer Please consider this issue.

          Hokwang Lee added a comment - when unstable result (fail or abort, etc), I should send email about which commit, which job, which status, which stage. only thing I can not know for now is stage. abayer Please consider this issue.

          Kurt Werle added a comment -

          I run a bunch of stages in parallel.  I would like all the failed stage names.  And the last 50 lines from all the failed stage logs.  Individually, of course - not blended.

          When I imagine a solution it looks like

          build_info = [
          stage_name: "some stage",
          log_lines: [log lines],
          status: SUCCESS/FAILURE/UNSTABLE
          ]
          

          And an API like

          report_build_logs(filter success/failure/unstable, # lines to report)
          

          Kurt Werle added a comment - I run a bunch of stages in parallel.  I would like all the failed stage names.  And the last 50 lines from all the failed stage logs.  Individually, of course - not blended. When I imagine a solution it looks like build_info = [ stage_name: "some stage" , log_lines: [log lines], status: SUCCESS/FAILURE/UNSTABLE ] And an API like report_build_logs(filter success/failure/unstable, # lines to report)

            Unassigned Unassigned
            pyrocks Mor L
            Votes:
            28 Vote for this issue
            Watchers:
            19 Start watching this issue

              Created:
              Updated: