From 0769bdc9fdfaa117bd04bbc9221dbf3b53687cbb 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 1/2] feat(server): make it possible to POST custom typings for testing during typing creation --- .../api/action-binding-generator.api | 15 +- .../domain/ActionCoords.kt | 7 +- .../domain/TypingActualSource.kt | 1 + .../generation/Generation.kt | 10 +- .../typing/TypesProviding.kt | 12 +- .../typing/TypesProvidingTest.kt | 154 ++++++++++++++++++ docs/user-guide/using-actions.md | 12 ++ jit-binding-server/build.gradle.kts | 1 + .../jitbindingserver/ActionCoords.kt | 18 +- .../jitbindingserver/ArtifactRoutes.kt | 51 +++++- .../workflows/jitbindingserver/Main.kt | 2 +- .../mavenbinding/ActionCoordsUtils.kt | 2 + .../workflows/mavenbinding/JarBuilding.kt | 4 +- .../mavenbinding/MavenMetadataBuilding.kt | 2 +- .../workflows/mavenbinding/ModuleBuilding.kt | 2 +- .../workflows/mavenbinding/PomBuilding.kt | 2 +- .../mavenbinding/VersionArtifactsBuilding.kt | 7 +- 17 files changed, 274 insertions(+), 28 deletions(-) diff --git a/action-binding-generator/api/action-binding-generator.api b/action-binding-generator/api/action-binding-generator.api index ca56118d20..e188ecffda 100644 --- a/action-binding-generator/api/action-binding-generator.api +++ b/action-binding-generator/api/action-binding-generator.api @@ -1,18 +1,20 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;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 ()Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; public final fun component5 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;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;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;Ljava/lang/String;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; + public final fun component6 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;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;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;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 getSignificantVersion ()Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; + public final fun getTypesUuid ()Ljava/lang/String; public final fun getVersion ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -59,6 +61,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; @@ -85,8 +88,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 682a68c510..0524ede704 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 @@ -15,6 +15,7 @@ public data class ActionCoords( */ val significantVersion: SignificantVersion = FULL, val path: String? = null, + val typesUuid: String? = null, ) /** @@ -23,9 +24,11 @@ public data class ActionCoords( */ public val ActionCoords.isTopLevel: Boolean get() = path == null -public val ActionCoords.prettyPrint: String get() = "$prettyPrintWithoutVersion@$version" +public val ActionCoords.prettyPrint: String get() = "$prettyPrintBase@$version${typesUuid?.let { " (types: $it)" } ?: ""}" -public val ActionCoords.prettyPrintWithoutVersion: String get() = "$owner/$fullName${ +public val ActionCoords.prettyPrintWithoutVersion: String get() = "$prettyPrintBase${typesUuid?.let { " (types: $it)" } ?: ""}" + +private val ActionCoords.prettyPrintBase: String get() = "$owner/$fullName${ significantVersion.takeUnless { it == FULL }?.let { " with $it version" } ?: "" }" 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 6af3e5c84d..f48580eda2 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 @@ -64,11 +64,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() @@ -81,7 +83,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( @@ -92,12 +94,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 46e6eb6344..63477a3860 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 @@ -10,6 +10,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 @@ -24,10 +25,12 @@ private val logger = logger { } 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) @@ -45,6 +48,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, @@ -68,7 +74,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 4d56781e1a..e0c2541f19 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 @@ -152,6 +152,12 @@ class TypesProvidingTest : stored-in-typing-catalog: type: string """.trimIndent() + val custom = + """ + inputs: + custom: + type: string + """.trimIndent() val metadata = """ "versionsWithTypings": @@ -376,6 +382,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", FULL, "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", FULL, "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", FULL, "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 = { @@ -576,6 +710,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 f88ea7208b..3f8a59dfa3 100644 --- a/docs/user-guide/using-actions.md +++ b/docs/user-guide/using-actions.md @@ -74,6 +74,18 @@ There are two ways of configuring typings: a community-maintained place to host the typings. You can contribute or fix typings for your favorite action by sending a PR. +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 there will still be only the property with `_Untyped` suffix and nullability according to required status. For each diff --git a/jit-binding-server/build.gradle.kts b/jit-binding-server/build.gradle.kts index d94dca67d0..abb520ddd3 100644 --- a/jit-binding-server/build.gradle.kts +++ b/jit-binding-server/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation("io.ktor:ktor-server-netty") implementation("io.ktor:ktor-server-call-logging") implementation("io.ktor:ktor-server-call-id") + implementation("it.krzeminski:snakeyaml-engine-kmp:3.0.2") implementation("io.ktor:ktor-server-metrics-micrometer") implementation("io.micrometer:micrometer-registry-prometheus:1.15.0") diff --git a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt index 125f567649..066fe95950 100644 --- a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt +++ b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt @@ -5,8 +5,10 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.Signific import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL import io.ktor.http.Parameters -fun Parameters.extractActionCoords(extractVersion: Boolean): ActionCoords { - val owner = this["owner"]!! +fun Parameters.extractActionCoords( + extractVersion: Boolean, + owner: String = this["owner"]!!, +): ActionCoords { val nameAndPathAndSignificantVersionParts = this["name"]!!.split("___", limit = 2) val nameAndPath = nameAndPathAndSignificantVersionParts.first() val significantVersion = @@ -27,6 +29,16 @@ fun Parameters.extractActionCoords(extractVersion: Boolean): ActionCoords { .joinToString("/") .takeUnless { it.isBlank() } val version = if (extractVersion) this["version"]!! else "irrelevant" + // 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() - return ActionCoords(owner, name, version, significantVersion, path) + return ActionCoords(ownerPlain, name, version, significantVersion, path, typesUuid) } diff --git a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ArtifactRoutes.kt b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ArtifactRoutes.kt index 553e969ee9..046f08e868 100644 --- a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ArtifactRoutes.kt +++ b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ArtifactRoutes.kt @@ -8,23 +8,28 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.prettyPr import io.github.typesafegithub.workflows.mavenbinding.JarArtifact import io.github.typesafegithub.workflows.mavenbinding.TextArtifact import io.github.typesafegithub.workflows.mavenbinding.VersionArtifacts +import io.github.typesafegithub.workflows.mavenbinding.buildVersionArtifacts import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.server.application.ApplicationCall import io.ktor.server.request.httpMethod +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.Routing 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.micrometer.core.instrument.Tag import io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics import io.micrometer.prometheusmetrics.PrometheusMeterRegistry +import it.krzeminski.snakeyaml.engine.kmp.api.Load import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.util.UUID.randomUUID private val logger = logger { } @@ -56,6 +61,7 @@ private fun Route.artifact( ) { headArtifact(bindingsCache, prometheusRegistry, refresh) getArtifact(bindingsCache, prometheusRegistry, refresh) + postArtifact(bindingsCache, prometheusRegistry) } private fun Route.headArtifact( @@ -110,17 +116,57 @@ internal fun prefetchBindingArtifacts( } } +private fun Route.postArtifact( + bindingsCache: LoadingCache, + prometheusRegistry: PrometheusMeterRegistry?, +) { + 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 + } + val typingActualSource = + call + .toBindingArtifacts( + refresh = true, + bindingsCache = bindingsCache, + owner = owner, + types = types, + )?.typingActualSource ?: TypingActualSource.CUSTOM + call.respondText(text = "$owner:$name:$version") + + prometheusRegistry?.incrementArtifactCounter(call, typingActualSource) + } +} + private suspend fun ApplicationCall.toBindingArtifacts( refresh: Boolean, bindingsCache: LoadingCache, + owner: String = parameters["owner"]!!, + types: String? = null, ): VersionArtifacts? { - val actionCoords = parameters.extractActionCoords(extractVersion = true) + val actionCoords = parameters.extractActionCoords(extractVersion = true, owner = owner) logger.info { "➡️ Requesting ${actionCoords.prettyPrint}" } if (refresh) { bindingsCache.invalidate(actionCoords) } - return bindingsCache.get(actionCoords) + return ( + if (types != null) { + bindingsCache.get(actionCoords) { buildVersionArtifacts(actionCoords, types) } + } else { + bindingsCache.get(actionCoords) + } + ) } private fun PrometheusMeterRegistry.incrementArtifactCounter( @@ -141,6 +187,7 @@ private fun PrometheusMeterRegistry.incrementArtifactCounter( when (typingActualSource) { TypingActualSource.ACTION -> "action" TypingActualSource.TYPING_CATALOG -> "typing_catalog" + TypingActualSource.CUSTOM -> "custom" null -> "no_typing" } 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 a77f43e0c4..8b7ffc4407 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 @@ -52,7 +52,7 @@ fun main() { } embeddedServer(Netty, port = 8080) { appModule( - buildVersionArtifacts = ::buildVersionArtifacts, + buildVersionArtifacts = { buildVersionArtifacts(it, it.typesUuid?.let { "" }) }, buildPackageArtifacts = ::buildPackageArtifacts, getGithubAuthToken = ::getGithubAuthToken, ) diff --git a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt index 33dcb41f46..431d31b03c 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt @@ -4,6 +4,8 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCo import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL import io.github.typesafegithub.workflows.actionbindinggenerator.domain.subName +internal val ActionCoords.mavenGroup: String get() = "$owner${typesUuid?.let { "__types__$it" } ?: ""}" + internal val ActionCoords.mavenName: String get() = "$name${subName.replace("/", "__")}${ significantVersion.takeUnless { it == FULL }?.let { "___$it" } ?: "" }" 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 603a3a6c3f..10d8266646 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 @@ -28,9 +28,9 @@ internal data class Jars( val typingActualSource: TypingActualSource?, ) -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/MavenMetadataBuilding.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuilding.kt index ef1fe14994..04a909c683 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuilding.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuilding.kt @@ -35,7 +35,7 @@ internal suspend fun ActionCoords.buildMavenMetadataFile( return """ - $owner + $mavenGroup $mavenName $newest diff --git a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ModuleBuilding.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ModuleBuilding.kt index e2dda2ba8f..5a7bbf4cce 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ModuleBuilding.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ModuleBuilding.kt @@ -17,7 +17,7 @@ internal fun ActionCoords.buildModuleFile( { "formatVersion": "1.1", "component": { - "group": "$owner", + "group": "$mavenGroup", "module": "$mavenName", "version": "$version", "attributes": { diff --git a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/PomBuilding.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/PomBuilding.kt index 071a899bf4..50a9fd9e43 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/PomBuilding.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/PomBuilding.kt @@ -11,7 +11,7 @@ internal fun ActionCoords.buildPomFile() = 4.0.0 - $owner + $mavenGroup $mavenName $version $fullName 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 803d871aa5..c00be90e8f 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 @@ -18,9 +18,12 @@ data class VersionArtifacts( val typingActualSource: TypingActualSource?, ) -fun buildVersionArtifacts(actionCoords: ActionCoords): VersionArtifacts? { +fun buildVersionArtifacts( + actionCoords: ActionCoords, + types: String? = null, +): VersionArtifacts? { with(actionCoords) { - val jars = buildJars() ?: return null + val jars = buildJars(types = types) ?: return null val pom = buildPomFile() val mainJarSize by lazy { jars.mainJar().size } val mainJarMd5Checksum by lazy { jars.mainJar().md5Checksum() } From a031ec96398aa25511cfbda87a0ac65278ded96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Mon, 27 Jan 2025 14:03:32 +0100 Subject: [PATCH 2/2] Also make it possible to supply the action manifest --- .../api/action-binding-generator.api | 8 +- .../generation/Generation.kt | 3 +- .../metadata/MetadataReading.kt | 5 + docs/user-guide/using-actions.md | 8 +- .../jitbindingserver/ArtifactRoutes.kt | 95 +++++++++++++++++-- .../workflows/jitbindingserver/Main.kt | 2 + .../workflows/jitbindingserver/PartData.kt | 20 ++++ .../workflows/mavenbinding/JarBuilding.kt | 7 +- .../mavenbinding/VersionArtifactsBuilding.kt | 3 +- 9 files changed, 133 insertions(+), 18 deletions(-) create mode 100644 jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/PartData.kt diff --git a/action-binding-generator/api/action-binding-generator.api b/action-binding-generator/api/action-binding-generator.api index e188ecffda..ba3df55131 100644 --- a/action-binding-generator/api/action-binding-generator.api +++ b/action-binding-generator/api/action-binding-generator.api @@ -88,8 +88,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/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 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/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;Ljava/lang/String;ILjava/lang/Object;)Ljava/util/List; } public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Input { @@ -160,8 +160,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/met } public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReadingKt { - public static final fun fetchMetadata (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lkotlin/jvm/functions/Function1;)Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata; - public static synthetic fun fetchMetadata$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata; + public static final fun fetchMetadata (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata; + public static synthetic fun fetchMetadata$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata; } public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Output { 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 f48580eda2..d502b2589e 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 @@ -65,8 +65,9 @@ public fun ActionCoords.generateBinding( metadata: Metadata? = null, inputTypings: Pair, TypingActualSource?>? = null, types: String? = null, + explicitMetadata: String? = null, ): List { - val metadataResolved = metadata ?: this.fetchMetadata(metadataRevision) ?: return emptyList() + val metadataResolved = metadata ?: this.fetchMetadata(metadataRevision, explicitMetadata) ?: return emptyList() val metadataProcessed = metadataResolved.removeDeprecatedInputsIfNameClash() val (inputTypingsResolved, typingActualSource) = diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt index 815e720eab..7551beeb00 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt @@ -45,8 +45,13 @@ private fun ActionCoords.actionYamlUrl(gitRef: String) = "https://raw.githubuser public fun ActionCoords.fetchMetadata( metadataRevision: MetadataRevision, + explicitMetadata: String? = null, fetchUri: (URI) -> String = ::fetchUri, ): Metadata? { + if (explicitMetadata != null) { + return yaml.decodeFromString(explicitMetadata) + } + val gitRef = when (metadataRevision) { is CommitHash -> metadataRevision.value diff --git a/docs/user-guide/using-actions.md b/docs/user-guide/using-actions.md index 3f8a59dfa3..881b35568e 100644 --- a/docs/user-guide/using-actions.md +++ b/docs/user-guide/using-actions.md @@ -78,7 +78,7 @@ While developing a typing manifest it might be a good idea to test the result wi 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 +curl -F types=@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 @@ -86,6 +86,12 @@ server and locally as long as you do not delete it from your local Maven reposit the cache period on the server ended requesting the same coordinates will return a binding as if no typing information is available at all. +When writing typings for a new action that is not published yet or a new version with changed inputs / outputs, +you should also provide the new action manifest, that the generation works with that state using +```bash +curl -F actionYaml=@action.yml -F types=@action-types.yml https://bindings.krzeminski.it/foo/bar/vX/bar-vX.pom +``` + 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 there will still be only the property with `_Untyped` suffix and nullability according to required status. For each diff --git a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ArtifactRoutes.kt b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ArtifactRoutes.kt index 046f08e868..3d85b4b8ab 100644 --- a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ArtifactRoutes.kt +++ b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ArtifactRoutes.kt @@ -11,8 +11,10 @@ import io.github.typesafegithub.workflows.mavenbinding.VersionArtifacts import io.github.typesafegithub.workflows.mavenbinding.buildVersionArtifacts import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode +import io.ktor.http.content.asFlow import io.ktor.server.application.ApplicationCall import io.ktor.server.request.httpMethod +import io.ktor.server.request.receiveMultipart import io.ktor.server.request.receiveText import io.ktor.server.response.respondBytes import io.ktor.server.response.respondText @@ -28,9 +30,14 @@ import io.micrometer.prometheusmetrics.PrometheusMeterRegistry import it.krzeminski.snakeyaml.engine.kmp.api.Load import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch import java.util.UUID.randomUUID +private const val METADATA_PARAMETER = "actionYaml" +private const val TYPES_PARAMETER = "types" + private val logger = logger { } typealias CachedVersionArtifact = VersionArtifacts? @@ -124,14 +131,45 @@ private fun Route.postArtifact( 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, - ) + + val (metadata, types) = + runCatching { + val parts = + call + .receiveMultipart() + .asFlow() + .map { it.name to it.asString() } + .toList() + .map { (name, result) -> + name to + when { + result.isSuccess -> result.getOrThrow() + else -> { + call.respondText( + text = HttpStatusCode.InternalServerError.description, + status = HttpStatusCode.InternalServerError, + ) + return@post + } + } + }.associate { it } + + if (parts.keys.any { (it != METADATA_PARAMETER) && (it != TYPES_PARAMETER) }) { + call.respondBadRequest( + text = "Only '$METADATA_PARAMETER' and '$TYPES_PARAMETER' are allowed as form data fields", + ) + return@post + } + if (!parts.containsKey(TYPES_PARAMETER)) { + call.respondBadRequest(text = "'$TYPES_PARAMETER' field is mandatory") + return@post + } + parts[METADATA_PARAMETER] to parts[TYPES_PARAMETER]!! + }.recover { + null to call.receiveText() + }.getOrThrow() + + if (!call.validateMetadata(metadata) || !call.validateTypes(types)) { return@post } val typingActualSource = @@ -141,6 +179,7 @@ private fun Route.postArtifact( bindingsCache = bindingsCache, owner = owner, types = types, + metadata = metadata, )?.typingActualSource ?: TypingActualSource.CUSTOM call.respondText(text = "$owner:$name:$version") @@ -148,11 +187,49 @@ private fun Route.postArtifact( } } +private suspend fun ApplicationCall.validateTypes(types: String): Boolean { + runCatching { + Load().loadOne(types) + }.onFailure { + respondText( + text = "Exception while parsing supplied $TYPES_PARAMETER:\n${it.stackTraceToString()}", + status = HttpStatusCode.UnprocessableEntity, + ) + return false + } + return true +} + +private suspend fun ApplicationCall.validateMetadata(metadata: String?): Boolean { + var result = true + if (metadata != null) { + if (metadata.isEmpty()) { + respondText( + text = "Supplied $METADATA_PARAMETER is empty", + status = HttpStatusCode.UnprocessableEntity, + ) + result = false + } + + runCatching { + Load().loadOne(metadata) + }.onFailure { + respondText( + text = "Exception while parsing supplied $METADATA_PARAMETER:\n${it.stackTraceToString()}", + status = HttpStatusCode.UnprocessableEntity, + ) + result = false + } + } + return result +} + private suspend fun ApplicationCall.toBindingArtifacts( refresh: Boolean, bindingsCache: LoadingCache, owner: String = parameters["owner"]!!, types: String? = null, + metadata: String? = null, ): VersionArtifacts? { val actionCoords = parameters.extractActionCoords(extractVersion = true, owner = owner) @@ -162,7 +239,7 @@ private suspend fun ApplicationCall.toBindingArtifacts( } return ( if (types != null) { - bindingsCache.get(actionCoords) { buildVersionArtifacts(actionCoords, types) } + bindingsCache.get(actionCoords) { buildVersionArtifacts(actionCoords, types, metadata) } } else { bindingsCache.get(actionCoords) } 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 8b7ffc4407..b18535329d 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 @@ -106,3 +106,5 @@ private fun buildMetadataCache( val deliverOnRefreshRoute = System.getenv("GWKT_DELIVER_ON_REFRESH").toBoolean() suspend fun ApplicationCall.respondNotFound() = respondText(text = "Not found", status = HttpStatusCode.NotFound) + +suspend fun ApplicationCall.respondBadRequest(text: String) = respondText(text, status = HttpStatusCode.BadRequest) diff --git a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/PartData.kt b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/PartData.kt new file mode 100644 index 0000000000..3bf310379a --- /dev/null +++ b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/PartData.kt @@ -0,0 +1,20 @@ +package io.github.typesafegithub.workflows.jitbindingserver + +import io.github.oshai.kotlinlogging.KotlinLogging.logger +import io.ktor.http.content.PartData +import io.ktor.utils.io.readRemaining +import io.ktor.utils.io.readText + +private val logger = logger { } + +internal suspend fun PartData.asString() = + runCatching { + when (this) { + is PartData.FileItem -> provider().readRemaining().readText() + is PartData.FormItem -> value + else -> { + logger.error { "Unexpected part data ${this::class.simpleName}" } + error("Unexpected part data ${this::class.simpleName}") + } + } + } 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 10d8266646..863856a556 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 @@ -28,9 +28,12 @@ internal data class Jars( val typingActualSource: TypingActualSource?, ) -internal fun ActionCoords.buildJars(types: String?): Jars? { +internal fun ActionCoords.buildJars( + types: String?, + metadata: String?, +): Jars? { val binding = - generateBinding(metadataRevision = NewestForVersion, types = types).also { + generateBinding(metadataRevision = NewestForVersion, types = types, explicitMetadata = metadata).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 c00be90e8f..7f5e0d9b02 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 @@ -21,9 +21,10 @@ data class VersionArtifacts( fun buildVersionArtifacts( actionCoords: ActionCoords, types: String? = null, + metadata: String? = null, ): VersionArtifacts? { with(actionCoords) { - val jars = buildJars(types = types) ?: return null + val jars = buildJars(types = types, metadata = metadata) ?: return null val pom = buildPomFile() val mainJarSize by lazy { jars.mainJar().size } val mainJarMd5Checksum by lazy { jars.mainJar().md5Checksum() }