You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

111 lines
3.1 KiB

package org.wikimedia.integration
import org.wikimedia.integration.ExecutionGraph
import org.wikimedia.integration.PatchSet
import org.wikimedia.integration.Pipeline
import org.wikimedia.integration.PipelineStage
class PipelineBuilder implements Serializable {
String configPath
/**
* Constructs a new {@PipelineBuilder} from the given YAML configuration.
*/
PipelineBuilder(String pipelineConfigPath) {
configPath = pipelineConfigPath
}
/**
* Builds a single-node Jenkins workflow script for each of the configured
* pipelines.
*
* If a pipeline defines any branching arcs in its directed
* <code>execution</code> graph, they will be iterated over concurrently—in
* the order that {@link ExecutionGraph#executions()} returns—and their
* stages defined as <code>parallel</code> stages in the workflow script.
*
* @param ws Jenkins Workflow Script (`this` when writing a Jenkinsfile)
* @param pipelineName Only build/run the given pipeline.
*/
void build(ws, pipelineName = "") {
def config
ws.node("blubber") {
ws.stage("configure") {
if (ws.params.ZUUL_REF) {
ws.checkout(PatchSet.fromZuul(ws.params).getSCM())
} else {
ws.checkout(ws.scm)
}
config = ws.readYaml(file: configPath)
}
}
def plines = pipelines(config)
if (pipelineName) {
plines = plines.findAll { it.name == pipelineName }
if (plines.size() == 0) {
throw new RuntimeException(
"Pipeline '${pipelineName}' is not defined in project's '${configPath}'",
)
}
}
for (def pline in plines) {
def stack = pline.stack()
ws.node(pline.getRequiredNodeLabels().join(" && ")) {
try {
for (def stages in stack) {
if (stages.size() > 1) {
def stageClosures = [:]
for (def stage in stages) {
stageClosures[stage.name] = stage.closure(ws)
}
ws.stage("${pline.name}: [parallel]") {
ws.parallel(stageClosures)
}
} else {
def stage = stages[0]
// if we've reached the teardown stage in our normal execution
// path (not after an exception was thrown), the result should
// be a success
if (stage.name == PipelineStage.TEARDOWN) {
ws.currentBuild.result = 'SUCCESS'
}
ws.stage("${pline.name}: ${stage.name}", stage.closure(ws))
}
}
} catch (exception) {
ws.currentBuild.result = 'FAILURE'
// ensure teardown steps are always executed
for (def stage in stack.last()) {
if (stage == PipelineStage.TEARDOWN) {
stage.closure(ws)()
}
}
throw exception
}
}
}
}
/**
* Constructs and returns all pipelines from the given configuration.
*/
List pipelines(cfg) {
cfg.pipelines.collect { pname, pconfig ->
def pline = new Pipeline(pname, pconfig)
pline.validate()
pline
}
}
}