Skip to content

Commit 4b64ecd

Browse files
authored
feat(server): serve maven-metadata.xml (#1360)
Part of #1318.
1 parent f129cd7 commit 4b64ecd

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCo
55
import io.github.typesafegithub.workflows.mavenbinding.Artifact
66
import io.github.typesafegithub.workflows.mavenbinding.JarArtifact
77
import io.github.typesafegithub.workflows.mavenbinding.TextArtifact
8+
import io.github.typesafegithub.workflows.mavenbinding.buildPackageArtifacts
89
import io.github.typesafegithub.workflows.mavenbinding.buildVersionArtifacts
910
import io.ktor.http.ContentType
1011
import io.ktor.http.HttpStatusCode
@@ -81,6 +82,29 @@ fun main() {
8182
}
8283
}
8384

85+
route("/binding/{owner}/{name}/{file}") {
86+
get {
87+
val owner = call.parameters["owner"]!!
88+
val name = call.parameters["name"]!!
89+
val file = call.parameters["file"]!!
90+
val actionCoords =
91+
ActionCoords(
92+
owner = owner,
93+
name = name,
94+
version = "irrelevant",
95+
)
96+
val bindingArtifacts = actionCoords.buildPackageArtifacts(githubToken = System.getenv("GITHUB_TOKEN"))
97+
if (file in bindingArtifacts) {
98+
when (val artifact = bindingArtifacts[file]) {
99+
is String -> call.respondText(artifact)
100+
else -> call.respondText(text = "Not found", status = HttpStatusCode.NotFound)
101+
}
102+
} else {
103+
call.respondText(text = "Not found", status = HttpStatusCode.NotFound)
104+
}
105+
}
106+
}
107+
84108
get("/status") {
85109
call.respondText("OK")
86110
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
plugins {
22
buildsrc.convention.`kotlin-jvm`
3+
kotlin("plugin.serialization")
34
}
45

56
dependencies {
67
implementation("org.jetbrains.kotlin:kotlin-compiler")
78
api(projects.actionBindingGenerator)
9+
implementation(platform("io.ktor:ktor-bom:2.3.10"))
10+
implementation("io.ktor:ktor-client-core")
11+
implementation("io.ktor:ktor-client-cio")
12+
implementation("io.ktor:ktor-client-content-negotiation")
13+
implementation("io.ktor:ktor-serialization-kotlinx-json")
14+
815
runtimeOnly(projects.githubWorkflowsKt)
916
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package io.github.typesafegithub.workflows.mavenbinding
2+
3+
import io.ktor.client.HttpClient
4+
import io.ktor.client.call.body
5+
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
6+
import io.ktor.client.request.bearerAuth
7+
import io.ktor.client.request.get
8+
import io.ktor.serialization.kotlinx.json.json
9+
import kotlinx.serialization.Serializable
10+
import kotlinx.serialization.json.Json
11+
import java.time.Instant
12+
import java.time.ZoneId
13+
import java.time.format.DateTimeFormatter
14+
15+
internal suspend fun buildMavenMetadataFile(
16+
owner: String,
17+
name: String,
18+
githubToken: String,
19+
): String {
20+
val lastUpdated =
21+
DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
22+
.withZone(ZoneId.systemDefault())
23+
.format(Instant.now())
24+
val availableMajorVersions =
25+
fetchAvailableVersions(owner = owner, name = name, githubToken = githubToken)
26+
.filter { !it.contains(".") }
27+
val newest = availableMajorVersions.maxBy { it.removePrefix("v") }
28+
return """
29+
<?xml version="1.0" encoding="UTF-8"?>
30+
<metadata>
31+
<groupId>$owner</groupId>
32+
<artifactId>$name</artifactId>
33+
<versioning>
34+
<latest>$newest</latest>
35+
<release>$newest</release>
36+
<versions>
37+
${availableMajorVersions.joinToString(separator = "\n") {
38+
" <version>$it</version>"
39+
}}
40+
</versions>
41+
<lastUpdated>$lastUpdated</lastUpdated>
42+
</versioning>
43+
</metadata>
44+
""".trimIndent()
45+
}
46+
47+
private suspend fun fetchAvailableVersions(
48+
owner: String,
49+
name: String,
50+
githubToken: String,
51+
): List<String> =
52+
listOf(
53+
apiTagsUrl(owner = owner, name = name),
54+
apiBranchesUrl(owner = owner, name = name),
55+
).flatMap { url -> fetchGithubRefs(url, githubToken) }
56+
.map { it.ref.substringAfterLast("/") }
57+
58+
private suspend fun fetchGithubRefs(
59+
url: String,
60+
githubToken: String,
61+
): List<GithubRef> =
62+
httpClient.get(urlString = url) {
63+
bearerAuth(githubToken)
64+
}.body()
65+
66+
private val httpClient by lazy {
67+
HttpClient {
68+
install(ContentNegotiation) {
69+
json(
70+
Json {
71+
ignoreUnknownKeys = true
72+
},
73+
)
74+
}
75+
}
76+
}
77+
78+
private fun apiTagsUrl(
79+
owner: String,
80+
name: String,
81+
): String = "https://api.github.com/repos/$owner/$name/git/matching-refs/tags/v"
82+
83+
private fun apiBranchesUrl(
84+
owner: String,
85+
name: String,
86+
): String = "https://api.github.com/repos/$owner/$name/git/matching-refs/heads/v"
87+
88+
@Serializable
89+
data class GithubRef(
90+
val ref: String,
91+
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.github.typesafegithub.workflows.mavenbinding
2+
3+
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords
4+
5+
suspend fun ActionCoords.buildPackageArtifacts(githubToken: String): Map<String, String> {
6+
val mavenMetadata = buildMavenMetadataFile(owner = owner, name = name, githubToken = githubToken)
7+
return mapOf(
8+
"maven-metadata.xml" to mavenMetadata,
9+
)
10+
}

0 commit comments

Comments
 (0)