# kotlin-gradle-plugin-template: a Kotlin-only starting point for Gradle plugins

> This GitHub template gives you a Gradle plugin project written entirely in Kotlin, with a composite build so you test the plugin against the real thing, ktlint and detekt for static analysis, GitHub Actions and publishing to the Gradle Portal already wired. It suits someone about to write their first Gradle plugin and wants the build plumbing settled for them.

**cortinico/kotlin-gradle-plugin-template** — 🐘 A template to let you started with custom Gradle Plugins + Kotlin in a few seconds

- Repository: https://github.com/cortinico/kotlin-gradle-plugin-template
- Stars: 523 · Forks: 36
- Language: Kotlin
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/cortinico-kotlin-gradle-plugin-template

## What the template gives you, and what you have to change

kotlin-gradle-plugin-template is a GitHub template for creating a Gradle plugin project using 100% Kotlin and being up and running in a few seconds.

The readme says it plainly: the template is focused on delivering a project with static analysis and continuous integration already in place. That is the pitch, and it is narrower than the usual template repositories that give you a hello world and leave everything else to you.

The feature list is nine items. It is a 100% Kotlin-only template. The plugin build setup uses a composite build. The build is 100% Gradle Kotlin DSL. Dependency versions are managed through a Gradle Versions Catalog in libs.versions.toml. There is CI setup with GitHub Actions. Static analysis is via ktlint and detekt. It is publishing-ready to the Gradle Portal. And there are issue templates for a bug report and a feature request, plus a pull request template.

Using it is a click on the Use this template button, which creates a new repository starting from this one. Then there are two things to update: the gradle.properties file in the plugin-build folder, and the plugin usages, which means searching the repository for the placeholder group and package and replacing it with your own ID.

That second step is the one worth understanding, because it is not a rename. The readme warns that the project relies on module name and group for dependency substitution to work properly, and that if you change only the plugin ID everything will work as expected, but if you change the module name or group, things might break and you probably have to specify a substitution rule. The plugin ID is the public identity of your plugin, the coordinate consumers type. The module name and group are the internal coordinates of the build, and the composite build's substitution mechanism matches the two together. Change the public identity freely; change the internal coordinates and you are editing the mechanism that makes the composite build work.

The primary language is Kotlin, the licence is MIT, the default branch is main, and the last push was on 2026-10-20. There are no GitHub releases, since it is a template rather than a library, and the topic list includes github-template, gradle-plugin-development, kotlin-dsl and hacktoberfest.

## The composite build, and the preMerge task that ties it together

The composite build is the technical heart of the template, and the readme explains the problem it solves in one sentence.

This template uses a Gradle composite build to build, test and publish the plugin, which means you do not need to run Gradle twice to test the changes on your Gradle plugin, with no more publishToMavenLocal tricks or so.

That is the pain point. The conventional way to develop a Gradle plugin is to publish it to your local Maven repository and then have a sample project depend on that snapshot. It works, but it is slow, it leaves a local repository in a state that can go stale, and the two builds can disagree about what you are testing.

With a composite build, the included build is inside the plugin-build folder and the top-level build substitutes the plugin's module with the included build automatically. What you test is the code you just wrote.

The template adds a preMerge task on the top level build, which runs all the check tasks both in the top level and in the included build.

```bash
./gradlew preMerge
```

And if you need to invoke a task inside the included build specifically, you point the wrapper at that build directory.

```bash
./gradlew -p plugin-build <task-name>
```

The preMerge task is the one to put in front of a pull request, and the reason it exists in the template rather than in each project is that getting the included-build invocation right is fiddly, and a task that runs both sides correctly is exactly the sort of thing a template should hand you.

The repository layout matches: there is a top-level build.gradle.kts and settings.gradle.kts, a plugin-build/ folder for the included build, an example/ directory with its own build.gradle.kts as the sample consumer, a config/ directory for the detekt configuration, a gradle/ directory for the wrapper and the version catalog, gradle.properties at the top level, and renovate.json for dependency updates.

## Static analysis, and the two tools doing different jobs

Two analysis tools are configured, and they do different things.

ktlint is used with the ktlint-gradle plugin to format your code. To reformat all the source code as well as the buildscript you run the ktlintFormat gradle task. So ktlint is the formatter, and the convention is that formatting is not a review comment but a command.

detekt is used to analyze the source code, with the configuration stored in config/detekt/detekt.yml, and the readme notes that the file was generated with the detektGenerateConfig task. That detail is useful in practice, because the generated file is the full rule set with all defaults listed, and editing it to disable or tune rules is a deliberate act rather than a hunt through documentation.

What static analysis buys a plugin project specifically is worth spelling out. A Gradle plugin is code that runs inside other people's builds, so a mistake in it fails at a moment when the user has no context for it. A build that refuses to compile because of a style violation, or an analysis run that catches an obvious error before publish, is a much better experience than a plugin that fails in a consumer's build with an unrelated-looking stack trace.

The combination of ktlint and detekt covers the two usual categories. ktlint handles the mechanical formatting that reviewers otherwise argue about in every pull request, and detekt handles the semantic checks, from complexity to potential bugs, that a formatter cannot express.

The other half of the guard rail is the CI, and the readme is explicit that you do not need to set up any external service and should have a running CI once you start using the template. Three workflows are listed. Validate Gradle Wrapper checks that the gradle wrapper has a valid checksum, which is a supply-chain check on the build itself. Pre Merge Checks runs the preMerge tasks as well as trying to run the Gradle plugin, so the CI exercises the actual plugin behaviour and not only the compilation. And Publish to Plugin Portal runs the publishPlugin task when a new tag is pushed.

## Publishing to the Gradle Portal

The template is ready to publish to the Gradle Portal, and the mechanism is a GitHub Action that runs on a tag push.

The readme notes that you need to configure two secrets, GRADLE_PUBLISH_KEY and GRADLE_PUBLISH_SECRET, with the credentials you can get from your profile on the Gradle Portal. The workflow is named Publish Plugin to Portal and it will take care of the publishing whenever you push a tag.

So the publishing path is a tag push rather than a manual command, which is the right shape for it: a tag is a release decision, and the action turns that decision into an artefact on the portal. It also means the credentials live as repository secrets rather than in a gradle.properties file, which is the difference between a publish key that is available to anyone with read access and one that is not.

One detail from the feature list is worth connecting to the earlier section. The template is 100% Kotlin, with the build files written using the Gradle Kotlin DSL and the Plugin DSL, and dependencies centralized inside the libs.versions.toml file. A minimalistic Gradle plugin is already provided in Kotlin so you can easily start developing your own around it.

That last part is the practical starting point. Rather than an empty plugin project, you get a working plugin that compiles, passes ktlint and detekt, is exercised by the CI, and is publishable. You replace its behaviour, and the machinery around it keeps working.

The two wrapper scripts gradlew and gradlew.bat are at the top level, and the Validate Gradle Wrapper workflow exists to check that the wrapper you ship has a valid checksum. That check matters more than it sounds, because the Gradle wrapper is a jar that downloads and executes a distribution, and it is a well-known target for supply chain attacks against build systems.

## The licence terms, and when to start from something else

Two practical points about using this template that are easy to skim past.

The first is the licence. The template is licensed under the MIT License, and the readme adds a note that the generated template is offering to start with a MIT license but you can change it to whatever you wish, as long as you attribute under the MIT terms that you are using the template. So the MIT terms apply to the template itself, and your project can carry a different licence provided you keep the attribution for the part that came from here. That is a different arrangement from a licence that propagates, and it means you can ship a plugin under a proprietary or copyleft licence without inheriting a viral obligation from the scaffolding.

The second is where this template is not the right answer.

If you need a specific Kotlin version, a specific Gradle version, a specific plugin ID or a specific package coordinate, you are going to change them immediately, and each one is a place to get something subtly wrong. The gradle.properties file in the plugin-build folder is where the plugin's own coordinates and version live, so that is the first file to open.

If your project is a plain library rather than a Gradle plugin, the composite build and the publishing workflow are irrelevant weight, and a plain Kotlin library template is a better starting point.

And if you are on a team that has strong opinions about static analysis configuration, the detekt.yml file is generated with all the default rules listed, which means you will be turning rules off. That is normal and expected, and it is also a real decision rather than a default.

The renovate.json at the top level is worth a note too, since it is the least expected file in a template. Automated dependency updates accumulate quickly in a build, and having it configured from the start means the version catalog gets updated on a schedule rather than in a panic the first time a dependency publishes a breaking release.

## Conclusion

kotlin-gradle-plugin-template fits a developer starting a Gradle plugin who wants the composite build, static analysis, CI and publishing already working, and it does not fit someone who needs a specific Gradle version, Kotlin version or plugin ID from the start, since those are the values you have to change yourself. Before generating a project, note the dependency substitution constraint, because the project relies on module name and group for it to work, and renaming the module or group will break the build unless you also write a substitution rule.

## FAQ

### What does the kotlin-gradle-plugin-template give me?

A Gradle plugin project written entirely in Kotlin, with a composite build so you test the plugin directly, 100% Gradle Kotlin DSL, a versions catalog for dependency versions, ktlint and detekt for static analysis, GitHub Actions CI, and publishing to the Gradle Portal already wired up, plus issue and pull request templates.

### How do I run the checks in the kotlin-gradle-plugin-template project?

Run ./gradlew preMerge, which is a task on the top level build that runs all the check tasks in both the top level and the included build. To run a task inside the included build specifically, use ./gradlew -p plugin-build with the task name.

### What do I have to change after generating a project from the template?

Update gradle.properties in the plugin-build folder, and replace the placeholder plugin group and package throughout the repository with your own ID. The readme warns that changing the module name or group, as opposed to the plugin ID, can break dependency substitution and may require a substitution rule.

### How does publishing to the Gradle Portal work in this template?

A Publish Plugin to Portal GitHub Action runs the publishPlugin task whenever you push a tag. You need to configure two repository secrets, GRADLE_PUBLISH_KEY and GRADLE_PUBLISH_SECRET, using the credentials from your profile on the Gradle Portal.

## Sources

- [cortinico/kotlin-gradle-plugin-template on GitHub](https://github.com/cortinico/kotlin-gradle-plugin-template)
- [Issues](https://github.com/cortinico/kotlin-gradle-plugin-template/issues)
- [License: MIT](https://github.com/cortinico/kotlin-gradle-plugin-template/blob/main/LICENSE)
- [README](https://github.com/cortinico/kotlin-gradle-plugin-template/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cortinico-kotlin-gradle-plugin-template
