etiennestuder/gradle-jooq-plugin

How to configure the jdbcUrl/username/password using properties from other tasks/buildservices

stackunderflow111 opened this issue · 0 comments

Hi,

I want to integrate this plugin with testcontainers so that our Jooq code is generated against a live testcontainers database. I think the best way to achieve it is to use Gradle build services. Currently, we have two build services ready.

  1. we have a postgresContainer buildservice which starts a postgres container using testcontainers. This build service provides the jdbcUrl, username and password of the started container.
  2. We also have a flywayMigratedDatabase buildservice which runs flyway migration against a database. This build service is configured to use the postgresContainer build service and it also provides the jdbcUrl, username and password of the migrated database

My question is: how to configure the generateJooq task to use the jdbcUrl, username and password from the flywayMigratedDatabase build service? It might be easy if the jooqConfiguration field is a Property, but it's not. Is there any other approach that configures the generateJooq task to use a data source from other tasks/build services?

We have tried to add a separate configuring task which looks like the following:

abstract public class ConfigureDatabaseTask extends DefaultTask {
    @Internal
    public abstract Property<String> getJdbcUrl();

    @Internal
    public abstract Property<String> getUsername();

    @Internal
    public abstract Property<String> getPassword();
}

Then, in build.gradle.kts we configure the generateJooq task in the doLast block of the ConfigureDatabaseTask and make genearteJooq depends on it.

val configureGenerateJooq = tasks.register<ConfigureDatabaseTask>("configureGenerateJooq") {
    jdbcUrl.set(flywayMigratedDatabase.map { it.jdbcUrl })
    username.set(flywayMigratedDatabase.map { it.username })
    password.set(flywayMigratedDatabase.map { it.password })
    usesService(flywayMigratedDatabase)

    doLast {
        this as ConfigureDatabaseTask
        val jdbcUrl = jdbcUrl.get()
        val username = username.get()
        val password = password.get()
        tasks.named<JooqGenerate>("generateJooq") {
            // this is the same as val jooqConfiguration = jooqGenerate.jooqConfiguration,
            // but it's a private field, so I have to bypass the access restriction using reflection
            val jooqConfigurationField = JooqGenerate::class.java.getDeclaredField("jooqConfiguration")
            jooqConfigurationField.isAccessible = true
            val jooqConfiguration = jooqConfigurationField.get(this) as Configuration

            jooqConfiguration.jdbc.apply {
                this.url = jdbcUrl
                this.user = username
                this.password = password
            }
        }
    }
}

tasks.named<JooqGenerate>("generateJooq") {
    // Make the generateJooq task dependent on the migration scripts so we get proper build caching
    // (trigger a clean rebuild only when the files change)
    inputs.files(fileTree(migrationFilesLocation))
        .withPropertyName("migrations")
        .withPathSensitivity(PathSensitivity.RELATIVE)
    // Let the task participate in incremental builds and build caching
    allInputsDeclared.set(true)
    outputs.cacheIf {
        true
    }

    dependsOn(configureGenerateJooq)
}

The problem is that this approach doesn't work well with incremental build. generateJooq is never up-to-date even when the migration files in migrationFilesLocation do not change. The reason seems to be that configureGenerateJooq make modifications to it.