How to Inject Stages or steps in Jenkins pipeline

Viewed 1039

The output of this python eval looks like it could be stages in a jenkins pipeline

 $ python3 -c 'print("\n".join(["stage({val}) {{ do something with {val} }}".format(val=i) for i in range(3)]))'
stage(0) { do something with 0 }
stage(1) { do something with 1 }
stage(2) { do something with 2 }

Is it possible for jenkins to use output like this to create steps or stages in a pipeline so the running python script is able to update jenkins ? The point of this would be to have Blue Ocean pipeline have a stage dot that was made by an external script running separate jobs.

To elaborate on the example ... if this demo.py script which outputs the uptime in a stage

#!/bin/env python3.6
import subprocess, time

def uptime():
    return (subprocess.run('uptime', stdout=subprocess.PIPE, encoding='utf8')).stdout.strip()

for i in range(3):
    print("stage({val}) {{\n    echo \"{output}\" \n}}".format(val=i, output=uptime()))
    time.sleep(1)

where to be setup in a jenkins pipeline

node {
    stage("start demo"){
        sh "/tmp/demo.py"

    }
}

As is this demo just outputs the text and does not create any stages in blue ocean

[Pipeline] sh
+ /tmp/demo.py
stage(0) {
    echo "03:17:16 up 182 days, 12:17,  8 users,  load average: 0.00, 0.03, 0.05" 
}
stage(1) {
    echo "03:17:17 up 182 days, 12:17,  8 users,  load average: 0.00, 0.03, 0.05" 
}
stage(2) {
    echo "03:17:18 up 182 days, 12:17,  8 users,  load average: 0.00, 0.03, 0.05" 
}

Again the point of this would be to have Blue Ocean pipeline have a stage dot with a log

2 Answers

You can evaluate an expression and then call it.

node(''){
 Closure x = evaluate("{it -> evaluate(it)}" )
 x(" stage('test'){ script { echo 'hi'}}")
}

Since Jenkins converts your Groovy script into Java, compiles it and then executes the result, it would be quite hard to use an external program to generate more Groovy to execute, since that additional groovy code would need to be converted. But the generated code is a result of running, which means that the conversion is already done.

Instead, you may want to programmatically build your stages in Groovy.


some_array = ["/tmp/demo.py", "sleep 10", "uptime"] 

def getBuilders()
{
    def builders = [:]

    some_array.eachWithIndex { it, index ->
        // name the stage
        def name = 'Stage #' + (index + 1)
        builders[name] = {
            stage (name) {
                def my_label = "jenkins_label" // can choose programmatically if needed
                node(my_label) {
                        try {
                            doSomething(it)
                        }
                        catch (err) { println "Failed to run ${it}"; throw err  }
                        finally {  }
                    }
            }
        }
    };
    return builders
}

def doSomething(something) {
    sh "${something}"
}

And later in your main pipeline

        stage('Do it all') {
            steps {
                script {
                    def builders = getBuilders()
                    parallel builders
                }
            }

This will run three parallel stages, where one would be running /tmp/demo.py, the second sleep 10, and the third uptime.

Related