jenkinsjenkins-pipelinejenkins-blueocean

Get step id in Jenkins Pipeline for linking to BlueOcean or Pipeline Steps view (flowGraphTable)


Given a Jenkins pipeline that runs a series of steps, some within parallel blocks, is there any way to obtain within the pipeline the Flow id of a given step or the most recent step?

What's a Flow ID? If you look at a Run of your Pipeline job you can see a "Pipeline Steps" link that points to flowGraphTable/. There you have links to specific job steps like execution/node/113/. These seem to represent a FlowNode.

Is there any way to get these IDs from within the pipeline, for generating links etc?

In particular I want to get a link to the sub-Flow for my parallel branches so I can link to the BlueOcean views of them. (The builtin Jenkins view is useless because it doesn't show a subtree).

I can see that the BlueOcean links correspond to the /execution/ links, they have the same id value. If my pipeline branch is myjob/9/execution/node/78/ then on blueocean it'll be jobname/9/pipeline/78.

But how do I get that ID if I want to use the build summary plugin or similar to generate the links and add them to the build results page?


Solution

  • I was struggling with the a similar use case and managed to find a solution that is working for me. This https://issues.jenkins-ci.org/browse/JENKINS-28119 might be an interesting read on the issue. It eventually pointed me in a good direction.

    Besides the Pipeline and Pipeline Stage View Plugin, I had to install the HTTP Request Plugin (https://wiki.jenkins.io/display/JENKINS/HTTP+Request+Plugin) and the Pipeline Utility Steps Plugin (for parsing JSON, https://wiki.jenkins.io/display/JENKINS/Pipeline+Utility+Steps+Plugin) on our Jenkins server. I am not sure which other plugins might be required.

    Here is my working example, only missing the stages being evaluated:

    #!groovy
    
    pipeline {
        agent any
    
        stages {
            stage('Test') {
                steps {
                    script {
                        def responseRun = httpRequest(
                            //consoleLogResponseBody: true,
                            contentType: 'APPLICATION_JSON',
                            httpMode: 'GET',
                            url: BUILD_URL + 'wfapi',
                            validResponseCodes: '200'
                        )
                        def runJson = readJSON text: responseRun.getContent()
                        def headNodeUrl = ''
                        runJson.stages.each {
                            if (it.name.toString() == 'Stage node label') {
                                // Found head node: it.id
                                headNodeUrl = BUILD_URL + 'execution/node/' + it.id.toString() + '/'
                            }
                        }
                        def responseNode = httpRequest(
                            contentType: 'APPLICATION_JSON',
                            httpMode: 'GET',
                            url: headNodeUrl + 'wfapi',
                            validResponseCodes: '200'
                        )
                        def nodeJson = readJSON text: responseNode.getContent()
                        def execNodeUrl = ''
                        nodeJson.stageFlowNodes.each {
                            if (it.name.toString() == 'Execution node label') {
                                // Found execution node: it.id
                                execNodeUrl = BUILD_URL + 'execution/node/' + it.id.toString() + '/log/'
                            }
                        }
                        echo execNodeUrl
                    }
                }
            }
        }
    }
    

    BUILD_URL is a global environment variable, supplied by Jenkins, I assume. In my full script I have a stage('Stage node label') { ... } containing a statement bat label: 'Execution node label', script: ... whose log URL will be constructed and printed with echo.

    The result is a URL like http://myjenkinsserver.org:8080/job/some_folder/job/my_job_name/181/execution/node/50/log/

    I think the use of each in my example might not be ideal, since I cannot abort it after the first match. Also I didn't manage to encapsulate the httpRequest and readJSON into a class method or something because I couldn't figure out the return type of readJSON. Any hints are appreciated.

    I hope this helps.

    Cheers