From 7cb9a46cbdc87f275c17b3fc4acd5c2321812029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Fri, 16 Aug 2024 12:07:43 +0200 Subject: [PATCH] feat(server): make it possible to POST custom typings for testing during typing creation --- .../api/action-binding-generator.api | 15 +- .../domain/ActionCoords.kt | 3 +- .../domain/TypingActualSource.kt | 1 + .../generation/Generation.kt | 10 +- .../typing/TypesProviding.kt | 12 +- .../typing/TypesProvidingTest.kt | 154 ++++++++++++++++++ docs/user-guide/using-actions.md | 13 +- jit-binding-server/build.gradle.kts | 1 + .../workflows/jitbindingserver/Main.kt | 52 +++++- .../workflows/mavenbinding/JarBuilding.kt | 4 +- .../mavenbinding/VersionArtifactsBuilding.kt | 4 +- 11 files changed, 241 insertions(+), 28 deletions(-) diff --git a/action-binding-generator/api/action-binding-generator.api b/action-binding-generator/api/action-binding-generator.api index 76660945c9..fe05e146cb 100644 --- a/action-binding-generator/api/action-binding-generator.api +++ b/action-binding-generator/api/action-binding-generator.api @@ -1,16 +1,18 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/lang/String; public final fun component4 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; - public static synthetic fun copy$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; + public final fun component5 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; + public static synthetic fun copy$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; public fun equals (Ljava/lang/Object;)Z public final fun getName ()Ljava/lang/String; public final fun getOwner ()Ljava/lang/String; public final fun getPath ()Ljava/lang/String; + public final fun getTypesUuid ()Ljava/lang/String; public final fun getVersion ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -46,6 +48,7 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/dom public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource : java/lang/Enum { public static final field ACTION Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource; + public static final field CUSTOM Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource; public static final field TYPING_CATALOG Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource; @@ -72,8 +75,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/gen } public final class io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationKt { - public static final fun generateBinding (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lkotlin/Pair;)Ljava/util/List; - public static synthetic fun generateBinding$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lkotlin/Pair;ILjava/lang/Object;)Ljava/util/List; + public static final fun generateBinding (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lkotlin/Pair;Ljava/lang/String;)Ljava/util/List; + public static synthetic fun generateBinding$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lkotlin/Pair;Ljava/lang/String;ILjava/lang/Object;)Ljava/util/List; } public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Input { diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt index 617d283cb0..c0415e5a71 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt @@ -5,6 +5,7 @@ public data class ActionCoords( val name: String, val version: String, val path: String? = null, + val typesUuid: String? = null, ) /** @@ -13,7 +14,7 @@ public data class ActionCoords( */ public val ActionCoords.isTopLevel: Boolean get() = path == null -public val ActionCoords.prettyPrint: String get() = "$owner/$fullName@$version" +public val ActionCoords.prettyPrint: String get() = "$owner/$fullName@$version${typesUuid?.let { " (types: $it)" } ?: ""}" /** * For most actions, it's empty. diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource.kt index 094f3251b4..4a6fca4f9e 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource.kt @@ -3,4 +3,5 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.domain public enum class TypingActualSource { ACTION, TYPING_CATALOG, + CUSTOM, } diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt index 0591b1b3fd..ed2804e68f 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt @@ -61,11 +61,13 @@ public fun ActionCoords.generateBinding( metadataRevision: MetadataRevision, metadata: Metadata? = null, inputTypings: Pair, TypingActualSource?>? = null, + types: String? = null, ): List { val metadataResolved = metadata ?: this.fetchMetadata(metadataRevision) ?: return emptyList() val metadataProcessed = metadataResolved.removeDeprecatedInputsIfNameClash() - val inputTypingsResolved = inputTypings ?: this.provideTypes(metadataRevision) + val (inputTypingsResolved, typingActualSource) = + inputTypings ?: this.provideTypes(metadataRevision, types = types) val packageName = owner.toKotlinPackageName() val className = this.buildActionClassName() @@ -78,7 +80,7 @@ public fun ActionCoords.generateBinding( emptyMap(), classNameUntyped, untypedClass = true, - replaceWith = inputTypingsResolved.second?.let { CodeBlock.of("ReplaceWith(%S)", className) }, + replaceWith = typingActualSource?.let { CodeBlock.of("ReplaceWith(%S)", className) }, ) return listOfNotNull( @@ -89,12 +91,12 @@ public fun ActionCoords.generateBinding( packageName = packageName, typingActualSource = null, ), - inputTypingsResolved.second?.let { + typingActualSource?.let { val actionBindingSourceCode = generateActionBindingSourceCode( metadata = metadataProcessed, coords = this, - inputTypings = inputTypingsResolved.first, + inputTypings = inputTypingsResolved, className = className, ) ActionBinding( diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt index a36d409d4e..013684aeeb 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt @@ -7,6 +7,7 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.Metadata import io.github.typesafegithub.workflows.actionbindinggenerator.domain.NewestForVersion import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.ACTION +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.CUSTOM import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.TYPING_CATALOG import io.github.typesafegithub.workflows.actionbindinggenerator.domain.subName import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.fetchUri @@ -20,10 +21,12 @@ import java.net.URI internal fun ActionCoords.provideTypes( metadataRevision: MetadataRevision, fetchUri: (URI) -> String = ::fetchUri, + types: String? = null, ): Pair, TypingActualSource?> = ( - this.fetchTypingMetadata(metadataRevision, fetchUri) - ?: this.toMajorVersion().fetchFromTypingsFromCatalog(fetchUri) + customTypingMetadata(types) + ?: this.fetchTypingMetadata(metadataRevision, fetchUri) + ?: this.toMajorVersion().fetchTypingsFromCatalog(fetchUri) )?.let { Pair(it.first.toTypesMap(), it.second) } ?: Pair(emptyMap(), null) @@ -41,6 +44,9 @@ private fun ActionCoords.catalogMetadata() = private fun ActionCoords.actionTypesYamlUrl(gitRef: String) = "https://raw.githubusercontent.com/$owner/$name/$gitRef$subName/action-types.yaml" +private fun customTypingMetadata(types: String? = null) = + types?.let { Pair(yaml.decodeFromStringOrDefaultIfEmpty(it, ActionTypes()), CUSTOM) } + private fun ActionCoords.fetchTypingMetadata( metadataRevision: MetadataRevision, fetchUri: (URI) -> String = ::fetchUri, @@ -64,7 +70,7 @@ private fun ActionCoords.fetchTypingMetadata( return Pair(yaml.decodeFromStringOrDefaultIfEmpty(typesMetadataYaml, ActionTypes()), ACTION) } -private fun ActionCoords.fetchFromTypingsFromCatalog(fetchUri: (URI) -> String = ::fetchUri): Pair? = +private fun ActionCoords.fetchTypingsFromCatalog(fetchUri: (URI) -> String = ::fetchUri): Pair? = ( fetchTypingsFromUrl(url = actionTypesFromCatalog(), fetchUri = fetchUri) ?: fetchTypingsForOlderVersionFromCatalog(fetchUri = fetchUri) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt index 4798402913..38ed2f09b4 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt @@ -87,6 +87,12 @@ class TypesProvidingTest : stored-in-typing-catalog: type: string """.trimIndent() + val custom = + """ + inputs: + custom: + type: string + """.trimIndent() val metadata = """ "versionsWithTypings": @@ -311,6 +317,134 @@ class TypesProvidingTest : types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION) } + test("only custom") { + // Given + val fetchUri: (URI) -> String = { throw IOException() } + val actionCoord = ActionCoords("some-owner", "some-name", "v3") + + // When + val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = custom) + + // Then + types shouldBe Pair(mapOf("custom" to StringTyping), TypingActualSource.CUSTOM) + } + + test("only custom for subaction") { + // Given + val fetchUri: (URI) -> String = { throw IOException() } + val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub") + + // When + val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = custom) + + // Then + types shouldBe Pair(mapOf("custom" to StringTyping), TypingActualSource.CUSTOM) + } + + test("hosted by action, stored in typing catalog, and custom") { + // Given + val fetchUri: (URI) -> String = { + when (it) { + URI( + "https://raw.githubusercontent.com/some-owner/some-name/" + + "some-hash/action-types.yml", + ), + -> hostedByActionYml + URI( + "https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" + + "main/typings/some-owner/some-name/v3/action-types.yml", + ), + -> storedInTypingCatalog + else -> throw IOException() + } + } + val actionCoord = ActionCoords("some-owner", "some-name", "v3") + + // When + val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = custom) + + // Then + types shouldBe Pair(mapOf("custom" to StringTyping), TypingActualSource.CUSTOM) + } + + test("hosted by subaction, stored in typing catalog, and custom") { + // Given + val fetchUri: (URI) -> String = { + when (it) { + URI( + "https://raw.githubusercontent.com/some-owner/some-name/" + + "some-hash/some-sub/action-types.yml", + ), + -> hostedByActionYml + URI( + "https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" + + "main/typings/some-owner/some-name/v3/some-sub/action-types.yml", + ), + -> storedInTypingCatalog + else -> throw IOException() + } + } + val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub") + + // When + val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = custom) + + // Then + types shouldBe Pair(mapOf("custom" to StringTyping), TypingActualSource.CUSTOM) + } + + test("hosted by action, stored in typing catalog, and empty custom") { + // Given + val fetchUri: (URI) -> String = { + when (it) { + URI( + "https://raw.githubusercontent.com/some-owner/some-name/" + + "some-hash/action-types.yml", + ), + -> hostedByActionYml + URI( + "https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" + + "main/typings/some-owner/some-name/v3/action-types.yml", + ), + -> storedInTypingCatalog + else -> throw IOException() + } + } + val actionCoord = ActionCoords("some-owner", "some-name", "v3") + + // When + val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = "") + + // Then + types shouldBe Pair(emptyMap(), TypingActualSource.CUSTOM) + } + + test("hosted by subaction, stored in typing catalog, and empty custom") { + // Given + val fetchUri: (URI) -> String = { + when (it) { + URI( + "https://raw.githubusercontent.com/some-owner/some-name/" + + "some-hash/some-sub/action-types.yml", + ), + -> hostedByActionYml + URI( + "https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" + + "main/typings/some-owner/some-name/v3/some-sub/action-types.yml", + ), + -> storedInTypingCatalog + else -> throw IOException() + } + } + val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub") + + // When + val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = "") + + // Then + types shouldBe Pair(emptyMap(), TypingActualSource.CUSTOM) + } + test("only stored in typing catalog for older version") { // Given val fetchUri: (URI) -> String = { @@ -511,6 +645,26 @@ class TypesProvidingTest : ) } + test("only custom") { + // Given + val fetchUri: (URI) -> String = { throw IOException() } + val actionCoord = ActionCoords("some-owner", "some-name", "v3") + + // When + val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = typingYml) + + // Then + types shouldBe + Pair( + mapOf( + "granted-scopes" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), + "granted-scopes2" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), + "granted-scopes3" to ListOfTypings("""\n""", EnumTyping("GrantedScopes", listOf("read", "write"))), + ), + TypingActualSource.CUSTOM, + ) + } + test("billion laughs attack is prevented") { // Given val billionLaughsAttack = diff --git a/docs/user-guide/using-actions.md b/docs/user-guide/using-actions.md index c1828fb59f..b20076d918 100644 --- a/docs/user-guide/using-actions.md +++ b/docs/user-guide/using-actions.md @@ -23,7 +23,7 @@ For every action, a binding will be generated. However, some less popular action their inputs, so by default all inputs are of type `String`, have the suffix `_Untyped`, and additionally the class name will have an `_Untyped` suffix. The nullability of the inputs will be according to their required status. -There are two ways of configuring typings: +There are two and a half ways of configuring typings: 1. Recommended: a typing manifest (`action-typing.yml`) in the action's repo, see [github-actions-typing](https://github.com/typesafegithub/github-actions-typing/). Thanks to this, the action's owner is responsible for providing and maintaining the typings defined in a technology-agnostic way, to be used @@ -34,6 +34,17 @@ There are two ways of configuring typings: [github-actions-typing-catalog](https://github.com/typesafegithub/github-actions-typing-catalog), a community-maintained place to host the typings. You can contribute or fix typings for your favorite action by sending a PR. +3. Temporary: while developing a typing manifest it might be a good idea to test the result without needing to + release the action in question or merge a PR in the catalog. For this you can `POST` the typing manifest you have + on disk to the binding server using any valid URL for the action in question, for example using + ```bash + curl --data-binary @action-types.yml https://bindings.krzeminski.it/pbrisbin/setup-tool-action/v2/setup-tool-action-v2.pom + ``` + The binding server generates a binding with only the given type manifest and answer with some unique coordinates + that you can use in a test workflow script. The binding will be available the normal cache time on the binding + server and locally as long as you do not delete it from your local Maven repository where it is cached. After + the cache period on the server ended requesting the same coordinates will return a binding as if no typing + information is available at all. Once there are any typings in place for the action, the `_Untyped` suffixed class is marked `@Deprecated`, and a class without that suffix is created additionally. In that class for each input that does not have type information available diff --git a/jit-binding-server/build.gradle.kts b/jit-binding-server/build.gradle.kts index 1410949889..1f7b8a114f 100644 --- a/jit-binding-server/build.gradle.kts +++ b/jit-binding-server/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation("io.ktor:ktor-client-cio") implementation("io.ktor:ktor-client-content-negotiation") implementation("io.ktor:ktor-serialization-kotlinx-json") + implementation("it.krzeminski:snakeyaml-engine-kmp:3.0.2") implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-2.0:2.3.0-alpha") implementation("io.opentelemetry:opentelemetry-sdk:1.42.1") implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.42.1") diff --git a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt index fb41166eba..c7887f9f72 100644 --- a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt +++ b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt @@ -16,14 +16,18 @@ import io.ktor.server.application.call import io.ktor.server.application.install import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty +import io.ktor.server.request.receiveText import io.ktor.server.response.respondBytes import io.ktor.server.response.respondText import io.ktor.server.routing.Route import io.ktor.server.routing.get import io.ktor.server.routing.head +import io.ktor.server.routing.post import io.ktor.server.routing.route import io.ktor.server.routing.routing import io.opentelemetry.instrumentation.ktor.v2_0.server.KtorServerTracing +import it.krzeminski.snakeyaml.engine.kmp.api.Load +import java.util.UUID.randomUUID import kotlin.time.Duration.Companion.hours fun main() { @@ -58,7 +62,7 @@ fun main() { } get("/status") { - call.respondText("OK") + call.respondText(text = "OK") } } }.start(wait = true) @@ -85,7 +89,7 @@ private fun Route.metadata(refresh: Boolean = false) { val bindingArtifacts = actionCoords.buildPackageArtifacts(githubToken = getGithubToken()) if (file in bindingArtifacts) { when (val artifact = bindingArtifacts[file]) { - is String -> call.respondText(artifact) + is String -> call.respondText(text = artifact) else -> call.respondText(text = "Not found", status = HttpStatusCode.NotFound) } } else { @@ -101,7 +105,7 @@ private fun Route.artifact( get { val bindingArtifacts = call.toBindingArtifacts(bindingsCache, refresh) if (bindingArtifacts == null) { - call.respondText("Not found", status = HttpStatusCode.NotFound) + call.respondText(text = "Not found", status = HttpStatusCode.NotFound) return@get } else if (refresh && !deliverOnRefreshRoute) { call.respondText(text = "OK") @@ -129,41 +133,71 @@ private fun Route.artifact( val bindingArtifacts = call.toBindingArtifacts(bindingsCache, refresh) val file = call.parameters["file"]!! if (bindingArtifacts == null) { - call.respondText("Not found", status = HttpStatusCode.NotFound) + call.respondText(text = "Not found", status = HttpStatusCode.NotFound) return@head } if (file in bindingArtifacts) { - call.respondText("Exists", status = HttpStatusCode.OK) + call.respondText(text = "Exists", status = HttpStatusCode.OK) } else { call.respondText(text = "Not found", status = HttpStatusCode.NotFound) } } + + post { + val owner = "${call.parameters["owner"]}__types__${randomUUID()}" + val name = call.parameters["name"]!! + val version = call.parameters["version"]!! + val types = call.receiveText() + runCatching { + Load().loadOne(types) + }.onFailure { + call.respondText( + text = "Exception while parsing supplied typings:\n${it.stackTraceToString()}", + status = HttpStatusCode.UnprocessableEntity, + ) + return@post + } + call.toBindingArtifacts(bindingsCache, refresh = true, owner = owner, types = types) + call.respondText(text = "$owner:$name:$version") + } } private suspend fun ApplicationCall.toBindingArtifacts( bindingsCache: Cache>>, refresh: Boolean, + owner: String = parameters["owner"]!!, + types: String? = null, ): Map? { - val owner = parameters["owner"]!! val nameAndPath = parameters["name"]!!.split("__") val name = nameAndPath.first() val version = parameters["version"]!! + // we cannot give the types UUID separately from the post handler + // only in the post handler we generate the UUID, but for the other + // handlers the UUID part is already coming through the request as part of the owner + val ownerAndTypesUuid = owner.split("__types__", limit = 2) + val ownerPlain = ownerAndTypesUuid.first() + val typesUuid = + ownerAndTypesUuid + .drop(1) + .takeIf { it.isNotEmpty() } + ?.single() val actionCoords = ActionCoords( - owner = owner, + owner = ownerPlain, name = name, version = version, path = nameAndPath.drop(1).joinToString("/").takeUnless { it.isBlank() }, + typesUuid = typesUuid, ) println("➡️ Requesting ${actionCoords.prettyPrint}") val bindingArtifacts = if (refresh) { - actionCoords.buildVersionArtifacts().also { + actionCoords.buildVersionArtifacts(types ?: typesUuid?.let { "" }).also { bindingsCache.put(actionCoords, Result.of(it)) } } else { bindingsCache - .get(actionCoords) { Result.of(actionCoords.buildVersionArtifacts()) } + .get(actionCoords) { Result.of(actionCoords.buildVersionArtifacts(types ?: typesUuid?.let { "" })) } .getOrNull() } return bindingArtifacts diff --git a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/JarBuilding.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/JarBuilding.kt index 5da351987d..b3a13e26e4 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/JarBuilding.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/JarBuilding.kt @@ -26,9 +26,9 @@ internal data class Jars( val sourcesJar: () -> ByteArray, ) -internal fun ActionCoords.buildJars(): Jars? { +internal fun ActionCoords.buildJars(types: String?): Jars? { val binding = - generateBinding(metadataRevision = NewestForVersion).also { + generateBinding(metadataRevision = NewestForVersion, types = types).also { if (it.isEmpty()) return null } diff --git a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/VersionArtifactsBuilding.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/VersionArtifactsBuilding.kt index aef17b35be..d0c3f80e30 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/VersionArtifactsBuilding.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/VersionArtifactsBuilding.kt @@ -12,8 +12,8 @@ data class JarArtifact( val data: () -> ByteArray, ) : Artifact -fun ActionCoords.buildVersionArtifacts(): Map? { - val jars = buildJars() ?: return null +fun ActionCoords.buildVersionArtifacts(types: String? = null): Map? { + val jars = buildJars(types = types) ?: return null val pom = buildPomFile() val module = buildModuleFile() return mapOf(