Building a custom gradle distribution

This doesn’t feel like it should be an unusual scenario, but I am quite unable to find any examples as to how to achieve this, beyond a general hand-wavey indication in the documentation that this should be possible.

Use case:
We have a fair bit of Kotlin/Java services, application and libraries spread out over a number of repositories (we don’t do mono-repos), all of them built using gradle.kts.

Since all of these applications are - or should be - using the same plugins, internal repository service, built to the same quality standards (using the same test framework, code coverage tool, etc), and are published in essentially the same way (with a few variations), somewhere between 70-80 of the code in the various build.gradle.kts files is identical.

Which means that anytime something changes in our build scripts, we need to go through all of these repositories to replicate that change. How do we avoid this?

Anecdote: When we had this issue with Teamcity’s kotlin dsl, the solution was very simple - we could basically extract the build configurations into a jar, and reuse the jar everywhere by calling it’s classes. We could even dogfood the common build config jar, which was kind of awesome. :smile:

The solution to this would seem to be to build a custom gradle distribution (alternatively an init.gradle script that is applied to every project), which handles all of the setup for the project. However, the documentation for this is sparse at best, and we’re hitting our heads against a number of issues:

Firstly, this simply does not appear to be doable using the Kotlin DSL. Being a Java/Kotlin shop, we’d prefer to do this using Kotlin, but it does not seem one can apply plugins in the allproject using the Kotlin DSL.

What is the Kotlin equivalent of?

initscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath "com.github.ben-manes:gradle-versions-plugin:0.38.0"
    }
}
allprojects {
    apply plugin: com.github.benmanes.gradle.versions.VersionsPlugin
}

So we basically had to revert to groovy to try and achieve this. We had to dig into the jars to figure out quite a fair bit (unfortunate), but even so, there appear to be some plugins that are very hard to figure out/impossible? How would one go about applying the Kotlin JVM plugin, for instance?

kotlin("jvm") version "1.4.32"

The meat of the repeating code is in the configurations though. However, there is - as it seems - no way to pass parameters into the init script, which makes it impossible to configure anything that might require variation by type (e.g., apps go into a different repo than libraries).

There also seem to be a number of configurations that simply cannot be done in allprojects. For example, the following extremely basic config for maven-publish:

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
}

Hits: Could not get unknown property ‘java’ for SoftwareComponentInternal set of type org.gradle.api.internal.component.DefaultSoftwareComponentContainer

I feel that the goal should be achievable - given that we’re dealing with code: to get the build.gradle.kts pared down to a variables declaring the name and artifact ID of each project (possibly a type as well) + a dependencies block. Because that is all that is really unique from project to project.

However, at the current moment, my conclusion is that the above scenario of extracting and reusing configuration code simply can’t be resolved for anything beyond the most trivial (and thus generally irrelevant) of use cases. Or can it? If so, how?