Skip to content

Commit

Permalink
#1236 Auto-versioning in a workflow on deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
dcoraboeuf committed Nov 18, 2024
1 parent 481b44d commit 8a53d42
Show file tree
Hide file tree
Showing 26 changed files with 841 additions and 68 deletions.
2 changes: 2 additions & 0 deletions ontrack-extension-auto-versioning/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation("org.apache.commons:commons-lang3")
implementation("jakarta.annotation:jakarta.annotation-api")
implementation(project(":ontrack-extension-notifications"))
implementation(project(":ontrack-extension-workflows"))

implementation("cc.ekblad:4koma:1.2.0")

Expand All @@ -31,6 +32,7 @@ dependencies {
testImplementation(project(path = ":ontrack-extension-general", configuration = "tests"))
testImplementation(project(path = ":ontrack-extension-casc", configuration = "tests"))
testImplementation(project(path = ":ontrack-extension-notifications", configuration = "tests"))
testImplementation(project(path = ":ontrack-extension-workflows", configuration = "tests"))
testImplementation(project(path = ":ontrack-model", configuration = "tests"))
testImplementation(project(":ontrack-it-utils"))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package net.nemerosa.ontrack.extension.av.workflows

import com.fasterxml.jackson.databind.JsonNode
import net.nemerosa.ontrack.extension.av.AutoVersioningExtensionFeature
import net.nemerosa.ontrack.extension.av.dispatcher.AutoVersioningOrder
import net.nemerosa.ontrack.extension.av.processing.AutoVersioningProcessingService
import net.nemerosa.ontrack.extension.workflows.engine.WorkflowInstance
import net.nemerosa.ontrack.extension.workflows.execution.AbstractTypedWorkflowNodeExecutor
import net.nemerosa.ontrack.extension.workflows.execution.WorkflowNodeExecutorResult
import org.springframework.stereotype.Component
import java.util.*

@Component
class AutoVersioningWorkflowNodeExecutor(
extensionFeature: AutoVersioningExtensionFeature,
private val autoVersioningProcessingService: AutoVersioningProcessingService,
) : AbstractTypedWorkflowNodeExecutor<AutoVersioningWorkflowNodeExecutorData>(
feature = extensionFeature,
id = "auto-versioning",
displayName = "Auto-versioning of a given branch",
dataType = AutoVersioningWorkflowNodeExecutorData::class,
) {

override fun execute(
workflowInstance: WorkflowInstance,
data: AutoVersioningWorkflowNodeExecutorData,
workflowNodeExecutorResultFeedback: (output: JsonNode?) -> Unit
): WorkflowNodeExecutorResult {
val order = createOrder(workflowInstance, data)
val outcome = autoVersioningProcessingService.process(order)
TODO("Checks the outcome")
}

private fun createOrder(
workflowInstance: WorkflowInstance,
data: AutoVersioningWorkflowNodeExecutorData,
): AutoVersioningOrder {
return AutoVersioningOrder(
uuid = UUID.randomUUID().toString(),
sourceProject = TODO(),
sourceBuildId = null,
sourcePromotionRunId = null,
sourcePromotion = null,
sourceBackValidation = null,
branch = TODO(),
targetPath = data.targetPath,
targetRegex = data.targetRegex,
targetProperty = data.targetProperty,
targetPropertyRegex = data.targetPropertyRegex,
targetPropertyType = data.targetPropertyType,
targetVersion = data.targetVersion,
autoApproval = data.autoApproval,
upgradeBranchPattern = data.upgradeBranchPattern,
postProcessing = data.postProcessing,
postProcessingConfig = data.postProcessingConfig,
validationStamp = data.validationStamp,
autoApprovalMode = data.autoApprovalMode,
reviewers = data.reviewers,
prTitleTemplate = data.prTitleTemplate,
prBodyTemplate = data.prBodyTemplate,
prBodyTemplateFormat = data.prBodyTemplateFormat,
additionalPaths = data.additionalPaths,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package net.nemerosa.ontrack.extension.av.workflows

import com.fasterxml.jackson.databind.JsonNode
import net.nemerosa.ontrack.extension.av.config.AutoApprovalMode
import net.nemerosa.ontrack.extension.av.config.AutoVersioningSourceConfig
import net.nemerosa.ontrack.extension.av.config.AutoVersioningSourceConfigPath

data class AutoVersioningWorkflowNodeExecutorData(
val targetProject: String,
val targetBranch: String,
val targetVersion: String,
val targetPath: String,
val targetRegex: String? = null,
val targetProperty: String? = null,
val targetPropertyRegex: String? = null,
val targetPropertyType: String? = null,
val autoApproval: Boolean = true,
val upgradeBranchPattern: String = AutoVersioningSourceConfig.DEFAULT_UPGRADE_BRANCH_PATTERN,
val postProcessing: String? = null,
val postProcessingConfig: JsonNode? = null,
val validationStamp: String? = null,
val autoApprovalMode: AutoApprovalMode = AutoApprovalMode.DEFAULT_AUTO_APPROVAL_MODE,
val reviewers: List<String> = emptyList(),
val prTitleTemplate: String? = null,
val prBodyTemplate: String? = null,
val prBodyTemplateFormat: String? = null,
val additionalPaths: List<AutoVersioningSourceConfigPath> = emptyList(),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.nemerosa.ontrack.extension.av.workflows

data class AutoVersioningWorkflowNodeExecutorOutput(
val autoVersioningOrderId: String,
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.nemerosa.ontrack.extension.environments.workflows.executors

import com.fasterxml.jackson.databind.JsonNode
import net.nemerosa.ontrack.extension.casc.context.ConfigContext
import net.nemerosa.ontrack.extension.environments.EnvironmentsExtensionFeature
import net.nemerosa.ontrack.extension.environments.Slot
import net.nemerosa.ontrack.extension.environments.service.EnvironmentService
Expand All @@ -28,7 +27,6 @@ class SlotPipelineCreationWorkflowNodeExecutor(
private val environmentService: EnvironmentService,
private val securityService: SecurityService,
private val structureService: StructureService,
private val configContext: ConfigContext,
) : AbstractExtension(extensionFeature), WorkflowNodeExecutor {

override val id: String = "slot-pipeline-creation"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package net.nemerosa.ontrack.extension.workflows.execution

import com.fasterxml.jackson.databind.JsonNode
import net.nemerosa.ontrack.extension.workflows.engine.WorkflowInstance
import net.nemerosa.ontrack.json.parseInto
import net.nemerosa.ontrack.model.extension.ExtensionFeature
import kotlin.reflect.KClass

/**
* [WorkflowNodeExecutor] implementation relying on known types for its data & output.
*
* @param D Type for the data
*/
abstract class AbstractTypedWorkflowNodeExecutor<D : Any>(
override val feature: ExtensionFeature,
override val id: String,
override val displayName: String,
private val dataType: KClass<D>,
) : WorkflowNodeExecutor {
override suspend fun execute(
workflowInstance: WorkflowInstance,
workflowNodeId: String,
workflowNodeExecutorResultFeedback: (output: JsonNode?) -> Unit
): WorkflowNodeExecutorResult {
val data = workflowInstance.workflow.getNode(workflowNodeId).data.parseInto(dataType)
return execute(workflowInstance, data, workflowNodeExecutorResultFeedback)
}

abstract fun execute(
workflowInstance: WorkflowInstance,
data: D,
workflowNodeExecutorResultFeedback: (output: JsonNode?) -> Unit,
): WorkflowNodeExecutorResult

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package net.nemerosa.ontrack.kdsl.acceptance.tests.av

import net.nemerosa.ontrack.kdsl.acceptance.tests.scm.withMockScmRepository
import net.nemerosa.ontrack.kdsl.acceptance.tests.support.uid
import net.nemerosa.ontrack.kdsl.acceptance.tests.support.waitUntil
import net.nemerosa.ontrack.kdsl.connector.graphql.schema.type.SlotPipelineStatus
import net.nemerosa.ontrack.kdsl.connector.graphql.schema.type.SlotWorkflowTrigger
import net.nemerosa.ontrack.kdsl.spec.extension.environments.environments
import net.nemerosa.ontrack.kdsl.spec.extension.environments.workflows.addWorkflow
import org.junit.jupiter.api.Test

class ACCAutoVersioningWorkflow : AbstractACCAutoVersioningTestSupport() {

@Test
fun `Deployment workflow triggering an auto-versioning followed by the change of state to deployed`() {
// Creating an application project
val application = project { this }

// Creating an environment
val environment = ontrack.environments.createEnvironment(
name = uid("env-"),
order = 0,
)
// Creating a slot for the environment & the project
val slot = environment.createSlot(
project = application,
)

// Creating a GitOps project targeted by the deployment
withMockScmRepository(ontrack) {
withAutoVersioning {
repositoryFile("gradle.properties") { "version = 0.0.1" }

project {
val gitOps = this
branch {
val gitOpsBranch = this
configuredForMockRepository()

// Adding a workflow to this slot
slot.addWorkflow(
trigger = SlotWorkflowTrigger.DEPLOYING,
workflowYaml = """
name: Deployment
nodes:
- id: av
executorId: auto-versioning
data:
targetProject: ${gitOps.name}
targetBranch ${gitOpsBranch.name}
targetPath: gradle.properties
targetProperty: version
targetVersion: ${'$'}{build}
- id: deployed
parents:
- id: av
executorId: slot-pipeline-deployed
data: {}
""".trimIndent()
)

// Creating a build for the application project
val build = build(name = "1.0.0") { this }
// Creating a pipeline for this build & starting its deployment
val pipeline = slot.createPipeline(build = build)
pipeline.startDeploying()

// We expect the pipeline to become deployed
waitUntil(
task = "Pipeline deployed",
timeout = 10_000,
interval = 1_000,
) {
ontrack.environments.findPipelineById(pipeline.id)?.status == SlotPipelineStatus.DEPLOYED
}

// We expect the GitOps repository to contain the new version
assertThatMockScmRepository {
fileContains("gradle.properties") { "version = 1.0.0" }
}
}
}
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
mutation CreateEnvironment(
$name: String!,
$order: Int!,
$description: String! = "",
$tags: [String!]! = [],
) {
createEnvironment(input: {
name: $name,
order: $order,
description: $description,
tags: $tags,
}) {
...PayloadUserErrors
environment {
id
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mutation CreatePipeline(
$slotId: String!,
$buildId: Int!,
) {
startSlotPipeline(input: {
slotId: $slotId,
buildId: $buildId,
}) {
...PayloadUserErrors
pipeline {
id
number
status
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
mutation CreateSlot(
$environmentId: String!,
$projectId: Int!,
$qualifier: String! = "",
$description: String = null,
) {
createSlots(input: {
environmentIds: [$environmentId],
projectId: $projectId,
qualifier: $qualifier,
description: $description,
}) {
...PayloadUserErrors
slots {
slots {
id
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
mutation CreateSlotWorkflow(
$slotId: String!,
$trigger: SlotWorkflowTrigger!,
$workflowYaml: String!,
) {
addSlotWorkflow(input: {
slotId: $slotId,
trigger: $trigger,
workflowYaml: $workflowYaml,
}) {
...PayloadUserErrors
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fragment EnvironmentFragment on Environment {
id
name
order
description
tags
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
query FindPipelineById(
$id: String!,
) {
slotPipelineById(id: $id) {
...SlotPipelineFragment
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fragment SlotFragment on Slot {
id
description
project {
...ProjectFragment
}
qualifier
environment {
...EnvironmentFragment
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fragment SlotPipelineFragment on SlotPipeline {
id
number
status
build {
...BuildFragment
}
slot {
...SlotFragment
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
mutation StartDeployingPipeline(
$pipelineId: String!,
) {
startSlotPipelineDeployment(input: {
pipelineId: $pipelineId,
}) {
...PayloadUserErrors
deploymentStatus {
status
}
}
}
Loading

0 comments on commit 8a53d42

Please sign in to comment.