Loading...
Loading...
Load when porting, converting, or reimplementing a single Gradle plugin as a Kotlin Toolchain local plugin, or when mapping Gradle plugin concepts (Task, Extension, project.version, dependsOn, -P properties, afterEvaluate) to Toolchain analogs. Skip for migrating a whole Gradle project or authoring a plugin from scratch.
npx skill4agent add kotlin/kotlin-agent-skills kotlin-tooling-gradle-to-kotlin-toolchain-pluginkotlin-tooling-kotlin-toolchain-plugin-authoringmodule.yamldocs/README.mdmyPlugin { ... }prepostfileUpdatecommitpush-Pfoo.barmodule.yamlkotlin-tooling-kotlin-toolchainkotlin initkotlinkotlin.batproject.yamlplugins/<name>/module.yamlplugin.yaml# demo-app/module.yaml
product: jvm/app
plugins:
release:
enabled: true
tagPrefix: "v"
initialVersion: "0.1.0"
releaseBranchPattern: "main|master"
settings:
jvm:
mainClass: com.example.demo.MainKt
jdk:
version: 21
kotlin:
languageVersion: 2.1// demo-app/src/main.kt
package com.example.demo
private const val VERSION_RESOURCE = "/META-INF/release/version.txt"
fun main() {
val version = readVersionFromClasspath() ?: "(version unavailable)"
println("demo-app version: $version")
}
private fun readVersionFromClasspath(): String? =
object {}.javaClass.getResourceAsStream(VERSION_RESOURCE)
?.bufferedReader()
?.use { it.readText().trim() }
?.takeIf { it.isNotEmpty() }./kotlin run :demo-app # => demo-app version: 0.1.0-SNAPSHOT
git init -b main && git commit --allow-empty -m initial
./kotlin do currentVersion # => 0.1.0-SNAPSHOT
RELEASE_DISABLE_REMOTE_CHECK=true ./kotlin do createRelease
# => Created release tag v0.1.0
./kotlin do currentVersion # => 0.1.0
git commit --allow-empty -m next
./kotlin do currentVersion # => 0.1.1-SNAPSHOT
RELEASE_FORCE_VERSION=2.0.0 ./kotlin do currentVersion
# => 2.0.0| Gradle concept | Kotlin Toolchain analog | Notes |
|---|---|---|
| | One module per plugin; no |
| Top-level | One per file in |
| | Defaults in interface getters; nested blocks → nested |
| | The DAG is inferred from path matching. |
| A task writing | No project-wide shared state; the filesystem is the channel. |
| | No |
| App reading the version at runtime | | Same file serves build-time and runtime consumers. |
| Generated Kotlin source | | Prefer resources when the value is only read at runtime. |
| Public task name users invoke | Entry in | Tasks are internal; commands are the API. Build-graph contributors stay out. |
| No analog — settings are static | Compute derived values in the action body. |
Groovy/Kotlin DSL hooks ( | New | No closure-based extension point. |
| | Same coordinates; |
Custom task types in | A | Local-only; no Maven publishing yet. |
| | Directory is created for you. |
| | Snapshotted for execution avoidance. |
| | For Git/network/env inputs. |
| No analog | Each task pulls from the plugin module's dependency list; the build graph isn't introspectable; use |
project.yamlmodules:
- demo-app
- plugins/release
plugins:
- ./plugins/releaseplugins:plugins: { release: enabled }SettingsmyPlugin { ... }@Configurable@Configurablepackage com.example.release
import org.jetbrains.amper.plugins.Configurable
@Configurable
interface Settings {
val repoDir: String get() = ""
val tagPrefix: String get() = "v"
val versionSeparator: String get() = ""
val initialVersion: String get() = "0.1.0"
val ignoreUncommittedChanges: Boolean get() = false
val releaseBranchPattern: String get() = "main|master"
val checks: ChecksSettings
}
@Configurable
interface ChecksSettings {
val uncommittedChanges: Boolean get() = true
val aheadOfRemote: Boolean get() = true
val snapshotDependencies: Boolean get() = true
}module.yamlpluginInfo.idplugins:
release:
enabled: true
tagPrefix: "v"
initialVersion: "0.1.0"
ignoreUncommittedChanges: false
checks:
aheadOfRemote: true@TaskActionTaskfunsrc/tasks/Path@Input@Outputplugin.yamlpackage com.example.release.tasks
import com.example.release.Settings
import com.example.release.git.GitRepo
import com.example.release.version.VersionPipeline
import org.jetbrains.amper.plugins.Input
import org.jetbrains.amper.plugins.TaskAction
import java.nio.file.Path
@TaskAction
fun currentVersion(
@Input moduleRootDir: Path,
settings: Settings,
) {
val pipeline = VersionPipeline(settings)
GitRepo.open(moduleRootDir, settings.repoDir).use { repo ->
println(pipeline.infer(repo).version)
}
}plugin.yamlaction:@TaskAction${module.rootDir}${taskOutputDir}${pluginSettings}${tasks.<task>.action.<param>}tasks:
currentVersion:
action: !com.example.release.tasks.currentVersion
moduleRootDir: ${module.rootDir}
settings: ${pluginSettings}
writeVersion:
action: !com.example.release.tasks.writeVersion
moduleRootDir: ${module.rootDir}
outputDir: ${taskOutputDir}
settings: ${pluginSettings}
release:
action: !com.example.release.tasks.release
moduleRootDir: ${module.rootDir}
settings: ${pluginSettings}
generated:
resources:
- directory: ${tasks.writeVersion.action.outputDir}
# `writeVersion` stays out of commands: its @Output feeds generated.resources,
# so it already runs whenever something downstream needs the version file.
commands:
- currentVersion
- releaseSettingssettings: ${pluginSettings}nullproject.version@TaskActionversion.txt@Output@Inputgenerated.resources-PSystem.getenv()class VersionPipeline(
private val settings: Settings,
private val env: Map<String, String?> = System.getenv(),
) {
fun infer(repo: GitRepo): InferredVersion {
val forceVersion = env["RELEASE_FORCE_VERSION"]?.takeIf { it.isNotBlank() }
val forceSnapshot = env["RELEASE_FORCE_SNAPSHOT"].asBoolean()
// ...
}
}
private fun String?.asBoolean(): Boolean =
this != null && this.equals("true", ignoreCase = true)<PLUGINID>_<UPPERCASE>-Prelease.forceVersion=X → RELEASE_FORCE_VERSION=X
-Prelease.forceSnapshot → RELEASE_FORCE_SNAPSHOT=true
-Prelease.disableChecks → RELEASE_DISABLE_CHECKS=true
-Prelease.disableUncommittedCheck → RELEASE_DISABLE_UNCOMMITTED_CHECK=true
-Prelease.disableRemoteCheck → RELEASE_DISABLE_REMOTE_CHECK=true
-Prelease.overriddenBranchName=X → RELEASE_OVERRIDDEN_BRANCH_NAME=Xmodule.yaml-P