Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 723
Craig Ringer Avatar asked Oct 16 '22 08:10

Craig Ringer


1 Answers

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

like image 59
Stefan Avatar answered Dec 28 '22 11:12

Stefan