Is there a way to generate jooq code from flyway migrations? - spring-boot

I have a kotlin spring boot application which is using Jooq.
My database is postresql and I have flyway migrations configured for it.
The problem is that when I am in my dev environment there is no problem generating code for jooq, but when I try to deploy my app to my server (with github actions and docker compose) I cant build the project, because Jooq is trying to connect to database (which is not started yet) to generate necessary code.
My gradle build looks like this:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jooq.meta.jaxb.ForcedType
import org.jooq.meta.jaxb.Logging
import org.jooq.meta.jaxb.Property
val environment = System.getenv()
plugins {
id("org.springframework.boot") version "2.7.5"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
kotlin("jvm") version "1.8.0"
kotlin("plugin.spring") version "1.8.0"
id("nu.studer.jooq") version "7.0"
}
group = "osu.salat23"
version = "ALPHA"
java.sourceCompatibility = JavaVersion.VERSION_17
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.flywaydb:flyway-core")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.github.yvasyliev:java-vk-bots-longpoll-api:3.5.3")
implementation("org.telegram:telegrambots-spring-boot-starter:6.3.0")
implementation("com.microsoft.playwright:driver-bundle:1.28.1")
implementation("com.microsoft.playwright:playwright:1.28.1")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("com.squareup.okhttp3:mockwebserver:4.10.0")
testImplementation("junit:junit:4.13.2")
jooqGenerator("org.postgresql:postgresql:42.5.1")
jooqGenerator("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
}
configurations {
all {
exclude("org.apache.logging.log4j", "log4j-slf4j-impl")
}
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
jooq {
version.set("3.16.0") // default (can be omitted)
edition.set(nu.studer.gradle.jooq.JooqEdition.OSS) // default (can be omitted)
configurations {
create("main") { // name of the jOOQ configuration
generateSchemaSourceOnCompilation.set(false) // default (can be omitted)
jooqConfiguration.apply {
logging = Logging.WARN
jdbc.apply {
driver = "org.postgresql.Driver"
url = environment["DATABASE_URL"] ?: "jdbc:postgresql://localhost:5432/postgres"
user = environment["DATABASE_USER"] ?: "postgres"
password = environment["DATABASE_PASSWORD"] ?: "postgres"
properties.add(Property().apply {
key = "ssl"
value = "false"
})
}
generator.apply {
name = "org.jooq.codegen.DefaultGenerator"
database.apply {
name = "org.jooq.meta.postgres.PostgresDatabase"
inputSchema = "public"
forcedTypes.addAll(listOf(
ForcedType().apply {
name = "varchar"
includeExpression = ".*"
includeTypes = "JSONB?"
},
ForcedType().apply {
name = "varchar"
includeExpression = ".*"
includeTypes = "INET"
}
))
}
generate.apply {
isDeprecated = false
isRecords = true
isImmutablePojos = true
isFluentSetters = true
}
target.apply {
packageName = "osu.salat23.circler"
directory = "src/generated-src/jooq/main" // default (can be omitted)
}
strategy.name = "org.jooq.codegen.DefaultGeneratorStrategy"
}
}
}
}
}
Is there a way to generate code not from database but rather from my flyway migrations? I have tried to follow official documentation but there is so little information and also no gladle kotlin dsl code examples, so I am asking it here.

You could consider intentionally committing the jOOQ generated code to your repo instead of re-generating it every time as part your build process.
Basic idea is, when you intend to make a DB change, presumably you'll be in your dev environment: run the DB migrations, then generate the jooq code, change your project to compile with the new code, commit.

There are 2 ways to do this:
If you think you're not using anything RDBMS vendor specific, then jOOQ can emulate (simple) Flyway migrations (much like any SQL script based migrations and database setups, including dumps) using the DDLDatabase
If you're using RDBMS vendor specific things, or more advanced Flyway features, which can't be emulated simply (e.g. repeatable migrations), then ideally, you just set up a build that runs an actual Flyway migration prior to code generation, e.g. in testcontainers
The article above uses Maven, but it should work the same way with Gradle, in principle.

I solved my problem the following way:
Because I am using github actions for my CI CD pipelines, I added a postgres service (because I am using vendor specific features) to my gh actions config:
jobs:
  Deploy:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:14.0
        env:
          POSTGRES_DB: postgres
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout#v3
      - uses: actions/setup-java#v3
        with:
          distribution: adopt
          java-version: 18
      - name: Setup gradle
        uses: gradle/gradle-build-action#v2
      - name: Execute gradle build
        run: ./gradlew clean && ./gradlew build && ./gradlew bootJar
The CI database credentials are also written in my gradle jooq config. By doing this, I can build my jar inside actions runner and then send it by using SCP to my server through ssh. Flyway generates database and jooq then generates code. Pretty simple yet good setup (at least for me)
Before that, I wanted to setup test containers, but decided that is was too big of a hustle for me.
If anybody will run into the same issue - I can send my gradle config if needed for better clarity.
Here is my gradle file for jooq compatibility:
import nu.studer.gradle.jooq.JooqExtension
import nu.studer.gradle.jooq.JooqGenerate
import org.flywaydb.gradle.task.FlywayMigrateTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jooq.meta.jaxb.ForcedType
import org.jooq.meta.jaxb.Logging
import org.jooq.meta.jaxb.Property
val environment = System.getenv()
plugins {
id("org.springframework.boot") version "2.7.5"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
kotlin("jvm") version "1.8.0"
kotlin("plugin.spring") version "1.8.0"
id("org.flywaydb.flyway") version "9.8.1"
id("nu.studer.jooq") version "7.0"
}
group = "osu.salat23"
version = "ALPHA"
java.sourceCompatibility = JavaVersion.VERSION_17
repositories {
mavenCentral()
maven(url="https://jitpack.io")
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("io.micrometer:micrometer-registry-prometheus")
implementation("com.github.cvb941:kotlin-parallel-operations:1.5.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.flywaydb:flyway-core")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.github.yvasyliev:java-vk-bots-longpoll-api:3.5.3")
implementation("org.telegram:telegrambots-spring-boot-starter:6.3.0")
implementation("com.microsoft.playwright:driver-bundle:1.28.1")
implementation("com.microsoft.playwright:playwright:1.28.1")
runtimeOnly("org.postgresql:postgresql")
implementation("com.github.holgerbrandl:jsonbuilder:-SNAPSHOT")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("com.squareup.okhttp3:mockwebserver:4.10.0")
testImplementation("junit:junit:4.13.2")
jooqGenerator("org.postgresql:postgresql:42.5.1")
jooqGenerator("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
}
configurations {
all {
exclude("org.apache.logging.log4j", "log4j-slf4j-impl")
}
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<JooqGenerate> {
dependsOn("flywayMigrate")
}
flyway {
url = "jdbc:postgresql://localhost:5432/postgres"
user = "postgres"
password = "postgres"
schemas = arrayOf("public")
locations = arrayOf("filesystem:${project.projectDir}/src/main/resources/db/migration")
}
jooq {
version.set("3.16.0") // default (can be omitted)
edition.set(nu.studer.gradle.jooq.JooqEdition.OSS) // 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.postgresql.Driver"
url = "jdbc:postgresql://localhost:5432/postgres"
user = "postgres"
password = "postgres"
properties.add(Property().apply {
key = "ssl"
value = "false"
})
}
generator.apply {
name = "org.jooq.codegen.DefaultGenerator"
database.apply {
name = "org.jooq.meta.postgres.PostgresDatabase"
inputSchema = "public"
forcedTypes.addAll(listOf(
ForcedType().apply {
name = "varchar"
includeExpression = ".*"
includeTypes = "JSONB?"
},
ForcedType().apply {
name = "varchar"
includeExpression = ".*"
includeTypes = "INET"
}
))
}
generate.apply {
isDeprecated = false
isRecords = true
isImmutablePojos = true
isFluentSetters = true
}
target.apply {
packageName = "osu.salat23.circler"
directory = "generated/generated-src/jooq/main" // default (can be omitted)
}
strategy.name = "org.jooq.codegen.DefaultGeneratorStrategy"
}
}
}
}
}

Related

How to generate jOOQ classes from Testcontainers database migrated with Flyway during Gradle build?

I am trying to generate jOOQ classes from a database running in docker container(Testcontainers) which will be migrated with flyway during gradle build. I am new to gradle and I created working proof of concept which does not feel right, because I have to start the container in configuration phase, so the database url, password and username are available to configuration extensions(flyway and jooq). Then I have to register container stop using deprecated method buildFinished
I would like for the container to start only when generateJooq task needs to be run. Ideally there would be three tasks, first would start the container with database, second would migrate the started database with flyway and third would generate jooq classes, and after that, stop the running container. Any advice how to do that?
import org.testcontainers.containers.PostgreSQLContainer
plugins {
id("org.flywaydb.flyway") version "8.5.13"
id("nu.studer.jooq") version "7.1.1"
}
repositories {
mavenCentral()
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.testcontainers:postgresql:1.17.3")
classpath("org.postgresql:postgresql:42.2.14")
}
}
val flywayMigration: Configuration by configurations.creating
dependencies {
flywayMigration("org.postgresql:postgresql:42.3.3")
jooqGenerator("org.postgresql:postgresql:42.3.3")
jooqGenerator("jakarta.xml.bind:jakarta.xml.bind-api:4.0.0")
}
var instance: PostgreSQLContainer<Nothing>? = null
task("postgreSQLContainer") {
instance = PostgreSQLContainer<Nothing>("postgres:11.6")
instance?.start()
gradle.buildFinished {
if (instance?.isRunning == true) {
instance?.stop()
}
}
}
flyway {
configurations = arrayOf("flywayMigration")
url = instance?.jdbcUrl
user = instance?.username
password = instance?.password
}
jooq {
configurations {
create("main") {
jooqConfiguration.apply {
logging = org.jooq.meta.jaxb.Logging.WARN
jdbc.apply {
driver = "org.postgresql.Driver"
url = flyway.url
user = flyway.user
password = flyway.password
}
generator.apply {
name = "org.jooq.codegen.DefaultGenerator"
database.apply {
inputSchema = "public"
}
target.apply {
packageName = "com.cleevio.jooq"
}
}
}
}
}
}
tasks.named<nu.studer.gradle.jooq.JooqGenerate>("generateJooq") {
dependsOn(tasks.named("postgreSQLContainer"))
dependsOn(tasks.named("flywayMigrate"))
inputs.files(fileTree("src/main/resources/db/migration"))
.withPropertyName("migrations")
.withPathSensitivity(PathSensitivity.RELATIVE)
allInputsDeclared.set(true)
}

How to parse and modify build.gradle.kts Kotlin Gradle build script?

I want to parse a build.gradle.kts (Gradle build script in Kotlin), so I can find out what values are currently set and I also want to modify or add new entries in some categories.
Example (build.gradle.kts):
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.2.6.RELEASE"
kotlin("jvm") version "1.3.71"
etc...
}
group = "net.myProject"
version = "1.0"
java.sourceCompatibility = JavaVersion.VERSION_11
val developmentOnly by configurations.creating
configurations {
runtimeClasspath {
extendsFrom(developmentOnly)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
etc...
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
It´s basically a classical Spring Boot application. What I would want to be able to do is:
Get some kind of structured representation of the file
So I can append a fixed version to a dependency (e.g. implementation("org.springframework.boot:spring-boot-starter-actuator :2.2.6.RELEASE")
And so I could append new dependencies into the dependencies list
I know that this is a special DSL for Gradle Build Scripts here, but where can I find this and how can I parse/use it?
Thanks!
Unfortunately kotlin doesn't seem to provide it's own parser, which means there won't be a simple answer and you'll have to deal with language updates down the line. You'll probably also want to make sure that the parsed structure allows you to preserve white-spaces to keep your formatting intact.
ktlint might be an interesting starting point. It uses the PSI-Elements from IntelliJ and also reuses IntelliJ's parser.
val normalizedText = text.replace("\r\n", "\n").replace("\r", "\n")
val positionByOffset = calculateLineColByOffset(normalizedText)
val fileName = if (script) "file.kts" else "file.kt"
val psiFile = psiFileFactory.createFileFromText(fileName, KotlinLanguage.INSTANCE, normalizedText) as KtFile
val errorElement = psiFile.findErrorElement()
if (errorElement != null) {
val (line, col) = positionByOffset(errorElement.textOffset)
throw ParseException(line, col, errorElement.errorDescription)
}
val rootNode = psiFile.node
// use visitor pattern on rootNode
Frankly, unless this brings a lot of value to your project, I'd try to find a different solution. Maybe you can read the values in your build.gradle.kts from an easily parsable source like a json file?
Hope that helps.

How to ignore gradle build of root project for a multi-project Kotlin Spring Boot application?

Background:
I currently have a multi-module (multi-project) application repo. The "root" is not a runnable application. It's merely the source directory where I have a root build.gradle.kts file which holds the dependencies and plugins that are common between all my sub-projects. Each of my sub-projects have their own build.gradle.kts.
So my overall project structure looks sort of like this:
my_root_project
- gradle
- wrapper
- gradle-wrapper.jar
- gradle-wrapper.properties
- gradle.build.kts
- settings.gradle.kts
- my_nested_project_a
- src
- main
- kotlin
- my_nested_project_b
...
Issue:
Every time I run gradle build, I get an error saying:
> Task :bootJar FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':bootJar'.
> Main class name has not been configured and it could not be resolved
However when I run any one of my sub-projects (e.g. build :my_nested_project_a:build), it builds just fine.
Current Gradle Build Files
Here's what I currently have in the "root" gradle.build.kts:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
group = "com.example"
version = "1.0.0"
java.sourceCompatibility = JavaVersion.VERSION_1_8
java.targetCompatibility = JavaVersion.VERSION_1_8
plugins {
id("org.springframework.boot") version "2.1.8.RELEASE" apply false
id("io.spring.dependency-management") version "1.0.8.RELEASE" apply false
kotlin("jvm") version "1.3.50"
kotlin("plugin.spring") version "1.3.50"
kotlin("plugin.jpa") version "1.3.50"
kotlin("plugin.allopen") version "1.3.50"
}
allprojects {
repositories {
maven(url = "https://my.company.com/repo/with/all/the/stuff/I/need")
}
apply(plugin = "org.jetbrains.kotlin.jvm")
apply(plugin = "java")
apply(plugin = "org.springframework.boot")
apply(plugin = "io.spring.dependency-management")
apply(plugin = "org.jetbrains.kotlin.plugin.spring")
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
}
NOTE: I'm using apply false on my plugins because I thought it would keep gradle from trying to find a main class when building using gradle build.
What I'm trying to do:
I have a CI pipeline that I'd like to simply run gradle build which should run the build task for all of the sub-projects. However, in that process, I'd like to ignore running the build for the "root" project or bypass it since it's not a runnable application, and just build the sub-projects.
Any help would be greatly appreciated! :)
If you want to ignore task bootJar,s o add the following configuration.
bootJar {
enabled = false
}
In your root build.gradle.kts, ignore bootJar task, with Kotlin DSL :
import org.springframework.boot.gradle.tasks.bundling.BootJar
tasks.getByName<BootJar>("bootJar") {
enabled = false
}
If you have the plugin applied in allprojects session, you're applying it to the root as well, and since it's the first one resolved in gradle build, you should have the main class configured there.
Alternatively, you can remove the apply(plugin = "org.springframework.boot") line from the root and apply the plugin only to the module that has the main method annotated with #SpringBootApplication, and point the plugin to the main class there.
Say your main class is in my_nested_project_a/src/main/com/example/MainClass.kt.
Your my_nested_project_a/build.gradle.kts should look like:
plugins {
id("org.springframework.boot")
}
springBoot {
mainClassName = "com.example.MainClass"
}
dependencies {
...
}
And you should remove this line from the root build.gradle.kts:
apply(plugin = "org.springframework.boot")
I have a similar setup and question. I replaced allprojects with subprojects and added jar.enabled(false) to the root build.gradle file and it worked.
plugins {
id("java-library")
id('org.jetbrains.kotlin.jvm') version "${kotlinVersion}"
id("com.diffplug.spotless") version "${spotlessVersion}"
id("maven-publish")
}
jar.enabled(false)
subprojects {
apply plugin: "java-library"
apply plugin: "org.jetbrains.kotlin.jvm"
apply plugin: "com.diffplug.spotless"
apply plugin: "maven-publish"
group = GROUP
version = VERSION_NAME
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation("org.junit.jupiter:junit-jupiter:${junitVersion}")
}
publishing {
publications {
library(MavenPublication) {
from components.java
}
}
repositories {
maven {
url "https://gitlab.mhighpoint.com/api/v4/projects/${System.getenv('CI_PROJECT_ID')}/packages/maven"
credentials(HttpHeaderCredentials) {
name = "Job-Token"
value = System.getenv('CI_JOB_TOKEN')
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
}
spotless {
java {
googleJavaFormat() // googleJavaFormat('1.1') to specify a specific version
}
kotlin {
target '**/src/**/*.kt'
ktlint("0.41.0").userData('disabled_rules': 'no-wildcard-imports,import-ordering')
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
format 'misc', {
target '**/*.gradle'
trimTrailingWhitespace()
indentWithSpaces() // or spaces. Takes an integer argument if you don't like 4
endWithNewline()
}
}
test {
useJUnitPlatform()
}
jar {
archiveBaseName = "${rootProject.name}-${project.name}"
}
tasks {
assemble.dependsOn(spotlessApply)
}
}

How to consolidate imported plugins to custom plugin in Gradle using Kotlin

I have microservices that will share some of the same configuration between all of them, mainly Jib, publish, and release. Not sure if it's possible to do the same for dependencies but it would be beneficial to include actuator and log4j2 in each. Here is the build.gradle.kts for one of my projects.
import net.researchgate.release.BaseScmAdapter
import net.researchgate.release.GitAdapter
import net.researchgate.release.ReleaseExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("com.gorylenko.gradle-git-properties") version "1.5.1"
id("com.google.cloud.tools.jib") version "1.6.1"
id("io.spring.dependency-management") version "1.0.7.RELEASE"
id("net.researchgate.release") version "2.8.1"
id("org.sonarqube") version "2.7.1"
id("org.springframework.boot") version "2.1.6.RELEASE"
kotlin("jvm") version "1.2.71"
kotlin("plugin.spring") version "1.2.71"
jacoco
`maven-publish`
}
java.sourceCompatibility = JavaVersion.VERSION_1_8
springBoot {
buildInfo {
group = project.properties["group"].toString()
version = project.properties["version"].toString()
description = project.properties["description"].toString()
}
}
repositories {
maven(url = uri(project.properties["nexus.url.gateway"].toString()))
mavenCentral()
}
dependencies {
// Kotlin
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Spring
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-log4j2")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.cloud:spring-cloud-config-server")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
dependencyManagement {
imports {
mavenBom("org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR3")
}
}
configurations.all {
exclude(group = "ch.qos.logback", module = "logback-classic")
exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
}
tasks {
withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
build { dependsOn(clean) }
afterReleaseBuild { dependsOn(publish) }
publish { dependsOn(build) }
jibDockerBuild { dependsOn(build) }
jacocoTestReport {
reports {
html.isEnabled = false
xml.isEnabled = true
}
}
}
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:company/${project.name}.git")
developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
url.set("https://github.com/company/${project.name}/")
}
}
versionMapping {
usage("java-api") {
fromResolutionOf("runtimeClasspath")
}
usage("java-runtime") {
fromResolutionResult()
}
}
}
}
repositories {
maven {
val releasesRepoUrl = "${project.properties["nexus.url.publish"].toString()}/releases"
val snapshotsRepoUrl = "${project.properties["nexus.url.publish"].toString()}/snapshots"
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
fun ReleaseExtension.git(configureFn : GitAdapter.GitConfig.() -> Unit) {
(propertyMissing("git") as GitAdapter.GitConfig).configureFn()
}
release {
scmAdapters = mutableListOf<Class<out BaseScmAdapter>> ( GitAdapter::class.java )
git {
requireBranch = "develop"
pushToRemote = project.properties["release.git.remote"].toString()
pushReleaseVersionBranch = "master"
tagTemplate = "${project.name}.${project.version}"
}
}
jib {
from {
image = "openjdk:8-jdk-alpine"
}
to {
image = "host:port/${project.name}:${project.version}"
auth {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
container {
workingDirectory = "/"
ports = listOf("8080")
environment = mapOf(
"SPRING_OUTPUT_ANSI_ENABLED" to "ALWAYS",
"SPRING_CLOUD_BOOTSTRAP_LOCATION" to "/path/to/bootstrap.yml"
)
useCurrentTimestamp = true
}
setAllowInsecureRegistries(true)
}
I was able to get a custom plugin created and added to this project using git#github.com:klg71/kotlintestplugin.git and git#github.com:klg71/kotlintestpluginproject.git but I have no idea how to implement these existing plugins and their configurations. In the main Plugin class in the apply function I am able to call the project.pluginManager.apply(PublishingPlugin::class.java) which causes the task to show in the project referencing the custom plugin but I can't figure out how to configure it and it does not successfully publish to the nexus server. I can publish the plugin itself to the nexus server and reference it in the microservice but it skips running the task, which I assume is caused by the configuration not being included. Also, when trying to apply/configure the Jib plugin, all of the classes are not visible when attempting to import.
So the above answer isn't super long and to preserve the issues I ran into I am posting a new answer.
PLUGIN
This portion of the answer is going to discuss the actual custom plugin project.
Because the plugins wrapper in the build.gradle.kts is runtime, the CustomPlugin.kt does not have access to it at compile time. My boss who is much smarter than me was kind enough to point this out to me even though he has never worked with gradle. Although I looked pretty dumb in front of him he still got me up and running by basically following the 'legacy' way of applying plugins in gradle.
plugins { // This is a runtime script preventing plugins declared here to be accessible in CustomPlugin.kt but is used to actually publish/release this plugin itself
id("net.researchgate.release") version "2.8.1"
kotlin("jvm") version "1.3.0"
`maven-publish`
}
repositories {
maven { url = uri("https://plugins.gradle.org/m2/") } // This is required to be able to import plugins below in the dependencies
jcenter()
}
dependencies {
compile(kotlin("stdlib"))
compile(kotlin("reflect"))
// These must be declared here (at compile-time) in order to access in CustomPlugin.kt
compile(group = "gradle.plugin.com.gorylenko.gradle-git-properties", name = "gradle-git-properties", version = "2.2.0")
compile(group = "gradle.plugin.com.google.cloud.tools", name = "jib-gradle-plugin", version = "1.7.0")
compile(group = "net.researchgate", name = "gradle-release", version = "2.8.1")
compile(group = "org.asciidoctor", name = "asciidoctor-gradle-plugin", version = "1.5.9.2")
compile(group = "org.jetbrains.dokka", name = "dokka-gradle-plugin", version = "0.9.18")
compile(group = "org.sonarsource.scanner.gradle", name = "sonarqube-gradle-plugin", version = "2.8")
implementation(gradleApi()) // This exposes the gradle API to CustomPlugin.kt
}
This allowed me to have access to jib and everything else in the CustomPlugin.kt.
The plugins jacoco and maven-publish are automatically accessible in the plugin project but still need to be added in the microservice project referencing the plugin. I was unable to find a workaround for this unfortunately.
I included the typical maven-publish plugin in the build.gradle.kts to push to nexus with the publishing task configurations in the build.gradle.kts as well so I could pull this from nexus in the microservice that wanted to use the plugin.
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:diendanyoi54/${project.name}.git")
developerConnection .set("scm:git:git#github.com:diendanyoi54/${project.name}.git")
url.set("https://github.com/diendanyoi54/${project.name}/")
}
}
}
}
repositories {
maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
url = uri(if (version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
Lastly, you want to make sure you include the properties file that will tell the microservices where the plugin class is. In Intellij's IDEA, when typing the path to the implementation-class it auto completed for me.
The name of this file should reflect apply(plugin = "string") in the microservice's build.gradle.kts.
IMPLEMENTATION
This portion of the answer is going to reflect the microservice project that will be referencing the plugin. As stated above, jacoco and maven-publish still need to be added to the plugin block in the build.gradle.kts for some reason (I think because they are official gradle plugins).
To reference the plugin from the nexus server it was published to, the microservice must reference it in the buildscript.
buildscript { // Custom plugin must be accessed by this buildscript
repositories {
maven {
url = uri("https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository/maven-public")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
dependencies { classpath("com.company:kotlin-consolidated-plugin:1.0.0-SNAPSHOT") }
}
Lastly, the plugin must be applied using the properties file name referenced above.
apply(plugin = "com.company.kotlinconsolidatedplugin") // Custom plugin cannot reside in plugin declaration above
I created sample projects of these and posted them to Github so feel free to clone or take a look:
git#github.com:diendanyoi54/kotlin-consolidated-plugin.git
git#github.com:diendanyoi54/kotlin-consolidated-plugin-implementation.git
I was able to successfully able to use the github repo examples referenced above to accomplish what I needed with the publish task. Here is my custom plugin's build.gradle.kts.
plugins {
id("com.google.cloud.tools.jib") version "1.6.1"
id("org.sonarqube") version "2.7.1"
kotlin("jvm") version "1.3.0"
`maven-publish`
}
dependencies {
compile(kotlin("stdlib"))
compile(kotlin("reflect"))
implementation(gradleApi())
}
repositories {
jcenter()
}
publishing {
publications {
create<MavenPublication>(project.name) {
from(components["java"])
pom {
scm {
connection.set("scm:git:git#github.com:company/${project.name}.git")
developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
url.set("https://github.com/company/${project.name}/")
}
}
}
}
repositories {
maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
url = uri(if (version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
credentials {
username = project.properties["nexus.user"].toString()
password = project.properties["nexus.password"].toString()
}
}
}
}
Here is the CustomPlugin.kt class.
package com.company.gradlemicroserviceplugin
//import com.google.cloud.tools.jib.gradle.JibExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.plugins.PublishingPlugin
import org.gradle.testing.jacoco.tasks.JacocoReport
import java.net.URI
open class CustomPlugin : Plugin<Project> {
override fun apply(project: Project) {
// applySonar(project)
applyPublish(project)
// applyJib(project)
}
// private fun applySonar(project: Project) {
// project.pluginManager.apply("sonarqube")
// val task = project.task("jacocoTestReport") as JacocoReport
// task.reports = JacocoReport()
// jacocoTestReport { This was nested in the tasks declaration in build.gradle.kts so the fields below are the fields I'm trying to set in task.reports
// reports {
// html.isEnabled = false
// xml.isEnabled = true
// }
// }
// }
private fun applyPublish(project: Project) {
project.pluginManager.apply(PublishingPlugin::class.java)
val publishingExtension = project.extensions.findByType(PublishingExtension::class.java)
val mavenPublication = publishingExtension?.publications?.create(project.name, MavenPublication::class.java)
publishingExtension?.repositories?.maven {
val baseUrl = "https://${project.properties["nexus.host"].toString()}:${project.properties["nexus.port.jar"].toString()}/repository"
it.url = URI(if (project.version.toString().endsWith("SNAPSHOT")) "$baseUrl/maven-snapshots" else "$baseUrl/maven-releases")
it.credentials { cred ->
cred.username = project.properties["nexus.user"].toString()
cred.password = project.properties["nexus.password"].toString()
}
}
mavenPublication?.from(project.components.findByName("java"))
mavenPublication?.pom?.scm {
it.connection.set("scm:git:git#github.com:company/${project.name}.git")
it.developerConnection.set("scm:git:git#github.com:company/${project.name}.git")
it.url.set("https://github.com/company/${project.name}/")
}
}
// private fun applyJib(project: Project) {
// project.pluginManager.apply(JibPlugin::class.java)
//
// }
}
There are definitely areas of improvement on this but at least I got something working here. There is maven-publish logic in both build.gradle.kts because I push to the custom plugin to nexus and the maven-publish logic is in the CustomPlugin.kt class so the microservice that references this plugin can use it. However, I am unable to successfully setup Jib and Sonar. Jib doesn't give me access to anything in com.google.cloud.tools.jib.gradle preventing me from using the same approach as I used in maven-publish.
For Sonar I think I'm on the right track with retrieving the task by its name but I'm unable to set any fields that belong to task.reports because they are all final and this is necessary for Sonar to properly analyze Kotlin.
Applying built-in plugins
plugins {
java
id("jacoco")
}
You can also use the older apply syntax:
apply(plugin = "checkstyle")
Applying external plugins
plugins {
id("org.springframework.boot") version "2.0.1.RELEASE"
}
i am not good in kotlin but here is link to better understanding missing migration guide to the Gradle Kotlin DSL

Gradle Kotlin DSL with wsdl2java

Migrating from Groovy to Kotlin and stumbled on a simple problem on wsdl2java generation.
Question is simple, does anyone have good example with it. Google wasn't very helpful and not good in Kotlin DSL yet also syntax wise.
Also using OpenJDK11.
plugins {
id("no.nils.wsdl2java") version "0.10"
}
wsdl2java {
enabled = true
wsdlsToGenerate = [
[
"-xjc",
"-p", "bla.bla.generated",
"-wsdlLocation", "classpath:wsdl/v1.wsdl",
"-autoNameResolution", "$projectDir/src/main/resources/wsdl/v1.wsdl"
],
[
"-xjc",
"-p", "bla.bla.generated",
"-wsdlLocation", "classpath:wsdl/v2.wsdl",
"-autoNameResolution", "$projectDir/src/main/resources/wsdl/v2.wsdl"
]]
generatedWsdlDir = file("$projectDir/src/main/java")
wsdlDir = file("$projectDir/src/main/resources/wsdl")
}
dependencies {
implementation(project(":common"))
implementation(project(":etcd"))
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-hateoas")
implementation("org.springframework.boot:spring-boot-starter-quartz")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-web-services")
api("no.nils:wsdl2java")
compileOnly("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
}
tasks.jar {
archiveFileName.set("ext.jar")
}
I got wsdl2java working through trial and error with the following:
plugins {
id("no.nils.wsdl2java") version "0.10"
}
wsdl2javaExt {
cxfVersion = "3.3.0"
deleteGeneratedSourcesOnClean = true
}
tasks.withType<no.nils.wsdl2java.Wsdl2JavaTask> {
// The use of ArrayList(listOf) is necessary as the Wsdl2JavaTask seems to make inline changes to its arguments
wsdlsToGenerate = listOf(
ArrayList(listOf("-p", "dk.grydholt.integration.sacho",
"-autoNameResolution", "-xjc-npa",
"-wsdlLocation", "classpath:wsdl/sacho/EduModelService.wsdl",
"$projectDir/src/main/resources/wsdl/sacho/EduModelService.wsdl")))
generatedWsdlDir = file("$projectDir/src/generated/java")
wsdlDir = file("$projectDir/src/main/resources/wsdl/sacho")
}
sourceSets {
create("generated") {
java.srcDirs(listOf("src/generated/java"))
}
}
Notice the use of ArrayList. It took me some time to debug as you'll get strange type errors if you do listOf(listOf("...")).
you could do it like:
plugins {
id("no.nils.wsdl2java") version "0.12"
}
dependencies {
// SOAP dependencies
implementation("org.springframework.boot:spring-boot-starter-web-services") {
exclude(module = "spring-boot-starter-tomcat")
}
implementation("org.glassfish.jaxb:jaxb-runtime")
implementation("org.apache.cxf.xjc-utils:cxf-xjc-runtime:3.3.1")
}
wsdl2java {
wsdlDir = file("$projectDir/src/main/wsdl")
wsdlsToGenerate = listOf(
// look here for other parameters: https://cxf.apache.org/docs/wsdl-to-java.html
listOf(
// activate plugin to add a toString() method to generated classes
// equivalent to: -Xts:style:org.apache.cxf.xjc.runtime.JAXBToStringStyle.DEFAULT_STYLE
"-xjc-Xts",
// generate getters methods for Booleans
"-xjc-Xbg",
// adds the #Generated annotation to classes generated.
"-mark-generated",
// automatically resolve naming conflicts without requiring the use of binding customizations.
"-autoNameResolution",
// map each of the namespaces to its own java package
// this is done 'cause the namespaces are conflicting between the different WSDLs files
// we have, which is leading to class overwriting during code generation
// you should look up these URLs in the WSDLs and come with package names in case
// you find out about conflicts
"-p", "http://xxx/xi/A1S/Global=e.r.t.y",
"-p", "http://xxx/xi/A1S/Global=e.r.t.ye",
"-p", "http://xxx/xi/A1S/Global=e.r.t.xer",
"$wsdlDir/mywsdl.wsdl"
)
)
}
however, `no.nils.wsdl2java` plugin does not work for gradle 7.*

Resources