migrate docker-compose convention plugin out of buildSrc

This commit is contained in:
Adam 2022-10-02 12:11:06 +02:00
parent 105555bf6c
commit f804c39a3b
32 changed files with 564 additions and 239 deletions

View file

@ -1,8 +1,9 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version embeddedKotlinVersion
`kotlin-dsl`
id("kafkatorio.conventions.kotlin-dsl")
// kotlin("jvm") version embeddedKotlinVersion
// `kotlin-dsl`
`java-gradle-plugin`
}
@ -13,17 +14,17 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable")
}
kotlin {
jvmToolchain {
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(gradleJvmTarget))
}
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = gradleJvmTarget
}
}
//kotlin {
// jvmToolchain {
// (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(gradleJvmTarget))
// }
//}
//
//tasks.withType<KotlinCompile>().configureEach {
// kotlinOptions {
// jvmTarget = gradleJvmTarget
// }
//}
gradlePlugin {
plugins {

View file

@ -0,0 +1,96 @@
package dev.adamko.geedeecee
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.file.Directory
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.logging.LogLevel
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.property
import org.gradle.process.ExecOperations
import org.gradle.work.NormalizeLineEndings
import org.jetbrains.kotlin.incremental.md5
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
// look into using 'build context' for up-to-date check
// https://stackoverflow.com/questions/38946683/how-to-test-dockerignore-file
// https://github.com/pwaller/docker-show-context
// https://snippets.khromov.se/see-which-files-are-included-in-your-docker-build-context/
@CacheableTask
@Suppress("UnstableApiUsage")
abstract class DockerComposeExec @Inject constructor(
private val executor: ExecOperations,
private val objects: ObjectFactory,
private val providers: ProviderFactory,
) : DefaultTask() {
@get:InputDirectory
@get:SkipWhenEmpty
@get:PathSensitive(PathSensitivity.RELATIVE)
@get:NormalizeLineEndings
abstract val dockerComposeDir: DirectoryProperty
@get:Input
abstract val command: Property<String>
@get:Input
val dockerIsActive: Property<Boolean> = objects.property<Boolean>().convention(
providers.exec {
commandLine = parseSpaceSeparatedArgs("docker info")
isIgnoreExitValue = true
}.result.map {
it.exitValue == 0
}
)
@get:OutputFile
val stateFile: RegularFileProperty = objects.fileProperty().convention {
temporaryDir.resolve("docker-files.md5")
}
init {
group = DOCKER_COMPOSE_GROUP
logging.captureStandardOutput(LogLevel.LIFECYCLE)
super.onlyIf { task ->
require(task is DockerComposeExec)
task.dockerIsActive.getOrElse(false)
}
}
@TaskAction
fun exec() {
executor.exec {
workingDir = dockerComposeDir.asFile.get()
commandLine = parseSpaceSeparatedArgs(command.get())
}
val inputChecksum = dockerComposeDir.get().filesChecksum()
stateFile.get().asFile.writeText("$inputChecksum")
}
companion object {
const val DOCKER_COMPOSE_GROUP: String = "docker-compose"
private fun Directory.filesChecksum(): Long = asFileTree
.files
.map { it.readBytes() + it.absolutePath.toByteArray() }
.fold(byteArrayOf()) { acc, bytes -> acc + bytes }
.md5()
}
}

View file

@ -0,0 +1,27 @@
package dev.adamko.geedeecee
import javax.inject.Inject
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.logging.LogLevel
import org.gradle.api.model.ObjectFactory
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.WriteProperties
@CacheableTask
open class DockerEnvUpdateTask @Inject constructor(
objects: ObjectFactory
) : WriteProperties() {
@Internal
// internal and not abstract so that it can delegate to `super.outputFile` in `init {}`
val dotEnvFile: RegularFileProperty = objects.fileProperty()
init {
group = GDCPlugin.GCD_TASK_GROUP
super.setOutputFile(dotEnvFile)
super.getLogging().captureStandardOutput(LogLevel.LIFECYCLE)
}
}

View file

@ -0,0 +1,47 @@
package dev.adamko.geedeecee
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.logging.LogLevel
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.process.ExecOperations
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
abstract class GDCCommandTask @Inject constructor(
private val executor: ExecOperations
) : DefaultTask() {
init {
super.setGroup(GDCPlugin.GCD_TASK_GROUP)
description = "Run a docker-compose command."
logging.captureStandardOutput(LogLevel.LIFECYCLE)
}
@get:InputDirectory
abstract val workingDir: DirectoryProperty
@get:Input
abstract val separatedArgs: ListProperty<String>
@TaskAction
fun dockerCompose() {
val separatedArgs: List<String> = separatedArgs.get()
executor.exec {
workingDir(this@GDCCommandTask.workingDir)
commandLine(separatedArgs)
}
}
@Suppress("FunctionName")
fun `docker-compose`(args: String) {
separatedArgs.addAll(
parseSpaceSeparatedArgs("docker-compose $args")
)
}
}

View file

@ -1,14 +1,17 @@
package dev.adamko.geedeecee
import javax.inject.Inject
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.*
import org.gradle.api.file.ProjectLayout
import org.gradle.api.provider.ProviderFactory
import org.gradle.kotlin.dsl.create
import org.gradle.api.logging.*
import org.gradle.api.provider.*
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.*
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
@Suppress("UnstableApiUsage")
abstract class GDCPlugin @Inject constructor(
private val providers: ProviderFactory,
private val layout: ProjectLayout,
@ -16,27 +19,78 @@ abstract class GDCPlugin @Inject constructor(
override fun apply(target: Project) {
target.createSettings()
target.plugins.apply(LifecycleBasePlugin::class)
val gdcSettings = target.createSettings()
val dockerComposeEnvUpdate by target.tasks.registering(DockerEnvUpdateTask::class) {
dotEnvFile.set(gdcSettings.srcDir.file(".env"))
// properties(
// "COMPOSE_PROJECT_NAME" to gdcSettings.composeProjectName,
// "KAFKATORIO_VERSION" to gdcSettings.composeProjectVersion,
// "REGISTRY_HOST" to providers.gradleProperty("dockerContainerRegistryHost"),
// )
}
val dockerComposeUp by target.tasks.registering(GDCCommandTask::class) {
`docker-compose`("up -d")
}
val dockerComposeDown by target.tasks.registering(GDCCommandTask::class) {
`docker-compose`("down")
}
val dockerComposeStop by target.tasks.registering(GDCCommandTask::class) {
`docker-compose`("stop")
}
val dockerComposeBuild by target.tasks.registering(GDCCommandTask::class) {
`docker-compose`("build")
}
val dockerComposeRemove by target.tasks.registering(GDCCommandTask::class) {
`docker-compose`("rm --stop -v -f")
}
dockerComposeUp.configure { dependsOn(dockerComposeBuild) }
dockerComposeRemove.configure { dependsOn(dockerComposeDown) }
target.tasks.withType<GDCCommandTask>().configureEach {
workingDir.convention(gdcSettings.srcDir)
dependsOn(dockerComposeEnvUpdate)
}
val assembleTasks = target.tasks.matching { LifecycleBasePlugin.ASSEMBLE_TASK_NAME == it.name }
assembleTasks.configureEach {
dependsOn(dockerComposeEnvUpdate)
}
dockerComposeUp.configure {
dependsOn(assembleTasks)
}
}
private fun Project.createSettings(): GDCSettings =
extensions.create<GDCSettings>(GDC_EXTENSION_NAME).apply {
composeProjectName.convention(providers.provider { project.name })
composeProjectVersion.convention(providers.provider { project.name })
containerRegistryHost.convention(providers.gradleProperty("dockerContainerRegistryHost"))
srcDir.convention(layout.projectDirectory.dir("docker"))
dotEnv.put("COMPOSE_PROJECT_NAME", composeProjectName)
dotEnv.put("PROJECT_VERSION", providers.provider { project.version.toString() })
dotEnv.put("REGISTRY_HOST", providers.gradleProperty("dockerContainerRegistryHost"))
dotEnv.put("PROJECT_VERSION", composeProjectVersion)
dotEnv.put("REGISTRY_HOST", containerRegistryHost)
enabled.convention(
providers.exec {
commandLine = parseSpaceSeparatedArgs("docker info")
isIgnoreExitValue = true
}.result.map {
it.exitValue == 0
}
)
enabled.convention(isDockerActive())
}
private fun isDockerActive(): Provider<Boolean> =
providers.exec {
commandLine = parseSpaceSeparatedArgs("docker info")
isIgnoreExitValue = true
}.result.map {
it.exitValue == 0
}

View file

@ -11,6 +11,12 @@ interface GDCSettings {
@get:Input
val composeProjectName: Property<String>
@get:Input
val composeProjectVersion: Property<String>
@get:Input
val containerRegistryHost: Property<String>
@get:Internal
val srcDir: DirectoryProperty