import com.google.cloud.tools.jib.api.buildplan.ImageFormat import net.nemerosa.versioning.VersioningExtension import org.jooq.meta.jaxb.Logging import nu.studer.gradle.jooq.JooqEdition import java.nio.file.Files import nu.studer.gradle.jooq.JooqExtension import nu.studer.gradle.jooq.JooqGenerate import org.flywaydb.gradle.FlywayExtension import org.flywaydb.gradle.task.FlywayMigrateTask import org.gradle.kotlin.dsl.invoke import org.gradle.kotlin.dsl.jooqGenerator import org.gradle.plugins.ide.idea.model.IdeaModel import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_22 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.springframework.boot.gradle.dsl.SpringBootExtension import org.springframework.boot.gradle.tasks.bundling.BootBuildImage import org.springframework.boot.gradle.tasks.run.BootRun plugins { id("idea") id("org.springframework.boot") version "3.4.0-SNAPSHOT" id("io.spring.dependency-management") version "1.1.6" id("net.nemerosa.versioning") version "3.1.0" id("com.google.cloud.tools.jib") version "3.4.4" id("nu.studer.jooq") version "9.0" id("org.flywaydb.flyway") version "10.20.0" id("org.siouan.frontend-jdk21") version "9.0.0" kotlin("jvm") version "2.0.21" kotlin("plugin.spring") version "2.0.21" } repositories { gradlePluginPortal() mavenCentral() maven("https://repo.spring.io/snapshot") maven("https://repo.spring.io/milestone") } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-cache") implementation("org.springframework.boot:spring-boot-starter-jooq") implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.data:spring-data-commons") implementation("com.github.ben-manes.caffeine:caffeine") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.apache.commons:commons-text:1.12.0") implementation("org.apache.lucene:lucene-core:10.0.0") implementation("org.apache.lucene:lucene-queryparser:10.0.0") implementation("org.apache.lucene:lucene-analysis-common:10.0.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.9.0") implementation("io.github.openfeign:feign-core:13.1") implementation("io.github.openfeign:feign-jackson:13.1") implementation("io.github.openfeign:feign-httpclient:13.1") implementation("org.slf4j:slf4j-api") implementation("io.github.oshai:kotlin-logging-jvm:7.0.0") implementation("com.h2database:h2") implementation("org.flywaydb:flyway-core") jooqGenerator("com.h2database:h2") testImplementation(kotlin("test")) testImplementation("io.mockk:mockk:1.13.12") testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(module = "mockito-core") } annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") } configure<FlywayExtension> { // this migration references a temporary database at compile time that's only used for generating JOOQ classes // Spring creates a migration at runtime, before the application starts and bootstraps any connections via JOOQ val tempDir = Files.createTempDirectory("naviseerr-build-db") url = "jdbc:h2:file:${tempDir}/naviseerr" schemas = arrayOf("PUBLIC") user = "naviseerr" password = "naviseerr-pw" } tasks.withType<FlywayMigrateTask> { // nothing for now } configure<JooqExtension> { version.set("3.19.14") // the default (can be omitted) edition.set(JooqEdition.OSS) // the default (can be omitted) configurations { create("main") { // name of the jOOQ configuration generateSchemaSourceOnCompilation.set(true) // default (can be omitted) jooqConfiguration.apply { logging = Logging.WARN jdbc.apply { driver = "org.h2.Driver" url = flyway.url user = flyway.user password = flyway.password } generator.apply { name = "org.jooq.codegen.DefaultGenerator" database.apply { inputSchema = "PUBLIC" outputSchema = "PUBLIC" name = "org.jooq.meta.h2.H2Database" includes = ".*" excludes = "flyway_schema_history" } generate.apply { isDeprecated = false isRecords = true isImmutablePojos = true isFluentSetters = true } target.apply { packageName = "com.github.schaka.naviseerr.db" directory = "build/generated-src/jooq/main" // default (can be omitted) } strategy.name = "org.jooq.codegen.DefaultGeneratorStrategy" } } } } } tasks.withType<JooqGenerate> { dependsOn("flywayMigrate") // declare Flyway migration scripts as inputs on the jOOQ task inputs.files(fileTree("src/main/resources/db/migration")) .withPropertyName("migrations") .withPathSensitivity(PathSensitivity.RELATIVE) //allInputsDeclared.set(true) } configure<SpringBootExtension> { buildInfo() } configure<IdeaModel> { module { inheritOutputDirs = true } } kotlin { jvmToolchain { languageVersion.set(JavaLanguageVersion.of(23)) vendor.set(JvmVendorSpec.ADOPTIUM) } } tasks.withType<Test> { useJUnitPlatform() } tasks.withType<JavaCompile> { sourceCompatibility = JavaVersion.VERSION_22.toString() targetCompatibility = JavaVersion.VERSION_22.toString() options.compilerArgs.addAll( listOf("--add-modules=jdk.incubator.vector") ) } tasks.withType<KotlinCompile> { compilerOptions { freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = JVM_22 javaParameters = true } } frontend { nodeDistributionProvided.set(false) nodeVersion.set("22.11.0") nodeInstallDirectory.set(project.layout.projectDirectory.dir("node")) corepackVersion.set("latest") installScript.set("install") cleanScript.set("run clean") assembleScript.set("run build") //checkScript.set("run type-check") //publishScript.set("run publish") packageJsonDirectory.set(project.layout.projectDirectory.dir("src/main/frontend")) cacheDirectory.set(project.layout.projectDirectory.dir(".frontend-gradle-plugin")) } tasks.register<Copy>("processFrontendResources") { // Directory containing the artifacts produced by the frontend project val frontendBuildDir = project.layout.projectDirectory.dir("src/main/frontend/dist/spa") val frontendResourcesDir = project.layout.buildDirectory.dir("resources/main/static") dependsOn(":assembleFrontend") from(frontendBuildDir) into(frontendResourcesDir) } tasks.named<Task>("processResources") { dependsOn("processFrontendResources") } configure<VersioningExtension> { /** * Add GitHub CI branch name environment variable */ branchEnv = listOf("GITHUB_REF_NAME") } extra { val build = getBuild() val versioning: VersioningExtension = extensions.getByName<VersioningExtension>("versioning") val branch = versioning.info.branch.replace("/", "-") val shortCommit = versioning.info.commit.take(8) project.extra["build.date-time"] = build.buildDateAndTime project.extra["build.date"] = build.formattedBuildDate() project.extra["build.time"] = build.formattedBuildTime() project.extra["build.revision"] = versioning.info.commit project.extra["build.revision.abbreviated"] = shortCommit project.extra["build.branch"] = branch project.extra["build.user"] = build.userName() val containerImageName = "schaka/${project.name}" val containerImageTags = mutableSetOf(shortCommit, branch) if (branch.startsWith("v")) { containerImageTags.add("stable") } project.extra["jib.image.name"] = containerImageName project.extra["jib.image.tags"] = containerImageTags val registryImageName = "ghcr.io/${containerImageName}" val registryImageTags = containerImageTags.map { "ghcr.io/${containerImageName}:$it" }.toMutableList() project.extra["docker.image.name"] = registryImageName project.extra["docker.image.version"] = branch project.extra["docker.image.source"] = build.projectSourceRoot() project.extra["docker.image.tags"] = registryImageTags //remove when there's a better way of producing both arm64 and amd64 images containerImageTags.add("arm64-$branch") registryImageTags.add("ghcr.io/${containerImageName}:amd64-$branch") } tasks.withType<BootRun> { jvmArgs( arrayOf( "--add-modules=jdk.incubator.vector", "-Dspring.config.additional-location=optional:file:/workspace/application.yaml", "-Dsun.jnu.encoding=UTF-8", "-Dfile.encoding=UTF-8", "-Dorg.jooq.no-logo=true" ) ) } jib { to { image = "ghcr.io/${project.extra["jib.image.name"]}" tags = project.extra["jib.image.tags"] as Set<String> auth { username = System.getenv("USERNAME") password = System.getenv("GITHUB_TOKEN") } } from { image = "eclipse-temurin:23-jdk-noble" auth { username = System.getenv("DOCKERHUB_USER") password = System.getenv("DOCKERHUB_PASSWORD") } platforms { /* platform { architecture = "amd64" os = "linux" } */ platform { architecture = "arm64" os = "linux" } } } container { jvmFlags = listOf( "-Dspring.config.additional-location=optional:file:/workspace/application.yaml", "-Dsun.jnu.encoding=UTF-8", "-Dfile.encoding=UTF-8", "-Dorg.jooq.no-logo=true", "--add-modules=jdk.incubator.vector", "-Xms256m", "-Xmx512m" ) mainClass = "com.github.schaka.naviseerr.NaviseerrApplicationKt" ports = listOf("8080") format = ImageFormat.Docker // OCI not yet supported volumes = listOf("/database", "/workspace") labels.set( mapOf( "org.opencontainers.image.created" to "${project.extra["build.date"]}T${project.extra["build.time"]}", "org.opencontainers.image.revision" to project.extra["build.revision"] as String, "org.opencontainers.image.version" to project.version as String, "org.opencontainers.image.title" to project.name, "org.opencontainers.image.authors" to "Schaka <schaka@github.com>", "org.opencontainers.image.source" to project.extra["docker.image.source"] as String, "org.opencontainers.image.description" to project.description, ) ) // Exclude all "developmentOnly" dependencies, e.g. Spring devtools. configurationName.set("productionRuntimeClasspath") } } tasks.withType<BootBuildImage> { docker.publishRegistry.url = "ghcr.io" docker.publishRegistry.username = System.getenv("USERNAME") ?: "INVALID_USER" docker.publishRegistry.password = System.getenv("GITHUB_TOKEN") ?: "INVALID_PASSWORD" builder = "paketobuildpacks/builder-jammy-buildpackless-tiny" buildpacks = listOf( "paketobuildpacks/environment-variables", "paketobuildpacks/adoptium", "paketobuildpacks/java", "paketobuildpacks/health-checker" ) imageName = project.extra["docker.image.name"] as String version = project.extra["docker.image.version"] as String tags = project.extra["docker.image.tags"] as List<String> createdDate = "now" // It would also be possible to set this in the graalVmNative block, but we don't want to overwrite Spring's settings environment = mapOf( "BP_HEALTH_CHECKER_ENABLED" to "true", "BPL_JVM_CDS_ENABLED" to "true", "BP_JVM_CDS_ENABLED" to "true", "CDS_TRAINING_JAVA_TOOL_OPTIONS" to "--add-modules=jdk.incubator.vector -Dspring.profiles.active=cds", "BP_JVM_VERSION" to "23", "BPE_LANG" to "en_US.UTF-8", "BPE_LANGUAGE" to "LANGUAGE=en_US:en", "BPE_LC_ALL" to "en_US.UTF-8", "BPE_APPEND_JAVA_OPTS" to "--add-modules=jdk.incubator.vector -Xmx512m -Xms256m -Dorg.jooq.no-logo=true" ) }