enable gradle build cache, and improve tasks

This commit is contained in:
Adam 2022-04-23 17:59:37 +02:00
parent f339333f76
commit b81e62e2fd
17 changed files with 330 additions and 200 deletions

View file

@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
idea
`kotlin-dsl`
kotlin("jvm") version "1.6.10"
kotlin("jvm") version "1.6.21"
`project-report`
}
@ -15,7 +15,7 @@ object Versions {
const val kotlin = "1.6.21"
const val gradleNodePlugin = "3.2.1"
const val gradleDockerComposePlugin = "0.15.1"
const val gradleDockerComposePlugin = "0.15.2"
const val kotlinXSerialization = "1.3.2"
const val kvision = "5.8.2"
@ -31,6 +31,7 @@ dependencies {
implementation(enforcedPlatform("org.jetbrains.kotlin:kotlin-bom:${Versions.kotlin}"))
implementation("org.jetbrains.kotlin:kotlin-serialization")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}")
implementation(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:${Versions.kotlinXSerialization}"))
@ -60,7 +61,14 @@ dependencies {
}
tasks.withType<KotlinCompile>().configureEach {
//tasks.compileKotlin {
// kotlinOptions {
// // needed by IntelliJ? Even though below does the same?
// languageVersion = "1.6"
// }
//}
tasks.withType<KotlinCompile>().all {
kotlinOptions {
jvmTarget = Versions.jvmTarget

View file

@ -1,60 +1,30 @@
package dev.adamko.kafkatorio.gradle
import dev.adamko.kafkatorio.jsonMapper
import java.io.ByteArrayOutputStream
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.Directory
import org.gradle.api.file.ProjectLayout
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.specs.NotSpec
import org.gradle.api.specs.Spec
import org.gradle.kotlin.dsl.invoke
import org.gradle.plugins.ide.idea.model.IdeaModule
import org.gradle.process.ExecSpec
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
import org.jetbrains.kotlin.gradle.targets.js.yarn.yarn
operator fun <T> Spec<T>.not(): Spec<T> = Spec<T> { !this(it) }
import org.jetbrains.kotlin.incremental.md5
fun Project.isProcessRunning(process: String, ignoreCase: Boolean = true): Spec<Task> =
Spec<Task> {
ByteArrayOutputStream().use { outputStream ->
project.exec {
commandLine("tasklist") // Windows only for now...
standardOutput = outputStream
}
outputStream.toString()
.contains(process, ignoreCase)
}
}
fun Project.areJsonPropertiesUpToDate(
packageJsonFile: RegularFileProperty,
properties: MapProperty<String, String>
): Spec<Task> = Spec<Task> {
val packageJsonContent = packageJsonFile.get().asFile.readText()
val packageJson = jsonMapper.parseToJsonElement(packageJsonContent).jsonObject
properties.get().all { (key, expectedVal) ->
val actualVal = packageJson[key]?.jsonPrimitive?.content
val isUpToDate = expectedVal == actualVal
if (isUpToDate) {
logger.info("package.json property is up to date, '$key = $actualVal'")
} else {
logger.lifecycle(
"package.json has outdated property '$key' (expected: $expectedVal, actual: $actualVal)"
)
}
isUpToDate
}
}
operator fun <T> Spec<T>.not(): NotSpec<T> = NotSpec(this)
//
//
//fun isProcessRunning(process: String, ignoreCase: Boolean = true): Spec<Task> =
// Spec<Task> {
// ByteArrayOutputStream().use { outputStream ->
// project.exec {
// }
// outputStream.toString().contains(process, ignoreCase)
// }
// }
//
/** exclude generated Gradle code, so it doesn't clog up search results */
@ -91,3 +61,10 @@ fun Project.execCapture(spec: ExecSpec.() -> Unit): String {
/** https://youtrack.jetbrains.com/issue/KT-50848 */
fun Project.yarn(configure: YarnRootExtension.() -> Unit) = with(yarn, configure)
fun Directory.filesChecksum() = asFileTree
.files
.map { it.readBytes() + it.absolutePath.toByteArray() }
.fold(byteArrayOf()) { acc, bytes -> acc + bytes }
.md5()

View file

@ -8,12 +8,6 @@ plugins {
kotlin("jvm")
}
kotlin {
jvmToolchain {
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(Versions.jvmTarget))
}
}
dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
@ -40,22 +34,27 @@ tasks.withType<KotlinCompile>().configureEach {
jvmTarget = Versions.jvmTarget
apiVersion = Versions.kotlinLang
languageVersion = Versions.kotlinLang
}
kotlinOptions.freeCompilerArgs += listOf(
freeCompilerArgs += listOf(
// "-Xcontext-receivers",
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=kotlin.time.ExperimentalTime",
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=kotlin.time.ExperimentalTime",
// "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
// "-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
)
)
}
}
tasks.compileTestKotlin {
kotlinOptions.freeCompilerArgs += "-opt-in=io.kotest.common.ExperimentalKotest"
}
kotlin {
jvmToolchain {
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(Versions.jvmTarget))
}
}
tasks.withType<Test> {
useJUnitPlatform()
}

View file

@ -1,17 +1,8 @@
package dev.adamko.kafkatorio.lang
import com.github.gradle.node.NodePlugin
import com.github.gradle.node.npm.task.NpmInstallTask
import dev.adamko.kafkatorio.Versions
import dev.adamko.kafkatorio.gradle.areJsonPropertiesUpToDate
import dev.adamko.kafkatorio.jsonMapper
import dev.adamko.kafkatorio.task.UpdatePackageJson
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject
import org.gradle.kotlin.dsl.support.useToRun
import org.jetbrains.kotlin.util.suffixIfNot
plugins {
id("com.github.node-gradle.node")
@ -26,34 +17,34 @@ node {
}
val updatePackageJson by tasks.registering(UpdatePackageJson::class) {
group = NodePlugin.NODE_GROUP
description = "Read the package.json file and update the version and name, based on the project."
// group = NodePlugin.NODE_GROUP
// description = "Read the package.json file and update the version and name, based on the project."
propertiesToCheck.put("version", "${project.version}")
propertiesToCheck.put("version", providers.provider { "${project.version}" })
// check to see if the properties are already up-to-date
outputs.upToDateWhen(areJsonPropertiesUpToDate(packageJsonFile, propertiesToCheck))
// outputs.upToDateWhen(areJsonPropertiesUpToDate(packageJsonFile, propertiesToCheck))
// onlyIf(!areJsonPropertiesUpToDate(packageJsonFile, propertiesToCheck))
doLast {
val packageJsonContent = packageJsonFile.get().asFile.readText()
val packageJson = jsonMapper.parseToJsonElement(packageJsonContent).jsonObject
val newJsonProps = propertiesToCheck.get().mapValues { (_, newVal) -> JsonPrimitive(newVal) }
val packageJsonUpdate = JsonObject(packageJson + newJsonProps)
val packageJsonContentUpdated =
jsonMapper
.encodeToString(packageJsonUpdate)
.suffixIfNot("\n")
logger.debug(packageJsonContentUpdated)
packageJsonFile.get().asFile.writer().useToRun {
write(packageJsonContentUpdated)
}
}
// doLast {
// val packageJsonContent = packageJsonFile.get().asFile.readText()
// val packageJson = jsonMapper.parseToJsonElement(packageJsonContent).jsonObject
//
// val newJsonProps = propertiesToCheck.get().mapValues { (_, newVal) -> JsonPrimitive(newVal) }
//
// val packageJsonUpdate = JsonObject(packageJson + newJsonProps)
// val packageJsonContentUpdated =
// jsonMapper
// .encodeToString(packageJsonUpdate)
// .suffixIfNot("\n")
//
// logger.debug(packageJsonContentUpdated)
//
// packageJsonFile.get().asFile.writer().useToRun {
// write(packageJsonContentUpdated)
// }
//
// }
}
tasks.assemble { dependsOn(updatePackageJson) }

View file

@ -0,0 +1,63 @@
package dev.adamko.kafkatorio.task
import dev.adamko.kafkatorio.gradle.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.model.ObjectFactory
import org.gradle.api.provider.Property
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.process.ExecOperations
import org.gradle.work.Incremental
import org.gradle.work.NormalizeLineEndings
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
@CacheableTask
abstract class DockerComposeExec @Inject constructor(
private val executor: ExecOperations,
private val objectFactory: ObjectFactory,
) : DefaultTask() {
@get:InputDirectory
@get:SkipWhenEmpty
@get:PathSensitive(PathSensitivity.RELATIVE)
@get:NormalizeLineEndings
abstract val dockerComposeDir: DirectoryProperty
@get:Input
abstract val command: Property<String>
@get:OutputFile
val stateFile: RegularFileProperty = objectFactory.fileProperty().convention {
temporaryDir.resolve("docker-files.md5")
}
init {
group = DOCKER_COMPOSE_GROUP
logging.captureStandardOutput(LogLevel.LIFECYCLE)
}
@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"
}
}

View file

@ -0,0 +1,46 @@
package dev.adamko.kafkatorio.task
import dev.adamko.kafkatorio.jsonMapper
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.gradle.api.Task
import org.gradle.api.internal.specs.ExplainingSpec
object JsonPropertiesUpToDateSpec : ExplainingSpec<Task> {
override fun isSatisfiedBy(element: Task?): Boolean = whyUnsatisfied(element) == null
// Returns a description explaining why the task is outdated,
// or null if the task is up to date
override fun whyUnsatisfied(element: Task?): String? = with(element) {
when (this) {
null -> "task is null"
!is UpdatePackageJson ->
"task ${this::class.simpleName} is not instance of ${UpdatePackageJson::class.simpleName}"
else -> {
val packageJsonContent = packageJsonFile.get().asFile.readText()
val packageJson = jsonMapper.parseToJsonElement(packageJsonContent).jsonObject
val outdatedProperties = propertiesToCheck.get().mapNotNull { (key, expectedVal) ->
val actualVal = packageJson[key]?.jsonPrimitive?.content
when {
expectedVal == actualVal -> null
else -> "'$key' (expected: $expectedVal, actual: $actualVal)"
}
}
when {
outdatedProperties.isEmpty() -> null
outdatedProperties.size == 1 ->
"package.json has outdated property: ${outdatedProperties.single()}"
else ->
"package.json has outdated properties: ${outdatedProperties.joinToString(", ")}"
}
}
}
}
}

View file

@ -0,0 +1,38 @@
package dev.adamko.kafkatorio.task
import java.io.ByteArrayOutputStream
import org.gradle.api.Task
import org.gradle.api.internal.specs.ExplainingSpec
import org.gradle.api.tasks.Exec
/** Checks if a process is running. Windows only. */
class ProcessRunningSpec(
private val process: String,
private val ignoreCase: Boolean = true,
) : ExplainingSpec<Task> {
override fun isSatisfiedBy(element: Task?): Boolean = whyUnsatisfied(element) == null
override fun whyUnsatisfied(element: Task?): String? = with(element) {
when (this) {
null -> "task is null"
!is Exec -> "task ${this::class.simpleName} is not ${Exec::class.simpleName}"
else -> {
return ByteArrayOutputStream().use { outputStream ->
project.exec {
commandLine("tasklist") // Windows only for now...
standardOutput = outputStream
}
if (outputStream.toString().contains(process, ignoreCase)) {
"found $process in tasklist"
} else {
null
}
}
}
}
}
}

View file

@ -1,28 +1,35 @@
package dev.adamko.kafkatorio.task
import com.github.gradle.node.npm.task.NpmTask
import javax.inject.Inject
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
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.work.Incremental
import org.gradle.work.NormalizeLineEndings
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
@CacheableTask
abstract class TypescriptToLuaTask : NpmTask() {
@get:InputDirectory
@get:SkipWhenEmpty
@get:PathSensitive(PathSensitivity.RELATIVE)
@Suppress("UnstableApiUsage")
@get:NormalizeLineEndings
abstract val sourceFiles: DirectoryProperty
@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty
@get:Inject
abstract val fs: FileSystemOperations
init {
super.setGroup(BasePlugin.BUILD_GROUP)
@ -33,4 +40,17 @@ abstract class TypescriptToLuaTask : NpmTask() {
super.args.set(parseSpaceSeparatedArgs("-- --outDir $temporaryDir"))
}
@TaskAction
fun tstl() {
fs.delete { delete(temporaryDir) }
temporaryDir.mkdirs()
super.exec()
fs.sync {
from(temporaryDir)
into(outputDirectory)
}
}
}

View file

@ -1,11 +1,24 @@
package dev.adamko.kafkatorio.task
import com.github.gradle.node.NodePlugin
import dev.adamko.kafkatorio.jsonMapper
import javax.inject.Inject
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.support.useToRun
import org.jetbrains.kotlin.util.suffixIfNot
@CacheableTask
abstract class UpdatePackageJson : DefaultTask() {
@get:Input
@ -14,10 +27,43 @@ abstract class UpdatePackageJson : DefaultTask() {
@get:OutputFile
abstract val packageJsonFile: RegularFileProperty
@get:Inject
abstract val providers: ProviderFactory
//
// @get:Inject
// abstract val fs: FileSystemOperations
init {
group = NodePlugin.NODE_GROUP
description = "Read the package.json file and update the version and name, based on the project."
outputs.upToDateWhen(JsonPropertiesUpToDateSpec)
super.doFirst {
propertiesToCheck.disallowChanges()
packageJsonFile.disallowChanges()
}
}
@TaskAction
fun exec() {
val pj = packageJsonFile.asFile.get()
logger.info("updating package.json ${pj.canonicalPath}")
val packageJson = jsonMapper.parseToJsonElement(pj.readText()).jsonObject
val newJsonProps = propertiesToCheck.get().mapValues { (_, newVal) -> JsonPrimitive(newVal) }
val packageJsonUpdate = JsonObject(packageJson + newJsonProps)
val packageJsonContentUpdated =
jsonMapper
.encodeToString(packageJsonUpdate)
.suffixIfNot("\n")
logger.debug(packageJsonContentUpdated)
packageJsonFile.get().asFile.writer().useToRun {
write(packageJsonContentUpdated)
}
}
}