Generate GoCD pipelines for tomzo/gocd-yaml-config-plugin: Plugin to declare GoCD pipelines and environments configuration in YAML in Kotlin DSL.
-
no need to define upstream pipelines, they are recognized by DSL structure
-
group wrapper (
group("groupName") { … }
) which sets the group in every contained pipeline
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>net.oneandone.gocd</groupId>
<artifactId>gocd-pico-dsl</artifactId>
<version>0.3.2</version>
</dependency>
</dependencies>
Define your pipelines with DSL. Call in a main function ConfigSuite(gocd, outputFolder = Paths.get(outputFolder)).writeFiles()
to start generation.
fun main(args: Array<String>) {
val gocd = gocd {
pipelines {
sequence {
group("dev") {
pipeline("deploy") {
materials {
repoPackage("myArtifact")
}
template = Template("deploy", "last-stage")
}
}
}
}
}
val outputFolder = if (args.isNotEmpty()) args[0] else "target/gocd-config"
ConfigSuite(gocd, outputFolder = Paths.get(outputFolder)).writeFiles()
}
Call the main function above via maven:
mvn compile exec:java -Dexec.mainClass="net.oneandone.gocd.picodsl.samples.MinimalExampleKt" -Dexec.args="target/gocd-config"
Have a look at examples for a working maven example.
The following snippet shows two kinds how pipelines can be generated. Using the config registry and net.oneandone.gocd.picodsl.GeneratePipelinesKt
should be easier.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>registry-starter</id>
<phase>process-classes</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<!--ready to use starter which renders all registered pipelines -->
<mainClass>net.oneandone.gocd.picodsl.GeneratePipelinesKt</mainClass>
<arguments>
<!-- sourcePackage is required -->
<argument>--sourcePackage=net.oneandone.gocd.picodsl.examples.registry</argument>
<!-- other arguments are optional -->
<argument>--outputFolder=target/gocd-config</argument><!-- default: target/gocd-config -->
<argument>--plantuml</argument>
<argument>--dot</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>custom-starter</id>
<phase>process-classes</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<!-- custom starter which calls ConfigSuite -->
<mainClass>net.oneandone.gocd.picodsl.examples.MinimalExampleKt</mainClass>
</configuration>
</execution>
</executions>
</plugin>
All objects which are derived from RegisteredGocdConfig
are registered in ConfigRegistry
and are automatically used by net.oneandone.gocd.picodsl.GeneratePipelinesKt
if they are found in the defined base package.
object Second : RegisteredGocdConfig({
environments() {
environment("devEnv") {}
}
pipelines {
sequence {
deploy("first") {
group = "dev"
materials {
repoPackage("euss")
}
}
}
}
})
If you pass the --dot
parameter a dot file is generated in the outputfolder. This can be converted with Graphviz to an image.:
examples/target/gocd-config$ dot pipelines-first.dot -Tpng -o pipelines-first.png
The first line is the pipeline name, second the template name.
With --plantuml
the same dot file is generated with a PlantUML wrapper:
@startuml
....
@enduml
So it can be easily viewed in IntelliJ if you have PlantUML integration - plugin for IntelliJ IDEs | JetBrains installed.
Have a look at examples to see most elements.
Stubs can help you if you already have existing pipelines and want to write a GoCD DSL which is based on them. Stubs will not be rendered in the YAML, but are required as they are part of the materials of downstream pipelines.
stubPipeline("existing-pipeline") {
template = Template("existing", "existing-stage")
}
pipeline("testing") {
template = testing
}
See Timer Trigger | GoCD User Documentation and https://github.com/tomzo/gocd-yaml-config-plugin#timer.
pipeline("p1") {
timer("0 15 10 * * ? *", true)
generates
p1:
timer:
only_on_changes: true
spec: 0 15 10 * * ? *
Second parameter for onlyOnChanges
is optional: timer("0 15 10 * * ? *")
is valid too and the YAML element is omitted in that case.
GoCD DSL facilitates pipeline writing as much as possible for the generic use case. If you use GoCD in your company you will define best practices and standards.
You can reflect these standards in the DSL to maker you definition even shorter.
If you provide for deployment a "deploy" template, you can define your custom deploy pipeline:
val deployTemplate = Template("deploy", "deploy-stage")
fun PipelineGroup.deploy(name: String, block: PipelineSingle.() -> Unit = {}) {
this.pipeline(name, block).apply {
template = deployTemplate
parameter("param1", "value1")
}
}
This deploy extension function creates the pipeline as usual and afterwards sets the template and defines also a parameter which is required by the template.
pipelines {
sequence {
deploy("first") {
group = "dev"
materials {
repoPackage("euss")
}
}
}
}
For a better understanding see Extensions - Kotlin Programming Language.
If you want to use your extension function in the group scope, you must add the extension function to every possible scope. This might look scary, but the pattern is always the same: Define extension functions for PipelineGroup
, SequenceContext
, ParallelContext
andd delegate to a function which adds your custom functionality.
fun PipelineGroup.deploy(
name: String,
block: PipelineSingle.() -> Unit = {}
) = deployPipeline(this, name, block)
fun SequenceContext.deploy(
name: String,
block: PipelineSingle.() -> Unit = {}
) = deployPipeline(this.pipelineGroup, name, block)
fun ParallelContext.deploy(
name: String,
block: PipelineSingle.() -> Unit = {}
) = deployPipeline(this.pipelineGroup, name, block)
private fun deployPipeline(pipelineGroup: PipelineGroup, name: String, block: PipelineSingle.() -> Unit) {
pipelineGroup.pipeline(name, block).apply {
template = deploy
parameter("param1", "value1")
}
}
For a better understanding why wee need to extend every scope have a look at Kotlin DSLs: Type-Safe Builders: Scope Control - Kotlin Programming Language.