Skip to content

Commit 6b26e51

Browse files
authored
add cli helpers (#76)
* Add CLI helpers to bump versions * update apiDump
1 parent b633050 commit 6b26e51

7 files changed

Lines changed: 331 additions & 1 deletion

File tree

librarian-cli/api/librarian-cli.api

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,40 @@ public final class com/gradleup/librarian/cli/command/CreateGitHubRepository : c
2424
public fun run ()V
2525
}
2626

27+
public final class com/gradleup/librarian/cli/command/FileContext {
28+
public fun <init> ()V
29+
public final fun replaceMavenCoordinates (Ljava/lang/String;)V
30+
public final fun replacePluginId (Ljava/lang/String;)V
31+
}
32+
2733
public final class com/gradleup/librarian/cli/command/GenerateKey : com/github/ajalt/clikt/core/CliktCommand {
2834
public fun <init> ()V
2935
public fun run ()V
3036
}
3137

38+
public final class com/gradleup/librarian/cli/command/PrepareNextVersion : com/github/ajalt/clikt/core/CliktCommand {
39+
public fun <init> (Lkotlin/jvm/functions/Function1;)V
40+
public fun run ()V
41+
}
42+
43+
public final class com/gradleup/librarian/cli/command/SetVersion : com/github/ajalt/clikt/core/CliktCommand {
44+
public fun <init> ()V
45+
public final fun getVersion ()Ljava/lang/String;
46+
public fun run ()V
47+
}
48+
3249
public final class com/gradleup/librarian/cli/command/UploadKey : com/github/ajalt/clikt/core/CliktCommand {
3350
public fun <init> ()V
3451
public final fun getKeyFile ()Ljava/lang/String;
3552
public fun run ()V
3653
}
3754

55+
public final class com/gradleup/librarian/cli/command/VersionContext {
56+
public fun <init> (Ljava/lang/String;)V
57+
public final fun file (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
58+
public final fun getVersion ()Ljava/lang/String;
59+
}
60+
3861
public final class com/gradleup/librarian/cli/command/init/GitHubSecretsKt {
3962
public static final fun setSecrets (Ljava/nio/file/Path;)V
4063
}

librarian-cli/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Librarian.module(project)
1010

1111
dependencies {
1212
implementation(project(":librarian-core"))
13-
implementation(libs.clikt)
13+
api(libs.clikt)
1414
implementation(libs.inquirer)
1515
implementation(libs.mordant)
1616

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.gradleup.librarian.cli.command
2+
3+
import com.github.ajalt.clikt.core.CliktCommand
4+
import java.io.File
5+
import java.util.regex.Pattern
6+
7+
class PrepareNextVersion(private val setVersionInDocs: VersionContext.() -> Unit) : CliktCommand() {
8+
override fun run() {
9+
val currentVersion = getCurrentVersion()
10+
check(currentVersion.endsWith("-SNAPSHOT")) {
11+
"Current version '$currentVersion' does not ends with '-SNAPSHOT'. Call set-version to update it."
12+
}
13+
14+
val releaseVersion = currentVersion.dropSnapshot()
15+
val nextSnapshot = getNextSnapshot(releaseVersion)
16+
17+
VersionContext(releaseVersion).setVersionInDocs()
18+
setCurrentVersion(nextSnapshot)
19+
20+
println("Docs have been updated to use version '$releaseVersion'.")
21+
println("Version is now '$nextSnapshot'.")
22+
}
23+
}
24+
25+
class VersionContext(val version: String) {
26+
fun file(path: String, block: FileContext.() -> Unit) {
27+
val f = File(path)
28+
var newText = f.readText()
29+
30+
val fileContext = FileContext().also(block)
31+
newText = fileContext.gas.fold(newText) { acc, ga ->
32+
acc.replace(Regex("\"${Pattern.quote(ga)}:.*\""), "\"$ga:$version\"")
33+
}
34+
newText = fileContext.pluginIds.fold(newText) { acc, pluginId ->
35+
acc.replace(Regex("id\\(\"${Pattern.quote(pluginId)}\"\\).version\\([^)]*\\)"), "id(\"$pluginId\").version($version)")
36+
.replace(Regex("id\\(\"${Pattern.quote(pluginId)}\"\\) version\\([^)]*\\)"), "id(\"$pluginId\") version($version)")
37+
}
38+
39+
f.writeText(newText)
40+
}
41+
}
42+
43+
class FileContext {
44+
internal val gas = mutableListOf<String>()
45+
internal val pluginIds = mutableListOf<String>()
46+
47+
/**
48+
* Replaces every instance version with the new version in all instances of:
49+
*
50+
* - `"$ga:version"`
51+
*
52+
* @param ga a group and artifact ids in the form `group:artifact`
53+
*/
54+
fun replaceMavenCoordinates(ga: String) {
55+
gas.add(ga)
56+
}
57+
58+
/**
59+
* Replaces every instance version with the new version in all instances of:
60+
*
61+
* - `id("$pluginId").version(version)`
62+
* - `id("$pluginId") version(version)`
63+
*/
64+
fun replacePluginId(pluginId: String) {
65+
pluginIds.add(pluginId)
66+
}
67+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.gradleup.librarian.cli.command
2+
3+
import com.github.ajalt.clikt.core.CliktCommand
4+
import com.github.ajalt.clikt.parameters.arguments.argument
5+
6+
class SetVersion : CliktCommand() {
7+
val version by argument()
8+
override fun run() {
9+
setCurrentVersion(version)
10+
11+
println("Version is now '$version'.")
12+
}
13+
}
14+
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.gradleup.librarian.cli.command
2+
3+
import java.io.File
4+
5+
6+
internal fun String.dropSnapshot() = this.removeSuffix("-SNAPSHOT")
7+
8+
internal fun setCurrentVersion(version: String) {
9+
val librarianRootProperties = File("librarian.root.properties")
10+
val newContent = librarianRootProperties.readLines().joinToString(separator = "\n", postfix = "\n") {
11+
it.replace(Regex("pom.version=.*"), "pom.version=$version")
12+
}
13+
librarianRootProperties.writeText(newContent)
14+
}
15+
16+
internal fun getCurrentVersion(): String {
17+
val versionLines = File("librarian.root.properties").readLines().filter { it.startsWith("pom.version=") }
18+
19+
require(versionLines.size > 0) {
20+
"cannot find the version in ./librarian.root.properties"
21+
}
22+
23+
require(versionLines.size == 1) {
24+
"multiple versions found in ./librarian.root.properties"
25+
}
26+
27+
val regex = Regex("pom.version=(.*)-SNAPSHOT")
28+
val matchResult = regex.matchEntire(versionLines.first())
29+
30+
require(matchResult != null) {
31+
"'${versionLines.first()}' doesn't match pom.version=(.*)-SNAPSHOT"
32+
}
33+
34+
return matchResult.groupValues[1] + "-SNAPSHOT"
35+
}
36+
37+
internal fun getNextSnapshot(version: String): String {
38+
val components = version.split(".").toMutableList()
39+
val part = components.removeLast()
40+
var digitCount = 0
41+
for (i in part.indices.reversed()) {
42+
if (part[i] !in '0'..'9') {
43+
break
44+
}
45+
digitCount++
46+
}
47+
48+
check(digitCount > 0) {
49+
"Cannot find a number to bump in $version"
50+
}
51+
52+
// prefix can be "alpha", "dev", etc...
53+
val prefix = if (digitCount < part.length) {
54+
part.substring(0, part.length - digitCount)
55+
} else {
56+
""
57+
}
58+
val numericPart = part.substring(part.length - digitCount, part.length)
59+
val asNumber = numericPart.toInt()
60+
61+
val nextPart = if (numericPart[0] == '0') {
62+
// https://docs.gradle.org/current/userguide/single_versions.html#version_ordering
63+
// Gradle understands that alpha2 > alpha10 but it might not be the case for everyone so
64+
// use the same naming schemes as other libs and keep the prefix
65+
val width = numericPart.length
66+
String.format("%0${width}d", asNumber + 1)
67+
} else {
68+
(asNumber + 1).toString()
69+
}
70+
71+
components.add("$prefix$nextPart")
72+
return components.joinToString(".") + "-SNAPSHOT"
73+
}

librarian-cli/src/main/kotlin/com/gradleup/librarian/cli/utils.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.gradleup.librarian.cli
22

33
import com.github.ajalt.mordant.terminal.Terminal
44
import com.gradleup.librarian.cli.command.checkOrExit
5+
import java.io.File
56

67
fun requireInteraction() {
78
checkOrExit(Terminal().info.inputInteractive) {

scripts/update-repo.main.kts

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/usr/bin/env kotlin
2+
3+
@file:DependsOn("com.github.ajalt.clikt:clikt-jvm:5.0.2")
4+
5+
import com.github.ajalt.clikt.core.CliktCommand
6+
import com.github.ajalt.clikt.core.main
7+
import com.github.ajalt.clikt.core.subcommands
8+
import com.github.ajalt.clikt.parameters.arguments.argument
9+
import java.io.File
10+
import java.util.regex.Pattern
11+
12+
class MainCommand : CliktCommand() {
13+
override fun run() {
14+
}
15+
}
16+
17+
class PrepareNextVersion(private val setVersionInDocs: VersionContext.() -> Unit) : CliktCommand() {
18+
override fun run() {
19+
val currentVersion = getCurrentVersion()
20+
check(currentVersion.endsWith("-SNAPSHOT")) {
21+
"Current version '$currentVersion' does not ends with '-SNAPSHOT'. Call set-version to update it."
22+
}
23+
24+
val releaseVersion = currentVersion.dropSnapshot()
25+
val nextSnapshot = getNextSnapshot(releaseVersion)
26+
27+
VersionContext(releaseVersion).setVersionInDocs()
28+
setCurrentVersion(nextSnapshot)
29+
30+
println("Docs have been updated to use version '$releaseVersion'.")
31+
println("Version is now '$nextSnapshot'.")
32+
}
33+
}
34+
35+
class VersionContext(val version: String) {
36+
fun file(path: String, block: FileContext.() -> Unit) {
37+
val f = File(path)
38+
var newText = f.readText()
39+
40+
val fileContext = FileContext().also(block)
41+
newText = fileContext.gas.fold(newText) { acc, ga ->
42+
acc.replace(Regex("\"${Pattern.quote(ga)}:.*\""), "\"$ga:$version\"")
43+
}
44+
newText = fileContext.pluginIds.fold(newText) { acc, pluginId ->
45+
acc.replace(Regex("id\\(\"${Pattern.quote(pluginId)}\"\\).version\\([^\\)]*\\)"), "id(\"$pluginId\").version($version)")
46+
.replace(Regex("id\\(\"${Pattern.quote(pluginId)}\"\\) version\\([^\\)]*\\)"), "id(\"$pluginId\") version($version)")
47+
}
48+
49+
f.writeText(newText)
50+
}
51+
}
52+
53+
class FileContext {
54+
internal val gas = mutableListOf<String>()
55+
internal val pluginIds = mutableListOf<String>()
56+
57+
/**
58+
* Replaces every instance version with the new version in all instances of:
59+
*
60+
* - `"$ga:version"`
61+
*
62+
* @param ga a group and artifact ids in the form `group:artifact`
63+
*/
64+
fun replaceMavenCoordinates(ga: String) {
65+
gas.add(ga)
66+
}
67+
68+
/**
69+
* Replaces every instance version with the new version in all instances of:
70+
*
71+
* - `id("$pluginId").version(version)`
72+
* - `id("$pluginId") version(version)`
73+
*/
74+
fun replacePluginId(pluginId: String) {
75+
pluginIds.add(pluginId)
76+
}
77+
}
78+
79+
internal fun String.dropSnapshot() = this.removeSuffix("-SNAPSHOT")
80+
81+
internal fun setCurrentVersion(version: String) {
82+
val librarianRootProperties = File("librarian.root.properties")
83+
val newContent = librarianRootProperties.readLines().joinToString(separator = "\n", postfix = "\n") {
84+
it.replace(Regex("pom.version=.*"), "pom.version=$version")
85+
}
86+
librarianRootProperties.writeText(newContent)
87+
}
88+
89+
internal fun getCurrentVersion(): String {
90+
val versionLines = File("librarian.root.properties").readLines().filter { it.startsWith("pom.version=") }
91+
92+
require(versionLines.size > 0) {
93+
"cannot find the version in ./librarian.root.properties"
94+
}
95+
96+
require(versionLines.size == 1) {
97+
"multiple versions found in ./librarian.root.properties"
98+
}
99+
100+
val regex = Regex("pom.version=(.*)-SNAPSHOT")
101+
val matchResult = regex.matchEntire(versionLines.first())
102+
103+
require(matchResult != null) {
104+
"'${versionLines.first()}' doesn't match pom.version=(.*)-SNAPSHOT"
105+
}
106+
107+
return matchResult.groupValues[1] + "-SNAPSHOT"
108+
}
109+
110+
internal fun getNextSnapshot(version: String): String {
111+
val components = version.split(".").toMutableList()
112+
val part = components.removeLast()
113+
var digitCount = 0
114+
for (i in part.indices.reversed()) {
115+
if (part[i] !in '0'..'9') {
116+
break
117+
}
118+
digitCount++
119+
}
120+
121+
check(digitCount > 0) {
122+
"Cannot find a number to bump in $version"
123+
}
124+
125+
// prefix can be "alpha", "dev", etc...
126+
val prefix = if (digitCount < part.length) {
127+
part.substring(0, part.length - digitCount)
128+
} else {
129+
""
130+
}
131+
val numericPart = part.substring(part.length - digitCount, part.length)
132+
val asNumber = numericPart.toInt()
133+
134+
val nextPart = if (numericPart[0] == '0') {
135+
// https://docs.gradle.org/current/userguide/single_versions.html#version_ordering
136+
// Gradle understands that alpha2 > alpha10 but it might not be the case for everyone so
137+
// use the same naming schemes as other libs and keep the prefix
138+
val width = numericPart.length
139+
String.format("%0${width}d", asNumber + 1)
140+
} else {
141+
(asNumber + 1).toString()
142+
}
143+
144+
components.add("$prefix$nextPart")
145+
return components.joinToString(".") + "-SNAPSHOT"
146+
}
147+
148+
MainCommand().subcommands(PrepareNextVersion {
149+
file("README.md") {
150+
replacePluginId("com.gradleup.librarian")
151+
}
152+
}).main(args)

0 commit comments

Comments
 (0)