[geedeecee] improve handling of build-context files, and build-avoidance, and remove old code
This commit is contained in:
parent
de24a62635
commit
657d5712f6
8 changed files with 135 additions and 196 deletions
|
|
@ -80,12 +80,12 @@ fun Directory.filesChecksum(): Long = asFileTree
|
||||||
|
|
||||||
|
|
||||||
/** Drop the first [count] directories from the path */
|
/** Drop the first [count] directories from the path */
|
||||||
fun FileCopyDetails.dropDirectories(count: Int): RelativePath =
|
fun RelativePath.dropDirectories(count: Int): RelativePath =
|
||||||
RelativePath(true, *relativePath.segments.drop(count).toTypedArray())
|
RelativePath(true, *segments.drop(count).toTypedArray())
|
||||||
|
|
||||||
|
|
||||||
/** Drop the first directory from the path */
|
/** Drop the first directory from the path */
|
||||||
fun FileCopyDetails.dropDirectory(): RelativePath =
|
fun RelativePath.dropDirectory(): RelativePath =
|
||||||
dropDirectories(1)
|
dropDirectories(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,13 @@
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("kafkatorio.conventions.kotlin-dsl")
|
id("kafkatorio.conventions.kotlin-dsl")
|
||||||
// kotlin("jvm") version embeddedKotlinVersion
|
|
||||||
// `kotlin-dsl`
|
|
||||||
`java-gradle-plugin`
|
`java-gradle-plugin`
|
||||||
}
|
}
|
||||||
|
|
||||||
val gradleJvmTarget = "11"
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(platform(libs.kotlin.bom))
|
implementation(platform(libs.kotlin.bom))
|
||||||
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable")
|
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable")
|
||||||
}
|
}
|
||||||
|
|
||||||
//kotlin {
|
|
||||||
// jvmToolchain {
|
|
||||||
// (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(gradleJvmTarget))
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//tasks.withType<KotlinCompile>().configureEach {
|
|
||||||
// kotlinOptions {
|
|
||||||
// jvmTarget = gradleJvmTarget
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
plugins {
|
plugins {
|
||||||
create("geedeecee") {
|
create("geedeecee") {
|
||||||
|
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
package dev.adamko.geedeecee
|
|
||||||
|
|
||||||
import javax.inject.Inject
|
|
||||||
import org.gradle.api.DefaultTask
|
|
||||||
import org.gradle.api.Task
|
|
||||||
import org.gradle.api.file.DirectoryProperty
|
|
||||||
import org.gradle.api.logging.LogLevel
|
|
||||||
import org.gradle.api.provider.ListProperty
|
|
||||||
import org.gradle.api.provider.Property
|
|
||||||
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() {
|
|
||||||
|
|
||||||
@get:InputDirectory
|
|
||||||
abstract val workingDir: DirectoryProperty
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
abstract val separatedArgs: ListProperty<String>
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
abstract val dockerActive: Property<Boolean>
|
|
||||||
|
|
||||||
init {
|
|
||||||
super.setGroup(GDCPlugin.GCD_TASK_GROUP)
|
|
||||||
description = "Run a docker-compose command."
|
|
||||||
|
|
||||||
enabledIf {
|
|
||||||
it.dockerActive.orNull == true
|
|
||||||
}
|
|
||||||
|
|
||||||
logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
fun dockerCompose() {
|
|
||||||
val separatedArgs: List<String> = separatedArgs.get()
|
|
||||||
|
|
||||||
executor.exec {
|
|
||||||
environment("BUILDKIT_PROGRESS", "plain")
|
|
||||||
workingDir(this@GDCCommandTask.workingDir)
|
|
||||||
commandLine(separatedArgs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
fun `docker-compose`(args: String) {
|
|
||||||
separatedArgs.addAll(
|
|
||||||
parseSpaceSeparatedArgs("docker-compose $args")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private inline fun <reified T: Task> T.enabledIf(crossinline spec: (T) -> Boolean) {
|
|
||||||
onlyIf {
|
|
||||||
require(it is T)
|
|
||||||
spec(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,19 @@
|
||||||
package dev.adamko.geedeecee
|
package dev.adamko.geedeecee
|
||||||
|
|
||||||
|
import dev.adamko.geedeecee.tasks.DockerContextFilesPreparation
|
||||||
|
import dev.adamko.geedeecee.tasks.DockerEnvUpdateTask
|
||||||
|
import dev.adamko.geedeecee.tasks.GDCCommandTask
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.gradle.api.*
|
import org.gradle.api.Plugin
|
||||||
import org.gradle.api.file.ProjectLayout
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.logging.*
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.provider.*
|
import org.gradle.api.provider.ProviderFactory
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.kotlin.dsl.apply
|
||||||
import org.gradle.kotlin.dsl.*
|
import org.gradle.kotlin.dsl.create
|
||||||
|
import org.gradle.kotlin.dsl.getValue
|
||||||
|
import org.gradle.kotlin.dsl.provideDelegate
|
||||||
|
import org.gradle.kotlin.dsl.registering
|
||||||
|
import org.gradle.kotlin.dsl.withType
|
||||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
|
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
|
||||||
|
|
||||||
|
|
@ -14,7 +21,6 @@ import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
|
||||||
@Suppress("UnstableApiUsage")
|
@Suppress("UnstableApiUsage")
|
||||||
abstract class GDCPlugin @Inject constructor(
|
abstract class GDCPlugin @Inject constructor(
|
||||||
private val providers: ProviderFactory,
|
private val providers: ProviderFactory,
|
||||||
private val layout: ProjectLayout,
|
|
||||||
) : Plugin<Project> {
|
) : Plugin<Project> {
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -34,6 +40,14 @@ abstract class GDCPlugin @Inject constructor(
|
||||||
// )
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val dockerContextPrepareFiles by target.tasks.registering(DockerContextFilesPreparation::class) {
|
||||||
|
into(gdcSettings.dockerBuildContextDir)
|
||||||
|
includeEmptyDirs = false
|
||||||
|
// dockerContextDir.set(gdcSettings.dockerBuildContextDir)
|
||||||
|
// copySpec.convention(target.copySpec())
|
||||||
|
// copySpec = Action { }
|
||||||
|
}
|
||||||
|
|
||||||
val dockerComposeUp by target.tasks.registering(GDCCommandTask::class) {
|
val dockerComposeUp by target.tasks.registering(GDCCommandTask::class) {
|
||||||
`docker-compose`("up -d")
|
`docker-compose`("up -d")
|
||||||
}
|
}
|
||||||
|
|
@ -45,6 +59,7 @@ abstract class GDCPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
val dockerComposeBuild by target.tasks.registering(GDCCommandTask::class) {
|
val dockerComposeBuild by target.tasks.registering(GDCCommandTask::class) {
|
||||||
`docker-compose`("build")
|
`docker-compose`("build")
|
||||||
|
cacheable.set(true)
|
||||||
}
|
}
|
||||||
val dockerComposePush by target.tasks.registering(GDCCommandTask::class) {
|
val dockerComposePush by target.tasks.registering(GDCCommandTask::class) {
|
||||||
dependsOn(dockerComposeBuild)
|
dependsOn(dockerComposeBuild)
|
||||||
|
|
@ -57,14 +72,21 @@ abstract class GDCPlugin @Inject constructor(
|
||||||
dockerComposeUp.configure { dependsOn(dockerComposeBuild) }
|
dockerComposeUp.configure { dependsOn(dockerComposeBuild) }
|
||||||
dockerComposeRemove.configure { dependsOn(dockerComposeDown) }
|
dockerComposeRemove.configure { dependsOn(dockerComposeDown) }
|
||||||
|
|
||||||
target.tasks.withType<DockerComposeExec>().configureEach {
|
// target.tasks.withType<DockerComposeExec>().configureEach {
|
||||||
dependsOn(dockerComposeEnvUpdate)
|
// dependsOn(dockerComposeEnvUpdate)
|
||||||
}
|
// dockerIsActive.set(isDockerActive())
|
||||||
|
// stateFile.set(temporaryDir.resolve("docker-state.md5"))
|
||||||
|
// }
|
||||||
|
|
||||||
target.tasks.withType<GDCCommandTask>().configureEach {
|
target.tasks.withType<GDCCommandTask>().configureEach {
|
||||||
workingDir.convention(gdcSettings.srcDir)
|
|
||||||
dependsOn(dockerComposeEnvUpdate)
|
dependsOn(dockerComposeEnvUpdate)
|
||||||
|
dependsOn(dockerContextPrepareFiles)
|
||||||
|
|
||||||
|
workingDir.convention(gdcSettings.srcDir)
|
||||||
dockerActive.set(gdcSettings.dockerActive)
|
dockerActive.set(gdcSettings.dockerActive)
|
||||||
|
stateFile.set(
|
||||||
|
gdcSettings.stateDir.file(workingDir.map { "dc_state${name.hashCode()}${it.asFile.hashCode()}.md5" })
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val assembleTasks = target.tasks.matching { LifecycleBasePlugin.ASSEMBLE_TASK_NAME == it.name }
|
val assembleTasks = target.tasks.matching { LifecycleBasePlugin.ASSEMBLE_TASK_NAME == it.name }
|
||||||
|
|
@ -76,7 +98,6 @@ abstract class GDCPlugin @Inject constructor(
|
||||||
dockerComposeUp.configure {
|
dockerComposeUp.configure {
|
||||||
dependsOn(assembleTasks)
|
dependsOn(assembleTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -86,6 +107,8 @@ abstract class GDCPlugin @Inject constructor(
|
||||||
composeProjectVersion.convention(providers.provider { project.version.toString() })
|
composeProjectVersion.convention(providers.provider { project.version.toString() })
|
||||||
containerRegistryHost.convention(providers.gradleProperty("dockerContainerRegistryHost"))
|
containerRegistryHost.convention(providers.gradleProperty("dockerContainerRegistryHost"))
|
||||||
srcDir.convention(layout.projectDirectory.dir("docker"))
|
srcDir.convention(layout.projectDirectory.dir("docker"))
|
||||||
|
dockerBuildContextDir.convention(srcDir.dir("build"))
|
||||||
|
stateDir.convention(layout.buildDirectory.dir("geedeecee/state/"))
|
||||||
|
|
||||||
dotEnv.put("COMPOSE_PROJECT_NAME", composeProjectName)
|
dotEnv.put("COMPOSE_PROJECT_NAME", composeProjectName)
|
||||||
dotEnv.put("APP_NAME", providers.provider { project.name })
|
dotEnv.put("APP_NAME", providers.provider { project.name })
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@ interface GDCSettings {
|
||||||
@get:Input
|
@get:Input
|
||||||
val dockerActive: Property<Boolean>
|
val dockerActive: Property<Boolean>
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
val dockerBuildContextDir: DirectoryProperty
|
||||||
|
|
||||||
|
val stateDir: DirectoryProperty
|
||||||
|
|
||||||
// @Suppress("PropertyName", "FunctionName")
|
// @Suppress("PropertyName", "FunctionName")
|
||||||
// abstract class DotEnv(
|
// abstract class DotEnv(
|
||||||
// private val dotEnv: MapProperty<String, String>,
|
// private val dotEnv: MapProperty<String, String>,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.adamko.geedeecee
|
package dev.adamko.geedeecee.tasks
|
||||||
|
|
||||||
|
import dev.adamko.geedeecee.GDCPlugin
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
package dev.adamko.geedeecee.tasks
|
||||||
|
|
||||||
|
import dev.adamko.geedeecee.GDCPlugin
|
||||||
|
import dev.adamko.geedeecee.internal.enabledIf
|
||||||
|
import dev.adamko.geedeecee.internal.filesChecksum
|
||||||
|
import javax.inject.Inject
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.file.RegularFileProperty
|
||||||
|
import org.gradle.api.logging.LogLevel
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.InputDirectory
|
||||||
|
import org.gradle.api.tasks.Optional
|
||||||
|
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.process.ExecOperations
|
||||||
|
import org.gradle.work.NormalizeLineEndings
|
||||||
|
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/
|
||||||
|
// or parsing Dockerfile
|
||||||
|
// https://github.com/keilerkonzept/dockerfile-json
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage") // NormalizeLineEndings
|
||||||
|
abstract class GDCCommandTask @Inject constructor(
|
||||||
|
private val executor: ExecOperations,
|
||||||
|
) : DefaultTask() {
|
||||||
|
|
||||||
|
@get:InputDirectory
|
||||||
|
@get:SkipWhenEmpty
|
||||||
|
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||||
|
@get:NormalizeLineEndings
|
||||||
|
abstract val workingDir: DirectoryProperty
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
abstract val separatedArgs: ListProperty<String>
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
abstract val dockerActive: Property<Boolean>
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
@get:Optional
|
||||||
|
abstract val cacheable: Property<Boolean>
|
||||||
|
|
||||||
|
@get:OutputFile
|
||||||
|
abstract val stateFile: RegularFileProperty
|
||||||
|
|
||||||
|
init {
|
||||||
|
group = GDCPlugin.GCD_TASK_GROUP
|
||||||
|
description = "Run a docker-compose command."
|
||||||
|
|
||||||
|
enabledIf { it.dockerActive.getOrElse(false) }
|
||||||
|
|
||||||
|
logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun dockerCompose() {
|
||||||
|
val separatedArgs: List<String> = separatedArgs.get()
|
||||||
|
|
||||||
|
executor.exec {
|
||||||
|
environment("BUILDKIT_PROGRESS", "plain")
|
||||||
|
workingDir(this@GDCCommandTask.workingDir)
|
||||||
|
commandLine(separatedArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheable.getOrElse(false)) {
|
||||||
|
val inputChecksum = workingDir.get().filesChecksum()
|
||||||
|
stateFile.get().asFile.writeText("$inputChecksum")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the `docker-compose` argument */
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
fun `docker-compose`(args: String) {
|
||||||
|
separatedArgs.set(
|
||||||
|
parseSpaceSeparatedArgs("docker-compose $args")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue