migrate docker-compose convention plugin out of buildSrc
This commit is contained in:
parent
105555bf6c
commit
f804c39a3b
32 changed files with 564 additions and 239 deletions
|
|
@ -1,8 +1,9 @@
|
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
kotlin("jvm") version embeddedKotlinVersion
|
||||
id("kafkatorio.conventions.kotlin-dsl")
|
||||
// `kotlin-dsl`
|
||||
// kotlin("jvm") version embeddedKotlinVersion
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
@ -34,20 +35,20 @@ dependencies {
|
|||
implementation(libs.kotlinx.cli)
|
||||
}
|
||||
|
||||
val gradleJvmTarget = "11"
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(gradleJvmTarget))
|
||||
}
|
||||
}
|
||||
|
||||
kotlinDslPluginOptions {
|
||||
jvmTarget.set(gradleJvmTarget)
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = gradleJvmTarget
|
||||
}
|
||||
}
|
||||
//val gradleJvmTarget = "11"
|
||||
//
|
||||
//kotlin {
|
||||
// jvmToolchain {
|
||||
// (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(gradleJvmTarget))
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//kotlinDslPluginOptions {
|
||||
// jvmTarget.set(gradleJvmTarget)
|
||||
//}
|
||||
//
|
||||
//tasks.withType<KotlinCompile>().configureEach {
|
||||
// kotlinOptions {
|
||||
// jvmTarget = gradleJvmTarget
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
val gradleJvmTarget = "11"
|
||||
|
||||
dependencies {
|
||||
implementation(platform(kotlin("bom")))
|
||||
implementation(gradleApi())
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(gradleJvmTarget))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = gradleJvmTarget
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package kafkatorio.common
|
||||
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
|
||||
|
||||
fun Configuration.asProvider() {
|
||||
isVisible = false
|
||||
isCanBeResolved = false
|
||||
isCanBeConsumed = true
|
||||
}
|
||||
|
||||
|
||||
fun Configuration.asConsumer() {
|
||||
isVisible = false
|
||||
isCanBeResolved = true
|
||||
isCanBeConsumed = false
|
||||
}
|
||||
|
|
@ -16,3 +16,16 @@ dependencyResolutionManagement {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
include(
|
||||
":modules:common-lib",
|
||||
|
||||
":modules:build-conventions",
|
||||
|
||||
":modules:docker-compose",
|
||||
":modules:factorio-mod",
|
||||
":modules:typescript",
|
||||
)
|
||||
|
||||
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
|
|
|||
|
|
@ -1,63 +1,63 @@
|
|||
package kafkatorio.conventions.infra
|
||||
|
||||
import kafkatorio.tasks.DockerEnvUpdateTask
|
||||
import kafkatorio.extensions.DOCKER_COMPOSE_TASK_GROUP
|
||||
import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
|
||||
|
||||
plugins {
|
||||
base
|
||||
id("kafkatorio.conventions.infra.process-runner")
|
||||
}
|
||||
|
||||
|
||||
val dockerComposeProjectName: String by extra(rootProject.name)
|
||||
val dockerSrcDir: Directory by extra(layout.projectDirectory.dir("src"))
|
||||
|
||||
|
||||
val dockerUp by tasks.registering(Exec::class) {
|
||||
group = DOCKER_COMPOSE_TASK_GROUP
|
||||
|
||||
dependsOn(tasks.assemble)
|
||||
logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
||||
|
||||
workingDir = dockerSrcDir.asFile
|
||||
commandLine = parseSpaceSeparatedArgs(""" docker-compose up -d """)
|
||||
}
|
||||
|
||||
|
||||
val dockerDown by tasks.registering(Exec::class) {
|
||||
group = DOCKER_COMPOSE_TASK_GROUP
|
||||
|
||||
logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
||||
|
||||
workingDir = dockerSrcDir.asFile
|
||||
commandLine = parseSpaceSeparatedArgs(""" docker-compose down """)
|
||||
}
|
||||
|
||||
|
||||
val dockerRemove by tasks.registering(Exec::class) {
|
||||
group = DOCKER_COMPOSE_TASK_GROUP
|
||||
|
||||
logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
||||
|
||||
workingDir = dockerSrcDir.asFile
|
||||
commandLine = parseSpaceSeparatedArgs(""" docker-compose rm --stop -v -f """)
|
||||
}
|
||||
|
||||
|
||||
tasks.matching { it.name == "processRun" }.configureEach { dependsOn(dockerUp, dockerEnvUpdate) }
|
||||
tasks.matching { it.name == "processKill" }.configureEach { dependsOn(dockerDown) }
|
||||
|
||||
|
||||
val dockerEnvUpdate by tasks.registering(DockerEnvUpdateTask::class) {
|
||||
dotEnvFile.set(dockerSrcDir.file(".env"))
|
||||
|
||||
properties(
|
||||
"COMPOSE_PROJECT_NAME" to dockerComposeProjectName,
|
||||
"KAFKATORIO_VERSION" to project.version,
|
||||
"REGISTRY_HOST" to providers.gradleProperty("dockerContainerRegistryHost"),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
tasks.assemble { dependsOn(dockerEnvUpdate) }
|
||||
//import kafkatorio.tasks.DockerEnvUpdateTask
|
||||
//import kafkatorio.extensions.DOCKER_COMPOSE_TASK_GROUP
|
||||
//import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
|
||||
//
|
||||
//plugins {
|
||||
// base
|
||||
// id("kafkatorio.conventions.infra.process-runner")
|
||||
//}
|
||||
//
|
||||
//
|
||||
//val dockerComposeProjectName: String by extra(rootProject.name)
|
||||
//val dockerSrcDir: Directory by extra(layout.projectDirectory.dir("src"))
|
||||
//
|
||||
//
|
||||
//val dockerUp by tasks.registering(Exec::class) {
|
||||
// group = DOCKER_COMPOSE_TASK_GROUP
|
||||
//
|
||||
// dependsOn(tasks.assemble)
|
||||
// logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
||||
//
|
||||
// workingDir = dockerSrcDir.asFile
|
||||
// commandLine = parseSpaceSeparatedArgs(""" docker-compose up -d """)
|
||||
//}
|
||||
//
|
||||
//
|
||||
//val dockerDown by tasks.registering(Exec::class) {
|
||||
// group = DOCKER_COMPOSE_TASK_GROUP
|
||||
//
|
||||
// logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
||||
//
|
||||
// workingDir = dockerSrcDir.asFile
|
||||
// commandLine = parseSpaceSeparatedArgs(""" docker-compose down """)
|
||||
//}
|
||||
//
|
||||
//
|
||||
//val dockerRemove by tasks.registering(Exec::class) {
|
||||
// group = DOCKER_COMPOSE_TASK_GROUP
|
||||
//
|
||||
// logging.captureStandardOutput(LogLevel.LIFECYCLE)
|
||||
//
|
||||
// workingDir = dockerSrcDir.asFile
|
||||
// commandLine = parseSpaceSeparatedArgs(""" docker-compose rm --stop -v -f """)
|
||||
//}
|
||||
//
|
||||
//
|
||||
//tasks.matching { it.name == "processRun" }.configureEach { dependsOn(dockerUp, dockerEnvUpdate) }
|
||||
//tasks.matching { it.name == "processKill" }.configureEach { dependsOn(dockerDown) }
|
||||
//
|
||||
//
|
||||
//val dockerEnvUpdate by tasks.registering(DockerEnvUpdateTask::class) {
|
||||
// dotEnvFile.set(dockerSrcDir.file(".env"))
|
||||
//
|
||||
// properties(
|
||||
// "COMPOSE_PROJECT_NAME" to dockerComposeProjectName,
|
||||
// "KAFKATORIO_VERSION" to project.version,
|
||||
// "REGISTRY_HOST" to providers.gradleProperty("dockerContainerRegistryHost"),
|
||||
// )
|
||||
//}
|
||||
//
|
||||
//
|
||||
//tasks.assemble { dependsOn(dockerEnvUpdate) }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package kafkatorio.tasks
|
||||
package dev.adamko.geedeecee
|
||||
|
||||
import kafkatorio.extensions.filesChecksum
|
||||
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
|
||||
|
|
@ -20,8 +20,16 @@ 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(
|
||||
|
|
@ -77,5 +85,12 @@ abstract class DockerComposeExec @Inject constructor(
|
|||
|
||||
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,7 +1,6 @@
|
|||
package kafkatorio.tasks
|
||||
package dev.adamko.geedeecee
|
||||
|
||||
import javax.inject.Inject
|
||||
import kafkatorio.extensions.DOCKER_COMPOSE_TASK_GROUP
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
|
|
@ -12,14 +11,15 @@ import org.gradle.api.tasks.WriteProperties
|
|||
|
||||
@CacheableTask
|
||||
open class DockerEnvUpdateTask @Inject constructor(
|
||||
private val objects: ObjectFactory
|
||||
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 = DOCKER_COMPOSE_TASK_GROUP
|
||||
group = GDCPlugin.GCD_TASK_GROUP
|
||||
super.setOutputFile(dotEnvFile)
|
||||
super.getLogging().captureStandardOutput(LogLevel.LIFECYCLE)
|
||||
}
|
||||
|
|
@ -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")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import kafkatorio.conventions.overrideKotlinLanguageVersion
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
kotlin("jvm") version embeddedKotlinVersion
|
||||
id("kafkatorio.conventions.kotlin-dsl")
|
||||
kotlin("plugin.serialization") version embeddedKotlinVersion
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.kotlin.bom))
|
||||
|
||||
|
|
@ -32,20 +34,42 @@ dependencies {
|
|||
}
|
||||
|
||||
|
||||
val gradleJvmTarget = "11"
|
||||
//val gradleJvmTarget = 11
|
||||
//
|
||||
//kotlin {
|
||||
// jvmToolchain(gradleJvmTarget)
|
||||
//}
|
||||
//
|
||||
//kotlinDslPluginOptions {
|
||||
// jvmTarget.set("$gradleJvmTarget")
|
||||
//}
|
||||
//
|
||||
//tasks.withType<KotlinCompile>().configureEach {
|
||||
// kotlinOptions {
|
||||
// jvmTarget = "$gradleJvmTarget"
|
||||
// }
|
||||
//}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(gradleJvmTarget))
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("factorioMod") {
|
||||
displayName = "Factorio Mod"
|
||||
id = "dev.adamko.factorio-mod"
|
||||
implementationClass = "dev.adamko.gradle.factorio.FactorioModPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlinDslPluginOptions {
|
||||
jvmTarget.set(gradleJvmTarget)
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = gradleJvmTarget
|
||||
}
|
||||
}
|
||||
overrideKotlinLanguageVersion("1.6")
|
||||
|
||||
//val gradleKotlinTarget = "1.7"
|
||||
//
|
||||
//afterEvaluate {
|
||||
// tasks.withType<KotlinCompile>().configureEach {
|
||||
// kotlinOptions {
|
||||
// apiVersion = gradleKotlinTarget
|
||||
// languageVersion = gradleKotlinTarget
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package dev.adamko.gradle.factorio
|
||||
|
||||
import javax.inject.Inject
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.internal.file.FileOperations
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class FactorioModInstallTask @Inject constructor(
|
||||
private val files: FileSystemOperations,
|
||||
) : DefaultTask() {
|
||||
|
||||
@get:Internal
|
||||
abstract val clientModDirectory: DirectoryProperty
|
||||
|
||||
@get:InputFiles
|
||||
abstract val modFiles: ConfigurableFileCollection
|
||||
|
||||
init {
|
||||
description = "Copy the mod to the Factorio client"
|
||||
group = FactorioModPlugin.TASK_GROUP
|
||||
|
||||
super.onlyIf {
|
||||
require(it is FactorioModInstallTask)
|
||||
it.clientModDirectory.asFile.get().exists()
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun install() {
|
||||
logger.lifecycle("Copying mod from $modFiles to $clientModDirectory")
|
||||
|
||||
files.copy {
|
||||
from(modFiles)
|
||||
into(clientModDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package dev.adamko.factorio_gradle
|
||||
package dev.adamko.gradle.factorio
|
||||
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
package dev.adamko.gradle.factorio.mod_portal
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.client.plugins.defaultRequest
|
||||
import io.ktor.client.plugins.logging.LogLevel
|
||||
import io.ktor.client.request.forms.MultiPartFormDataContent
|
||||
import io.ktor.client.request.forms.formData
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.Headers
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.Parameters
|
||||
import io.ktor.http.append
|
||||
import io.ktor.http.isSuccess
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.gradle.api.internal.tasks.userinput.UserInputHandler
|
||||
import org.gradle.kotlin.dsl.support.useToRun
|
||||
import io.ktor.client.plugins.logging.Logger as KtorLogger
|
||||
import io.ktor.client.plugins.logging.Logging as KtorLogging
|
||||
import org.gradle.api.logging.Logger as GradleLogger
|
||||
import org.gradle.api.logging.Logging as GradleLogging
|
||||
|
||||
|
||||
class FactorioModPortalPublishClient(
|
||||
private val userInputHandler: UserInputHandler,
|
||||
private val distributionZip: File,
|
||||
private val modName: String,
|
||||
private val modVersion: String,
|
||||
private val portalApiKey: String,
|
||||
private val portalUploadEndpoint: String,
|
||||
private val modPortalBaseURl: String,
|
||||
) {
|
||||
|
||||
private val logger: GradleLogger = GradleLogging.getLogger(this::class.java)
|
||||
|
||||
private fun client() = HttpClient(CIO) {
|
||||
install(KtorLogging) {
|
||||
logger = object : KtorLogger {
|
||||
override fun log(message: String) {
|
||||
this@FactorioModPortalPublishClient.logger.debug(message)
|
||||
}
|
||||
}
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
prettyPrint = false
|
||||
isLenient = true
|
||||
})
|
||||
}
|
||||
defaultRequest {
|
||||
header(HttpHeaders.Authorization, "Bearer $portalApiKey")
|
||||
port = 443
|
||||
}
|
||||
followRedirects = false
|
||||
expectSuccess = false
|
||||
}
|
||||
|
||||
fun uploadMod(): Unit = runBlocking {
|
||||
client().useToRun {
|
||||
val initUploadResponse = initUpload()
|
||||
|
||||
val enteredVersion = userInputHandler
|
||||
.askQuestion(
|
||||
"""
|
||||
|Are you sure you want to publish $modName:$modVersion?
|
||||
|Enter the version number to confirm:
|
||||
""".trimMargin(),
|
||||
"",
|
||||
)
|
||||
|
||||
val confirmed = enteredVersion.trim().equals(modVersion.trim(), ignoreCase = true)
|
||||
|
||||
if (confirmed) {
|
||||
upload(initUploadResponse)
|
||||
} else {
|
||||
logger.lifecycle("aborting mod '$modName' upload")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun HttpClient.initUpload(): InitUploadResponse.Success {
|
||||
|
||||
val response = submitForm(
|
||||
url = portalUploadEndpoint,
|
||||
formParameters = Parameters.build {
|
||||
append("mod", modName)
|
||||
}
|
||||
)
|
||||
|
||||
// println(response)
|
||||
val initUploadResponse: InitUploadResponse =
|
||||
Json.decodeFromString(InitUploadResponse.serializer(), response.bodyAsText())
|
||||
// println(initUploadResponse)
|
||||
|
||||
require(response.status.isSuccess()) { "init upload request failed" }
|
||||
|
||||
return when (initUploadResponse) {
|
||||
is Failure -> error(initUploadResponse)
|
||||
is InitUploadResponse.Success -> initUploadResponse
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun HttpClient.upload(initUploadResponse: InitUploadResponse.Success) {
|
||||
|
||||
val requestForm = formData {
|
||||
append(
|
||||
"file",
|
||||
distributionZip.readBytes(),
|
||||
Headers.build {
|
||||
append(HttpHeaders.ContentType, ContentType.Application.Zip)
|
||||
append(HttpHeaders.ContentDisposition, "filename=${distributionZip.name}")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val response = post(initUploadResponse.uploadUrl) {
|
||||
setBody(MultiPartFormDataContent(requestForm))
|
||||
}
|
||||
|
||||
val submitUploadResponse =
|
||||
Json.decodeFromString(SubmitUploadResponse.serializer(), response.bodyAsText())
|
||||
|
||||
// logger(response)
|
||||
// println(submitUploadResponse)
|
||||
require(response.status.isSuccess() && submitUploadResponse is SubmitUploadResponse.Success) {
|
||||
"upload request failed"
|
||||
}
|
||||
logger.lifecycle("Mod '$modName' uploaded successfully! ${modPortalBaseURl.removeSuffix("/")}/$modName")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package dev.adamko.gradle.factorio.mod_portal
|
||||
|
||||
import dev.adamko.gradle.factorio.FactorioModPlugin
|
||||
import io.ktor.http.URLBuilder
|
||||
import io.ktor.http.takeFrom
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.internal.tasks.userinput.UserInputHandler
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.property
|
||||
import org.gradle.kotlin.dsl.support.get
|
||||
|
||||
|
||||
abstract class FactorioModPublishTask @Inject constructor(
|
||||
objects: ObjectFactory,
|
||||
providers: ProviderFactory,
|
||||
) : DefaultTask() {
|
||||
|
||||
@get:InputFile
|
||||
abstract val distributionZip: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val modName: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val modVersion: Property<String>
|
||||
|
||||
// @get:Input
|
||||
// abstract val factorioVersion: Property<String>
|
||||
|
||||
/** Bearer API key */
|
||||
@get:Input
|
||||
val portalApiKey: Property<String> = objects.property<String>().convention(
|
||||
providers.gradleProperty("FACTORIO_MOD_PORTAL_API_KEY")
|
||||
)
|
||||
|
||||
@get:Input
|
||||
val portalApiUrl: Property<String> = objects.property<String>()
|
||||
.convention("https://mods.factorio.com/api/v2/")
|
||||
|
||||
/**
|
||||
* Base URL for linking to a mod page in the portal
|
||||
*
|
||||
* Example `https://mods.factorio.com/mod/my-mod-name`
|
||||
*/
|
||||
@get:Input
|
||||
val modPortalBaseURl: Property<String> = objects.property<String>()
|
||||
.convention("https://mods.factorio.com/mod/")
|
||||
|
||||
@get:Input
|
||||
val portalUploadEndpoint: Property<String> = objects.property<String>()
|
||||
.convention("mods/releases/init_upload")
|
||||
|
||||
|
||||
init {
|
||||
group = FactorioModPlugin.TASK_GROUP
|
||||
}
|
||||
|
||||
|
||||
@TaskAction
|
||||
fun deploy(): Unit = runBlocking {
|
||||
|
||||
val userInputHandler = services.get<UserInputHandler>()
|
||||
|
||||
val portalUploadEndpoint = URLBuilder(portalApiUrl.get())
|
||||
.takeFrom(portalUploadEndpoint.get())
|
||||
.buildString()
|
||||
|
||||
val client = FactorioModPortalPublishClient(
|
||||
userInputHandler = userInputHandler,
|
||||
distributionZip = distributionZip.get().asFile,
|
||||
modName = modName.get(),
|
||||
modVersion = modVersion.get(),
|
||||
portalApiKey = portalApiKey.get(),
|
||||
portalUploadEndpoint = portalUploadEndpoint,
|
||||
modPortalBaseURl = modPortalBaseURl.get(),
|
||||
)
|
||||
|
||||
client.uploadMod()
|
||||
}
|
||||
|
||||
// private fun validateInfoJson() {
|
||||
// val modVersion = modVersion.get()
|
||||
// val factorioVersion = factorioVersion.get()
|
||||
//
|
||||
// val distributionZip = distributionZip.get().asFile.toOkioPath()
|
||||
//
|
||||
// val zipContents = FileSystem.SYSTEM.openZip(distributionZip)
|
||||
//
|
||||
// val infoJson = zipContents.read("info.json".toPath()) {
|
||||
// generateSequence { readUtf8Line() }
|
||||
// }.joinToString("\n")
|
||||
//
|
||||
// val info: FactorioModInfo = Json.decodeFromString(FactorioModInfo.serializer(), infoJson)
|
||||
//
|
||||
// require(info.version == modVersion) {
|
||||
// "mod version mismatch, ${info.version} vs $modVersion "
|
||||
// }
|
||||
//
|
||||
// require(info.factorioVersion == factorioVersion) {
|
||||
// "Factorio version mismatch, ${info.factorioVersion} vs $factorioVersion "
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package dev.adamko.gradle.factorio.mod_portal
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
|
||||
|
||||
@Serializable(with = InitUploadResponse.Serializer::class)
|
||||
sealed interface InitUploadResponse {
|
||||
|
||||
/**
|
||||
* @param[uploadUrl] URL the mod zip file should be uploaded to
|
||||
*/
|
||||
@Serializable
|
||||
data class Success(
|
||||
@SerialName("upload_url")
|
||||
val uploadUrl: String,
|
||||
) : InitUploadResponse
|
||||
|
||||
object Serializer :
|
||||
JsonContentPolymorphicSerializer<InitUploadResponse>(InitUploadResponse::class) {
|
||||
override fun selectDeserializer(element: JsonElement) = when {
|
||||
"upload_url" in element.jsonObject -> Success.serializer()
|
||||
else -> Failure.serializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Serializable(with = SubmitUploadResponse.Serializer::class)
|
||||
sealed interface SubmitUploadResponse {
|
||||
|
||||
@Serializable
|
||||
data class Success(
|
||||
val success: Boolean
|
||||
) : SubmitUploadResponse
|
||||
|
||||
object Serializer : JsonContentPolymorphicSerializer<SubmitUploadResponse>(
|
||||
SubmitUploadResponse::class
|
||||
) {
|
||||
override fun selectDeserializer(element: JsonElement) = when {
|
||||
"success" in element.jsonObject -> Success.serializer()
|
||||
else -> Failure.serializer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
data class Failure(
|
||||
val error: String? = null,
|
||||
val message: String? = null,
|
||||
) : InitUploadResponse, SubmitUploadResponse {
|
||||
|
||||
private val reason: String?
|
||||
get() = reasons[error]
|
||||
|
||||
override fun toString() = "Failure(error=$error, message=$message, reason=$reason)"
|
||||
|
||||
companion object {
|
||||
private val reasons: Map<String?, String> = mapOf(
|
||||
//@formatter:off
|
||||
"InvalidApiKey" to "Missing or invalid API key for the current endpoint",
|
||||
"InvalidRequest" to "Invalid request",
|
||||
"InternalError" to "Internal error, please try again later.",
|
||||
"Forbidden" to "Insufficient permission for current endpoint",
|
||||
"Unknown" to "Unknown error, please try again later",
|
||||
"InvalidModRelease" to "Invalid release data in info.json",
|
||||
"InvalidModUpload" to "Invalid mod data in zipfile",
|
||||
"UnknownMod" to "Mod does not exist in mod portal",
|
||||
//@formatter:on
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
data class FactorioModInfo(
|
||||
val name: String,
|
||||
val version: String,
|
||||
val title: String,
|
||||
val description: String,
|
||||
val author: String,
|
||||
@SerialName("factorio_version") val factorioVersion: String,
|
||||
val dependencies: List<String>,
|
||||
)
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import org.gradle.kotlin.dsl.support.expectedKotlinDslPluginsVersion
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
|
|
@ -11,6 +12,10 @@ dependencies {
|
|||
// This project should only be providing simple plugins for configuring Gradle without
|
||||
// dependencies. Try to avoid setting dependencies here, else the Gradle script classpath might
|
||||
// get polluted and messy.
|
||||
|
||||
implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:$expectedKotlinDslPluginsVersion")
|
||||
// implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${embeddedKotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${libs.versions.kotlin.get()}")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package kafkatorio.conventions
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id("org.gradle.kotlin.kotlin-dsl")
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
val gradleJvmTarget = 11
|
||||
|
||||
//kotlin {
|
||||
// jvmToolchain(gradleJvmTarget)
|
||||
//}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(gradleJvmTarget))
|
||||
}
|
||||
}
|
||||
|
||||
kotlinDslPluginOptions {
|
||||
jvmTarget.set("$gradleJvmTarget")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "$gradleJvmTarget"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package kafkatorio.conventions
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.jvm.toolchain.JavaToolchainSpec
|
||||
import org.gradle.kotlin.dsl.plugins.dsl.*
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
fun Project.overrideKotlinLanguageVersion(
|
||||
gradleKotlinTarget: String = "1.6"
|
||||
) {
|
||||
afterEvaluate {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
apiVersion = gradleKotlinTarget
|
||||
languageVersion = gradleKotlinTarget
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue