diff --git a/build.gradle.kts b/build.gradle.kts index 98b7d4ae..bc858bb1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,7 @@ buildscript { classpath(libs.ksp.gradle.plugin) classpath(libs.tapmoc.at.build.time) classpath(libs.serialization.gradle.plugin) + classpath(libs.compose.compiler) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e0f2d40c..0d332674 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,11 +1,14 @@ [versions] -kgp = "2.3.0-RC" +kgp = "2.3.0" nmcp = "1.3.0" +mosaic = "0.18.0" + [libraries] agp = "com.android.tools.build:gradle:8.2.0" bcv = "org.jetbrains.kotlinx:binary-compatibility-validator:0.16.3" bouncycastle-pg = "org.bouncycastle:bcpg-jdk18on:1.78.1" bouncycastle-prov = "org.bouncycastle:bcprov-jdk18on:1.78.1" +compose-compiler = { module = "org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin", version.ref = "kgp" } clikt = "com.github.ajalt.clikt:clikt:3.5.0" tapmoc = "com.gradleup.tapmoc:tapmoc-gradle-plugin:0.3.2" tapmoc-at-build-time = "com.gradleup.tapmoc:tapmoc-gradle-plugin:0.2.1-SNAPSHOT-dda1362c1d0f8389f50b362a08967793e320cf29" @@ -25,6 +28,7 @@ librarian-gradle-plugin = "com.gradleup.librarian:librarian-gradle-plugin:0.2.2- maven-sympathy = "com.gradleup.maven-sympathy:maven-sympathy:0.0.2" mockserver = "com.apollographql.mockserver:apollo-mockserver:0.0.3" mordant = "com.github.ajalt.mordant:mordant:2.7.1" +mosaic-runtime = { module = "com.jakewharton.mosaic:mosaic-runtime", version.ref = "mosaic" } nmcp = { module = "com.gradleup.nmcp:com.gradleup.nmcp.gradle.plugin", version.ref = "nmcp" } nmcp-tasks = { module = "com.gradleup.nmcp:nmcp-tasks", version.ref = "nmcp" } nmcp-build-time = "com.gradleup.nmcp:com.gradleup.nmcp.gradle.plugin:1.2.0-SNAPSHOT-b2b859946a8e9938f7b7f22003e9dc029aac69af" @@ -32,3 +36,4 @@ okhttp = "com.squareup.okhttp3:okhttp:4.12.0" serialization-gradle-plugin = {module = "org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin", version.ref = "kgp"} serialization-json = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0" xmlutil = "io.github.pdvrieze.xmlutil:serialization:0.91.1" +semver = "net.swiftzer.semver:semver:2.1.0" diff --git a/librarian-cli/api/librarian-cli.api b/librarian-cli/api/librarian-cli.api index 045634a8..794649e6 100644 --- a/librarian-cli/api/librarian-cli.api +++ b/librarian-cli/api/librarian-cli.api @@ -3,6 +3,7 @@ public final class com/gradleup/librarian/cli/MainKt { } public final class com/gradleup/librarian/cli/MultilineSecretComponent : com/github/kinquirer/core/Component { + public static final field $stable I public fun (Ljava/lang/String;)V public fun isInteracting ()Z public fun onEvent (Lcom/github/kinquirer/core/KInquirerEvent;)V @@ -12,6 +13,7 @@ public final class com/gradleup/librarian/cli/MultilineSecretComponent : com/git } public final class com/gradleup/librarian/cli/Update_repoKt { + public static final fun main ([Ljava/lang/String;)V public static final fun updateRepo ([Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V } @@ -24,11 +26,13 @@ public final class com/gradleup/librarian/cli/VersionKt { } public final class com/gradleup/librarian/cli/command/CreateGitHubRepository : com/github/ajalt/clikt/core/CliktCommand { + public static final field $stable I public fun ()V public fun run ()V } public final class com/gradleup/librarian/cli/command/FileContext { + public static final field $stable I public fun ()V public final fun replaceAllArtifactVersions (Ljava/lang/String;)V public final fun replaceArtifactVersion (Ljava/lang/String;)V @@ -37,17 +41,20 @@ public final class com/gradleup/librarian/cli/command/FileContext { } public final class com/gradleup/librarian/cli/command/GenerateKey : com/github/ajalt/clikt/core/CliktCommand { + public static final field $stable I public fun ()V public fun run ()V } public final class com/gradleup/librarian/cli/command/UploadKey : com/github/ajalt/clikt/core/CliktCommand { + public static final field $stable I public fun ()V public final fun getKeyFile ()Ljava/lang/String; public fun run ()V } public final class com/gradleup/librarian/cli/command/VersionContext { + public static final field $stable I public fun (Ljava/lang/String;)V public final fun file (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V public final fun getVersion ()Ljava/lang/String; @@ -58,7 +65,40 @@ public final class com/gradleup/librarian/cli/command/init/GitHubSecretsKt { } public final class com/gradleup/librarian/cli/command/init/GradleWrapperCommand : com/github/ajalt/clikt/core/CliktCommand { + public static final field $stable I public fun ()V public fun run ()V } +public final class com/gradleup/librarian/cli/component/Choice { + public static final field $stable I + public fun (Ljava/lang/String;Ljava/lang/Object;)V + public final fun getData ()Ljava/lang/Object; + public final fun getText ()Ljava/lang/String; +} + +public final class com/gradleup/librarian/cli/component/ListInputKt { + public static final fun ListInput (Ljava/lang/String;Ljava/util/List;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;I)V + public static final fun ListInput2 (Ljava/lang/String;Ljava/util/List;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/gradleup/librarian/cli/component/TextInputKt { + public static final fun TextInput-ObWx_Vo (Ljava/lang/String;Lcom/jakewharton/mosaic/modifier/Modifier;ILkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/gradleup/librarian/cli/screen/AskVersionKt { + public static final fun AskVersion (Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/gradleup/librarian/cli/screen/MainScreenKt { + public static final fun MainScreen (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/gradleup/librarian/cli/screen/PrepareNextVersionScreenKt { + public static final fun CommitChangesScreen (Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/gradleup/librarian/cli/screen/SetVersionScreenKt { + public static final fun SetVersionScreen (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V +} + diff --git a/librarian-cli/build.gradle.kts b/librarian-cli/build.gradle.kts index 67d5dbef..c0147185 100644 --- a/librarian-cli/build.gradle.kts +++ b/librarian-cli/build.gradle.kts @@ -4,6 +4,7 @@ plugins { id("java") id("org.jetbrains.kotlin.jvm") id("distribution") + id("org.jetbrains.kotlin.plugin.compose") } Librarian.module(project) @@ -13,6 +14,7 @@ dependencies { api(libs.clikt) implementation(libs.inquirer) implementation(libs.mordant) + implementation(libs.mosaic.runtime) implementation(libs.jansi) testImplementation(kotlin("test")) diff --git a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/command/TagAndBump.kt b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/command/TagAndBump.kt index d1191dd5..3cf5c7f1 100644 --- a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/command/TagAndBump.kt +++ b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/command/TagAndBump.kt @@ -5,14 +5,14 @@ import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.optional import com.github.kinquirer.KInquirer import com.github.kinquirer.components.promptConfirm -import com.gradleup.librarian.core.tooling.Version +import com.gradleup.librarian.core.tooling.SemVer import com.gradleup.librarian.core.tooling.next import com.gradleup.librarian.core.tooling.getCurrentVersion import com.gradleup.librarian.core.tooling.nextMinor import com.gradleup.librarian.core.tooling.nextPatch import com.gradleup.librarian.core.tooling.runCommand import com.gradleup.librarian.core.tooling.tagAndBump -import com.gradleup.librarian.core.tooling.toVersionOrNull +import com.gradleup.librarian.core.tooling.semVerOrNull import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract import kotlin.io.path.Path @@ -36,7 +36,7 @@ internal fun exit(message: String): Nothing { internal abstract class AbstractTagAndBump : CliktCommand() { val versionToRelease by argument().optional() - abstract fun run(versionToRelease: Version) + abstract fun run(versionToRelease: SemVer) private fun confirmOrExit(message: String) { if (!KInquirer.promptConfirm(message, true)) { @@ -46,7 +46,7 @@ internal abstract class AbstractTagAndBump : CliktCommand() { override fun run() { val currentVersion = getCurrentVersion() - val currentVersionParsed = currentVersion.toVersionOrNull() + val currentVersionParsed = currentVersion.semVerOrNull() checkOrExit(currentVersionParsed != null) { "Cannot parse current version: '${currentVersion}'" } @@ -67,7 +67,7 @@ internal abstract class AbstractTagAndBump : CliktCommand() { versionToRelease = currentVersionParsed.nextMinor().toString() } - val tagVersionParsed = versionToRelease.toVersionOrNull() + val tagVersionParsed = versionToRelease.semVerOrNull() checkOrExit(tagVersionParsed != null) { "Version '$versionToRelease' cannot be parsed (expected 'major.minor.patch[-prerelease.version][-SNAPSHOT]')" } @@ -82,13 +82,13 @@ internal abstract class AbstractTagAndBump : CliktCommand() { } internal class TriggerTagAndBump : AbstractTagAndBump() { - override fun run(versionToRelease: Version) { + override fun run(versionToRelease: SemVer) { Path(".").runCommand("gh", "workflow", "run", "tag-and-bump.yaml", "-f", "versionToRelease=$versionToRelease") } } internal class TagAndBump : AbstractTagAndBump() { - override fun run(versionToRelease: Version) { + override fun run(versionToRelease: SemVer) { tagAndBump(versionToRelease) } } \ No newline at end of file diff --git a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/component/ListInput.kt b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/component/ListInput.kt new file mode 100644 index 00000000..41befcaf --- /dev/null +++ b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/component/ListInput.kt @@ -0,0 +1,61 @@ +package com.gradleup.librarian.cli.component + +import androidx.compose.runtime.Composable +import com.jakewharton.mosaic.layout.onKeyEvent +import com.jakewharton.mosaic.modifier.Modifier +import com.jakewharton.mosaic.ui.Column +import com.jakewharton.mosaic.ui.Text +import com.jakewharton.mosaic.ui.TextStyle + +class Choice(val text: String, val data: D) + +@Composable +fun ListInput( + question: String, + choices: List>, + index: Int, + onHighlight: (Int) -> Unit, + onChoice: (Choice) -> Unit +) { + var index = index + Column(modifier = Modifier.onKeyEvent { event -> + when (event.key) { + "ArrowUp" -> index -= 1 + "ArrowDown" -> index += 1 + "Enter" -> onChoice(choices.get(index)) + else -> return@onKeyEvent false + } + + index = index.coerceIn(0, choices.lastIndex) + + onHighlight(index) + true + }) { + Text(question) + Text("") + choices.forEachIndexed { i, choice -> + Text( + "${if (index == i) ">" else " "} ${choice.text}", + textStyle = if (index == i) TextStyle.Bold else TextStyle.Unspecified + ) + } + } +} + +@Composable +@JvmName("ListInput2") +fun ListInput( + question: String, + choices: List, + index: Int, + onHighlight: (Int) -> Unit, + onChoice: (Int) -> Unit +) { + ListInput( + question = question, + choices = choices.mapIndexed { index, string -> Choice(string, index) }, + index = index, + onHighlight = onHighlight, + onChoice = { onChoice(it.data) } + ) +} \ No newline at end of file diff --git a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/component/TextInput.kt b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/component/TextInput.kt new file mode 100644 index 00000000..9340ecd4 --- /dev/null +++ b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/component/TextInput.kt @@ -0,0 +1,32 @@ +package com.gradleup.librarian.cli.component + +import androidx.compose.runtime.Composable +import com.jakewharton.mosaic.layout.KeyEvent +import com.jakewharton.mosaic.layout.onKeyEvent +import com.jakewharton.mosaic.modifier.Modifier +import com.jakewharton.mosaic.ui.Text +import com.jakewharton.mosaic.ui.TextStyle + +@Composable +fun TextInput( + value: String, + modifier: Modifier = Modifier, + textStyle: TextStyle = TextStyle.Unspecified, + onEnter: (() -> Unit) = {}, + onTextChanged: ((String) -> Unit), +) { + Text( + value = value, + textStyle = textStyle, + modifier = modifier.onKeyEvent { event -> + when { + event == KeyEvent("c", ctrl = true) -> return@onKeyEvent false + event.key.toCharArray().singleOrNull() != null -> onTextChanged(value + event.key) + event == KeyEvent("Backspace") -> onTextChanged(value.dropLast(1)) + event == KeyEvent("Enter") -> onEnter() + else -> return@onKeyEvent false + } + true + }, + ) +} diff --git a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/AskVersion.kt b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/AskVersion.kt new file mode 100644 index 00000000..24de2fd8 --- /dev/null +++ b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/AskVersion.kt @@ -0,0 +1,32 @@ +package com.gradleup.librarian.cli.screen + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import com.gradleup.librarian.cli.command.setCurrentVersion +import com.gradleup.librarian.cli.component.ListInput +import com.gradleup.librarian.cli.component.TextInput +import com.gradleup.librarian.core.tooling.getCurrentVersion +import com.jakewharton.mosaic.ui.Column +import com.jakewharton.mosaic.ui.Row +import com.jakewharton.mosaic.ui.Text + +@Composable +fun AskVersion( + onVersionSet: () -> Unit, +) { + Row { + val text = remember { mutableStateOf(getCurrentVersion()) } + Text("What version should be set? ") + TextInput( + value = text.value, + onEnter = { + setCurrentVersion(text.value) + onVersionSet() + }, + onTextChanged = { + text.value = it + }, + ) + } +} \ No newline at end of file diff --git a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/MainScreen.kt b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/MainScreen.kt new file mode 100644 index 00000000..d45b5686 --- /dev/null +++ b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/MainScreen.kt @@ -0,0 +1,30 @@ +package com.gradleup.librarian.cli.screen + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import com.gradleup.librarian.cli.component.ListInput + +@Composable +fun MainScreen( + onSetVersion: () -> Unit, + onPrepareNextVersion: () -> Unit, +) { + val indexState = remember { mutableStateOf(0) } + + ListInput( + question = "What do you want to do?", + choices = listOf( + "Set version.", + "Prepare next version." + ), + index = indexState.value, + onHighlight = { indexState.value = it }, + onChoice = { + when (it) { + 0 -> onSetVersion() + 1 -> onPrepareNextVersion() + } + }, + ) +} \ No newline at end of file diff --git a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/PrepareNextVersionScreen.kt b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/PrepareNextVersionScreen.kt new file mode 100644 index 00000000..c48818dc --- /dev/null +++ b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/PrepareNextVersionScreen.kt @@ -0,0 +1,31 @@ +package com.gradleup.librarian.cli.screen + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import com.gradleup.librarian.cli.command.getCurrentVersion +import com.gradleup.librarian.cli.component.ListInput +import com.gradleup.librarian.core.tooling.runCommand + +@Composable +fun CommitChangesScreen( + onDone: () -> Unit, +) { + + val indexState = remember { mutableStateOf(0) } + ListInput( + question = "Commit changes?", + choices = listOf("yes", "no"), + index = indexState.value, + onHighlight = { indexState.value = it}, + onChoice = { + if (it == 0) { + val currentVersion = getCurrentVersion() + runCommand("git", "checkout", "-b", "bump-to-$currentVersion") + runCommand("git", "commit", "-a", "-m", "Bump version to $currentVersion") + } + + onDone() + }, + ) +} \ No newline at end of file diff --git a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/SetVersionScreen.kt b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/SetVersionScreen.kt new file mode 100644 index 00000000..30ff369f --- /dev/null +++ b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/screen/SetVersionScreen.kt @@ -0,0 +1,61 @@ +package com.gradleup.librarian.cli.screen + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import com.gradleup.librarian.cli.command.dropSnapshot +import com.gradleup.librarian.cli.command.getCurrentVersion +import com.gradleup.librarian.cli.command.getNextSnapshot +import com.gradleup.librarian.cli.command.setCurrentVersion +import com.gradleup.librarian.cli.component.Choice +import com.gradleup.librarian.cli.component.ListInput +import com.gradleup.librarian.core.tooling.SemVer +import com.gradleup.librarian.core.tooling.bump +import com.gradleup.librarian.core.tooling.nextMajor +import com.gradleup.librarian.core.tooling.nextMinor +import com.gradleup.librarian.core.tooling.nextPatch +import com.gradleup.librarian.core.tooling.nextPrerelease +import com.gradleup.librarian.core.tooling.semVerOrNull + +@Composable +fun SetVersionScreen( + onVersionSet: () -> Unit, + onOtherVersion: () -> Unit, +) { + val indexState = remember { mutableStateOf(0) } + + val choices = remember { + val currentVersion = getCurrentVersion().semVerOrNull() + check(currentVersion != null) { + "Cannot parse version '$currentVersion'" + } + check(currentVersion.isSnapshot) { + "Current version '$currentVersion' does not ends with '-SNAPSHOT'. Call set-version to update it." + } + + buildList { + if (currentVersion.preRelease != null) { + add(Choice("Bump prerelease (${currentVersion.nextPrerelease()}", currentVersion.nextPrerelease())) + } + add(Choice("Bump patch to ${currentVersion.nextPatch()}", currentVersion.nextPatch())) + add(Choice("Bump minor to ${currentVersion.nextMinor()}", currentVersion.nextMinor())) + add(Choice("Bump major to ${currentVersion.nextMajor()}", currentVersion.nextMajor())) + add(Choice("Other?", null)) + } + } + ListInput( + question = "Version is currently '${getCurrentVersion()}', what do you want to set the version to?", + choices = choices, + index = indexState.value, + onHighlight = { indexState.value = it }, + onChoice = { + when (it.data) { + null -> onOtherVersion() + else -> { + setCurrentVersion(it.data.toString()) + onVersionSet() + } + } + }, + ) +} \ No newline at end of file diff --git a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/update-repo.kt b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/update-repo.kt index 12e78f69..ad58bd4d 100644 --- a/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/update-repo.kt +++ b/librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/update-repo.kt @@ -1,17 +1,83 @@ package com.gradleup.librarian.cli +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.subcommands import com.gradleup.librarian.cli.command.PrepareNextVersion import com.gradleup.librarian.cli.command.SetVersion import com.gradleup.librarian.cli.command.VersionContext +import com.gradleup.librarian.cli.command.setCurrentVersion +import com.gradleup.librarian.cli.screen.AskVersion +import com.gradleup.librarian.cli.screen.CommitChangesScreen +import com.gradleup.librarian.cli.screen.MainScreen +import com.gradleup.librarian.cli.screen.SetVersionScreen +import com.gradleup.librarian.core.tooling.bump +import com.gradleup.librarian.core.tooling.copy +import com.gradleup.librarian.core.tooling.getCurrentVersion +import com.gradleup.librarian.core.tooling.semVerOrNull +import com.jakewharton.mosaic.runMosaicBlocking +import kotlinx.coroutines.CompletableDeferred -private class MainCommand : CliktCommand() { - override fun run() { +private class MainCommand(val setVersionInDocs: VersionContext.() -> Unit) : CliktCommand(invokeWithoutSubcommand = true) { + override fun run() { + runMosaicBlocking { + val exit = remember { CompletableDeferred() } + LaunchedEffect(Unit) { + exit.await() + } + + val state = remember { mutableStateOf(State.Main) } + + when (state.value) { + State.Main -> MainScreen( + onSetVersion = { state.value = State.SetVersion }, + onPrepareNextVersion = { state.value = State.PrepareNextVersion } + ) + + State.SetVersion -> SetVersionScreen( + onVersionSet = { state.value = State.CommitChanges }, + onOtherVersion = { state.value = State.AskVersion } + ) + State.AskVersion -> AskVersion( + onVersionSet = { state.value = State.CommitChanges } + ) + State.PrepareNextVersion -> { + val version = getCurrentVersion().semVerOrNull() + check (version != null) { + "Cannot parse version '${getCurrentVersion()}'" + } + check(version.isSnapshot) { + "Current version '$version' does not ends with '-SNAPSHOT'. Call set-version to update it." + } + val releaseVersion = version.copy(isSnapshot = false) + VersionContext(releaseVersion.toString()).setVersionInDocs() + setCurrentVersion(version.bump().toString()) + state.value = State.CommitChanges + } + State.CommitChanges -> CommitChangesScreen { + exit.complete(Unit) + } + } } + } } +private enum class State { + Main, + SetVersion, + AskVersion, + PrepareNextVersion, + CommitChanges +} + + fun updateRepo(args: Array, setVersionInDocs: VersionContext.() -> Unit) { - MainCommand().subcommands(SetVersion(), PrepareNextVersion(setVersionInDocs)).main(args) + MainCommand(setVersionInDocs).subcommands(SetVersion(), PrepareNextVersion(setVersionInDocs)).main(args) } + +fun main(args: Array) { + updateRepo(args) {} +} \ No newline at end of file diff --git a/librarian-core/api/librarian-core.api b/librarian-core/api/librarian-core.api index 799db81f..0c6ff1d0 100644 --- a/librarian-core/api/librarian-core.api +++ b/librarian-core/api/librarian-core.api @@ -78,20 +78,35 @@ public final class com/gradleup/librarian/core/tooling/SecretKeyRing { public final fun secretKey ()[B } +public final class com/gradleup/librarian/core/tooling/SemVer { + public fun (IIILcom/gradleup/librarian/core/tooling/PreRelease;Z)V + public final fun compareTo (Lcom/gradleup/librarian/core/tooling/SemVer;)I + public final fun getMajor ()I + public final fun getMinor ()I + public final fun getPatch ()I + public final fun getPreRelease ()Lcom/gradleup/librarian/core/tooling/PreRelease; + public final fun isSnapshot ()Z + public fun toString ()Ljava/lang/String; +} + public final class com/gradleup/librarian/core/tooling/SemverKt { + public static final fun bump (Lcom/gradleup/librarian/core/tooling/SemVer;)Lcom/gradleup/librarian/core/tooling/SemVer; public static final fun copy (Lcom/gradleup/librarian/core/tooling/PreRelease;Ljava/lang/String;I)Lcom/gradleup/librarian/core/tooling/PreRelease; - public static final fun copy (Lcom/gradleup/librarian/core/tooling/Version;IIILcom/gradleup/librarian/core/tooling/PreRelease;Z)Lcom/gradleup/librarian/core/tooling/Version; + public static final fun copy (Lcom/gradleup/librarian/core/tooling/SemVer;IIILcom/gradleup/librarian/core/tooling/PreRelease;Z)Lcom/gradleup/librarian/core/tooling/SemVer; public static synthetic fun copy$default (Lcom/gradleup/librarian/core/tooling/PreRelease;Ljava/lang/String;IILjava/lang/Object;)Lcom/gradleup/librarian/core/tooling/PreRelease; - public static synthetic fun copy$default (Lcom/gradleup/librarian/core/tooling/Version;IIILcom/gradleup/librarian/core/tooling/PreRelease;ZILjava/lang/Object;)Lcom/gradleup/librarian/core/tooling/Version; - public static final fun next (Lcom/gradleup/librarian/core/tooling/Version;)Lcom/gradleup/librarian/core/tooling/Version; - public static final fun nextMinor (Lcom/gradleup/librarian/core/tooling/Version;)Lcom/gradleup/librarian/core/tooling/Version; - public static final fun nextPatch (Lcom/gradleup/librarian/core/tooling/Version;)Lcom/gradleup/librarian/core/tooling/Version; - public static final fun toVersionOrNull (Ljava/lang/String;)Lcom/gradleup/librarian/core/tooling/Version; + public static synthetic fun copy$default (Lcom/gradleup/librarian/core/tooling/SemVer;IIILcom/gradleup/librarian/core/tooling/PreRelease;ZILjava/lang/Object;)Lcom/gradleup/librarian/core/tooling/SemVer; + public static final fun next (Lcom/gradleup/librarian/core/tooling/SemVer;)Lcom/gradleup/librarian/core/tooling/SemVer; + public static final fun nextMajor (Lcom/gradleup/librarian/core/tooling/SemVer;)Lcom/gradleup/librarian/core/tooling/SemVer; + public static final fun nextMinor (Lcom/gradleup/librarian/core/tooling/SemVer;)Lcom/gradleup/librarian/core/tooling/SemVer; + public static final fun nextPatch (Lcom/gradleup/librarian/core/tooling/SemVer;)Lcom/gradleup/librarian/core/tooling/SemVer; + public static final fun nextPrerelease (Lcom/gradleup/librarian/core/tooling/SemVer;)Lcom/gradleup/librarian/core/tooling/SemVer; + public static final fun semVerOrNull (Ljava/lang/String;)Lcom/gradleup/librarian/core/tooling/SemVer; } public final class com/gradleup/librarian/core/tooling/Tag_and_bumpKt { public static final fun getCurrentVersion ()Ljava/lang/String; - public static final fun tagAndBump (Lcom/gradleup/librarian/core/tooling/Version;)V + public static final fun runCommand ([Ljava/lang/String;)Ljava/lang/String; + public static final fun tagAndBump (Lcom/gradleup/librarian/core/tooling/SemVer;)V } public final class com/gradleup/librarian/core/tooling/UploadError : com/gradleup/librarian/core/tooling/UploadResult { @@ -107,17 +122,6 @@ public final class com/gradleup/librarian/core/tooling/UploadSuccess : com/gradl public final fun getUrl ()Ljava/lang/String; } -public final class com/gradleup/librarian/core/tooling/Version { - public fun (IIILcom/gradleup/librarian/core/tooling/PreRelease;Z)V - public final fun compareTo (Lcom/gradleup/librarian/core/tooling/Version;)I - public final fun getMajor ()I - public final fun getMinor ()I - public final fun getPatch ()I - public final fun getPreRelease ()Lcom/gradleup/librarian/core/tooling/PreRelease; - public final fun isSnapshot ()Z - public fun toString ()Ljava/lang/String; -} - public final class com/gradleup/librarian/core/tooling/init/ActionsKt { public static final fun initActions (Ljava/nio/file/Path;Ljava/lang/String;Z)V } diff --git a/librarian-core/build.gradle.kts b/librarian-core/build.gradle.kts index be2b3431..5fd98495 100644 --- a/librarian-core/build.gradle.kts +++ b/librarian-core/build.gradle.kts @@ -1,21 +1,21 @@ import com.gradleup.librarian.gradle.Librarian plugins { - id("org.jetbrains.kotlin.jvm") + id("org.jetbrains.kotlin.jvm") } Librarian.module(project) dependencies { - implementation(libs.coroutines) - implementation(libs.jsonpath) - implementation(libs.jsonpathkt) - implementation(libs.okhttp) - implementation(libs.bouncycastle.prov) - implementation(libs.bouncycastle.pg) - implementation(libs.serialization.json) + implementation(libs.coroutines) + implementation(libs.jsonpath) + implementation(libs.jsonpathkt) + implementation(libs.okhttp) + implementation(libs.bouncycastle.prov) + implementation(libs.bouncycastle.pg) + implementation(libs.serialization.json) - testImplementation(kotlin("test")) - testImplementation(libs.mockserver) + testImplementation(kotlin("test")) + testImplementation(libs.mockserver) } diff --git a/librarian-core/src/main/kotlin/com/gradleup/librarian/core/tooling/semver.kt b/librarian-core/src/main/kotlin/com/gradleup/librarian/core/tooling/semver.kt index f0d35498..19561e3e 100644 --- a/librarian-core/src/main/kotlin/com/gradleup/librarian/core/tooling/semver.kt +++ b/librarian-core/src/main/kotlin/com/gradleup/librarian/core/tooling/semver.kt @@ -5,15 +5,79 @@ class PreRelease( val version: Int, ) - -class Version( +/** + * + * A variation of the traditional semantic versioning specification to support `-SNAPSHOT` and prerelease versions. + * + * ``` + * ::= + * | "-SNAPSHOT" + * | "-" + * | "-" "-SNAPSHOT" + * + * ::= "." "." + * + * ::= + * + * ::= + * + * ::= + * + * ::= + * + * ::= + * | "." + * + * ::= + * + * ::= + * | "." + * + * ::= + * | + * + * ::= + * | + * | + * | + * + * ::= "0" + * | + * | + * + * ::= + * | + * + * ::= + * | + * + * ::= + * | "-" + * + * ::= + * | + * + * ::= "0" + * | + * + * ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" + * + * ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" + * | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" + * | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" + * | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" + * | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" + * | "y" | "z" + * ``` + */ +class SemVer( val major: Int, val minor: Int, val patch: Int, val preRelease: PreRelease?, val isSnapshot: Boolean, ) { - operator fun compareTo(currentVersionParsed: Version): Int { + operator fun compareTo(currentVersionParsed: SemVer): Int { var ret = major.compareTo(currentVersionParsed.major) if (ret != 0) return ret @@ -53,24 +117,24 @@ internal fun PreRelease?.compareTo(other: PreRelease?): Int { } else if (other == null) { -1 } else { - // XXX: should we handle non-lexicographic order here? + // XXX: should we handle non-lexicographic order here? (-dev could be lower than -alpha, etc...) val ret = name.compareTo(other.name) if (ret != 0) { return ret } - return version.compareTo(other.version) + version.compareTo(other.version) } } -fun Version.copy( +fun SemVer.copy( major: Int = this.major, minor: Int = this.minor, patch: Int = this.patch, preRelease: PreRelease? = this.preRelease, isSnapshot: Boolean = this.isSnapshot, -): Version { - return Version( +): SemVer { + return SemVer( major, minor, patch, @@ -86,15 +150,38 @@ fun PreRelease.copy( return PreRelease(name, version) } -fun Version.nextPatch(): Version { - return copy(patch = patch + 1, preRelease = null, isSnapshot = false) +fun SemVer.nextPatch(): SemVer { + if (preRelease != null) { + return copy(preRelease = null) + } + return copy(patch = patch + 1, preRelease = null) +} + +fun SemVer.nextMinor(): SemVer { + return copy(minor = minor + 1, patch = 0, preRelease = null) +} + +fun SemVer.nextMajor(): SemVer { + return copy(major = major + 1, minor = 0, patch = 0, preRelease = null) +} + +fun SemVer.nextPrerelease(): SemVer { + require(preRelease != null) { "Cannot bump prerelease on non-prerelease version" } + return copy(preRelease = preRelease.copy(version = preRelease.version + 1)) } -fun Version.nextMinor(): Version { - return copy(minor = minor + 1, patch = 0, preRelease = null, isSnapshot = false) +/** + * Bumps the prerelease if any, or the patch else. + */ +fun SemVer.bump(): SemVer { + return when { + preRelease != null -> nextPrerelease() + else -> nextPatch() + } } -fun Version.next(): Version { +@Deprecated("Use bump() instead") +fun SemVer.next(): SemVer { return when { isSnapshot -> { copy(isSnapshot = false) @@ -108,7 +195,7 @@ fun Version.next(): Version { } } -fun String.toVersionOrNull(): Version? { +fun String.semVerOrNull(): SemVer? { val regex1 = Regex("([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)") val result1 = regex1.matchEntire(this) ?: return null @@ -119,7 +206,7 @@ fun String.toVersionOrNull(): Version? { var rem = result1.groupValues[4] if (rem.isEmpty()) { - return Version(major, minor, patch, null, false) + return SemVer(major, minor, patch, null, false) } var preRelease: PreRelease? = null @@ -139,7 +226,7 @@ fun String.toVersionOrNull(): Version? { val v = result2.groupValues[2].toIntOrNull() ?: return null preRelease = PreRelease(result2.groupValues[1], v) } - return Version( + return SemVer( major, minor, patch, diff --git a/librarian-core/src/main/kotlin/com/gradleup/librarian/core/tooling/tag-and-bump.kt b/librarian-core/src/main/kotlin/com/gradleup/librarian/core/tooling/tag-and-bump.kt index 8f85241a..33fd77fd 100644 --- a/librarian-core/src/main/kotlin/com/gradleup/librarian/core/tooling/tag-and-bump.kt +++ b/librarian-core/src/main/kotlin/com/gradleup/librarian/core/tooling/tag-and-bump.kt @@ -3,7 +3,7 @@ package com.gradleup.librarian.core.tooling import com.gradleup.librarian.core.tooling.init.rootPropertiesFilename import java.io.File -fun tagAndBump(versionToRelease: Version) { +fun tagAndBump(versionToRelease: SemVer) { checkCwd() check(runCommand("git", "status", "--porcelain").isEmpty()) { @@ -60,7 +60,7 @@ fun tagAndBump(versionToRelease: Version) { println("Everything is done.") } -internal fun runCommand(vararg args: String): String { +fun runCommand(vararg args: String): String { val builder = ProcessBuilder(*args) .redirectError(ProcessBuilder.Redirect.INHERIT) diff --git a/librarian-core/src/test/kotlin/SemVerTest.kt b/librarian-core/src/test/kotlin/SemVerTest.kt index 969bdd01..b7c4a906 100644 --- a/librarian-core/src/test/kotlin/SemVerTest.kt +++ b/librarian-core/src/test/kotlin/SemVerTest.kt @@ -1,4 +1,4 @@ -import com.gradleup.librarian.core.tooling.toVersionOrNull +import com.gradleup.librarian.core.tooling.semVerOrNull import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -6,7 +6,7 @@ import kotlin.test.assertNotNull class SemVerTest { @Test fun test() { - "1.2.3-alpha.0".toVersionOrNull().apply { + "1.2.3-alpha.0".semVerOrNull().apply { assertNotNull(this) assertEquals(1, major) assertEquals(2, minor) @@ -15,7 +15,7 @@ class SemVerTest { assertEquals("alpha", preRelease?.name) assertEquals(0, preRelease?.version) } - "1.2.3".toVersionOrNull().apply { + "1.2.3".semVerOrNull().apply { assertNotNull(this) assertEquals(1, major) assertEquals(2, minor) @@ -23,7 +23,7 @@ class SemVerTest { assertEquals(false, isSnapshot) assertEquals(null, preRelease) } - "1.2.3-SNAPSHOT".toVersionOrNull().apply { + "1.2.3-SNAPSHOT".semVerOrNull().apply { assertNotNull(this) assertEquals(1, major) assertEquals(2, minor) @@ -31,7 +31,7 @@ class SemVerTest { assertEquals(true, isSnapshot) assertEquals(null, preRelease) } - "1.2.3-alpha.0-SNAPSHOT".toVersionOrNull().apply { + "1.2.3-alpha.0-SNAPSHOT".semVerOrNull().apply { assertNotNull(this) assertEquals(1, major) assertEquals(2, minor)