- 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
- }
- }
- }
|