Skip to content

Commit a3231c5

Browse files
committed
Also make it possible to supply the action manifest
1 parent 12b553a commit a3231c5

File tree

9 files changed

+133
-18
lines changed

9 files changed

+133
-18
lines changed

action-binding-generator/api/action-binding-generator.api

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/gen
8888
}
8989

9090
public final class io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationKt {
91-
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;
92-
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;
91+
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;
92+
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;
9393
}
9494

9595
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Input {
@@ -160,8 +160,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/met
160160
}
161161

162162
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReadingKt {
163-
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;
164-
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;
163+
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;
164+
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;
165165
}
166166

167167
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Output {

action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ public fun ActionCoords.generateBinding(
6565
metadata: Metadata? = null,
6666
inputTypings: Pair<Map<String, Typing>, TypingActualSource?>? = null,
6767
types: String? = null,
68+
explicitMetadata: String? = null,
6869
): List<ActionBinding> {
69-
val metadataResolved = metadata ?: this.fetchMetadata(metadataRevision) ?: return emptyList()
70+
val metadataResolved = metadata ?: this.fetchMetadata(metadataRevision, explicitMetadata) ?: return emptyList()
7071
val metadataProcessed = metadataResolved.removeDeprecatedInputsIfNameClash()
7172

7273
val (inputTypingsResolved, typingActualSource) =

action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,13 @@ private fun ActionCoords.actionYamlUrl(gitRef: String) = "https://raw.githubuser
4545

4646
public fun ActionCoords.fetchMetadata(
4747
metadataRevision: MetadataRevision,
48+
explicitMetadata: String? = null,
4849
fetchUri: (URI) -> String = ::fetchUri,
4950
): Metadata? {
51+
if (explicitMetadata != null) {
52+
return yaml.decodeFromString(explicitMetadata)
53+
}
54+
5055
val gitRef =
5156
when (metadataRevision) {
5257
is CommitHash -> metadataRevision.value

docs/user-guide/using-actions.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,20 @@ While developing a typing manifest it might be a good idea to test the result wi
7878
release the action in question or merge a PR in the catalog. For this you can `POST` the typing manifest you have
7979
on disk to the binding server using any valid URL for the action in question, for example using
8080
```bash
81-
curl --data-binary @action-types.yml https://bindings.krzeminski.it/pbrisbin/setup-tool-action/v2/setup-tool-action-v2.pom
81+
curl -F types=@action-types.yml https://bindings.krzeminski.it/pbrisbin/setup-tool-action/v2/setup-tool-action-v2.pom
8282
```
8383
The binding server generates a binding with only the given type manifest and answer with some unique coordinates
8484
that you can use in a test workflow script. The binding will be available the normal cache time on the binding
8585
server and locally as long as you do not delete it from your local Maven repository where it is cached. After
8686
the cache period on the server ended requesting the same coordinates will return a binding as if no typing
8787
information is available at all.
8888

89+
When writing typings for a new action that is not published yet or a new version with changed inputs / outputs,
90+
you should also provide the new action manifest, that the generation works with that state using
91+
```bash
92+
curl -F [email protected] -F [email protected] https://bindings.krzeminski.it/foo/bar/vX/bar-vX.pom
93+
```
94+
8995
Once there are any typings in place for the action, the `_Untyped` suffixed class is marked `@Deprecated`, and a class
9096
without that suffix is created additionally. In that class for each input that does not have type information available
9197
there will still be only the property with `_Untyped` suffix and nullability according to required status. For each

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

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import io.github.typesafegithub.workflows.mavenbinding.VersionArtifacts
1111
import io.github.typesafegithub.workflows.mavenbinding.buildVersionArtifacts
1212
import io.ktor.http.ContentType
1313
import io.ktor.http.HttpStatusCode
14+
import io.ktor.http.content.asFlow
1415
import io.ktor.server.application.ApplicationCall
1516
import io.ktor.server.request.httpMethod
17+
import io.ktor.server.request.receiveMultipart
1618
import io.ktor.server.request.receiveText
1719
import io.ktor.server.response.respondBytes
1820
import io.ktor.server.response.respondText
@@ -28,9 +30,14 @@ import io.micrometer.prometheusmetrics.PrometheusMeterRegistry
2830
import it.krzeminski.snakeyaml.engine.kmp.api.Load
2931
import kotlinx.coroutines.CoroutineScope
3032
import kotlinx.coroutines.Dispatchers
33+
import kotlinx.coroutines.flow.map
34+
import kotlinx.coroutines.flow.toList
3135
import kotlinx.coroutines.launch
3236
import java.util.UUID.randomUUID
3337

38+
private const val METADATA_PARAMETER = "actionYaml"
39+
private const val TYPES_PARAMETER = "types"
40+
3441
private val logger = logger { }
3542

3643
typealias CachedVersionArtifact = VersionArtifacts?
@@ -124,14 +131,45 @@ private fun Route.postArtifact(
124131
val owner = "${call.parameters["owner"]}__types__${randomUUID()}"
125132
val name = call.parameters["name"]!!
126133
val version = call.parameters["version"]!!
127-
val types = call.receiveText()
128-
runCatching {
129-
Load().loadOne(types)
130-
}.onFailure {
131-
call.respondText(
132-
text = "Exception while parsing supplied typings:\n${it.stackTraceToString()}",
133-
status = HttpStatusCode.UnprocessableEntity,
134-
)
134+
135+
val (metadata, types) =
136+
runCatching {
137+
val parts =
138+
call
139+
.receiveMultipart()
140+
.asFlow()
141+
.map { it.name to it.asString() }
142+
.toList()
143+
.map { (name, result) ->
144+
name to
145+
when {
146+
result.isSuccess -> result.getOrThrow()
147+
else -> {
148+
call.respondText(
149+
text = HttpStatusCode.InternalServerError.description,
150+
status = HttpStatusCode.InternalServerError,
151+
)
152+
return@post
153+
}
154+
}
155+
}.associate { it }
156+
157+
if (parts.keys.any { (it != METADATA_PARAMETER) && (it != TYPES_PARAMETER) }) {
158+
call.respondBadRequest(
159+
text = "Only '$METADATA_PARAMETER' and '$TYPES_PARAMETER' are allowed as form data fields",
160+
)
161+
return@post
162+
}
163+
if (!parts.containsKey(TYPES_PARAMETER)) {
164+
call.respondBadRequest(text = "'$TYPES_PARAMETER' field is mandatory")
165+
return@post
166+
}
167+
parts[METADATA_PARAMETER] to parts[TYPES_PARAMETER]!!
168+
}.recover {
169+
null to call.receiveText()
170+
}.getOrThrow()
171+
172+
if (!call.validateMetadata(metadata) || !call.validateTypes(types)) {
135173
return@post
136174
}
137175
val typingActualSource =
@@ -141,18 +179,57 @@ private fun Route.postArtifact(
141179
bindingsCache = bindingsCache,
142180
owner = owner,
143181
types = types,
182+
metadata = metadata,
144183
)?.typingActualSource ?: TypingActualSource.CUSTOM
145184
call.respondText(text = "$owner:$name:$version")
146185

147186
prometheusRegistry?.incrementArtifactCounter(call, typingActualSource)
148187
}
149188
}
150189

190+
private suspend fun ApplicationCall.validateTypes(types: String): Boolean {
191+
runCatching {
192+
Load().loadOne(types)
193+
}.onFailure {
194+
respondText(
195+
text = "Exception while parsing supplied $TYPES_PARAMETER:\n${it.stackTraceToString()}",
196+
status = HttpStatusCode.UnprocessableEntity,
197+
)
198+
return false
199+
}
200+
return true
201+
}
202+
203+
private suspend fun ApplicationCall.validateMetadata(metadata: String?): Boolean {
204+
var result = true
205+
if (metadata != null) {
206+
if (metadata.isEmpty()) {
207+
respondText(
208+
text = "Supplied $METADATA_PARAMETER is empty",
209+
status = HttpStatusCode.UnprocessableEntity,
210+
)
211+
result = false
212+
}
213+
214+
runCatching {
215+
Load().loadOne(metadata)
216+
}.onFailure {
217+
respondText(
218+
text = "Exception while parsing supplied $METADATA_PARAMETER:\n${it.stackTraceToString()}",
219+
status = HttpStatusCode.UnprocessableEntity,
220+
)
221+
result = false
222+
}
223+
}
224+
return result
225+
}
226+
151227
private suspend fun ApplicationCall.toBindingArtifacts(
152228
refresh: Boolean,
153229
bindingsCache: LoadingCache<ActionCoords, CachedVersionArtifact>,
154230
owner: String = parameters["owner"]!!,
155231
types: String? = null,
232+
metadata: String? = null,
156233
): VersionArtifacts? {
157234
val actionCoords = parameters.extractActionCoords(extractVersion = true, owner = owner)
158235

@@ -162,7 +239,7 @@ private suspend fun ApplicationCall.toBindingArtifacts(
162239
}
163240
return (
164241
if (types != null) {
165-
bindingsCache.get(actionCoords) { buildVersionArtifacts(actionCoords, types) }
242+
bindingsCache.get(actionCoords) { buildVersionArtifacts(actionCoords, types, metadata) }
166243
} else {
167244
bindingsCache.get(actionCoords)
168245
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,5 @@ private fun buildMetadataCache(
105105
val deliverOnRefreshRoute = System.getenv("GWKT_DELIVER_ON_REFRESH").toBoolean()
106106

107107
suspend fun ApplicationCall.respondNotFound() = respondText(text = "Not found", status = HttpStatusCode.NotFound)
108+
109+
suspend fun ApplicationCall.respondBadRequest(text: String) = respondText(text, status = HttpStatusCode.BadRequest)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.github.typesafegithub.workflows.jitbindingserver
2+
3+
import io.github.oshai.kotlinlogging.KotlinLogging.logger
4+
import io.ktor.http.content.PartData
5+
import io.ktor.utils.io.readRemaining
6+
import io.ktor.utils.io.readText
7+
8+
private val logger = logger { }
9+
10+
internal suspend fun PartData.asString() =
11+
runCatching {
12+
when (this) {
13+
is PartData.FileItem -> provider().readRemaining().readText()
14+
is PartData.FormItem -> value
15+
else -> {
16+
logger.error { "Unexpected part data ${this::class.simpleName}" }
17+
error("Unexpected part data ${this::class.simpleName}")
18+
}
19+
}
20+
}

maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/JarBuilding.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ internal data class Jars(
2828
val typingActualSource: TypingActualSource?,
2929
)
3030

31-
internal fun ActionCoords.buildJars(types: String?): Jars? {
31+
internal fun ActionCoords.buildJars(
32+
types: String?,
33+
metadata: String?,
34+
): Jars? {
3235
val binding =
33-
generateBinding(metadataRevision = NewestForVersion, types = types).also {
36+
generateBinding(metadataRevision = NewestForVersion, types = types, explicitMetadata = metadata).also {
3437
if (it.isEmpty()) return null
3538
}
3639

maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/VersionArtifactsBuilding.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ data class VersionArtifacts(
2121
fun buildVersionArtifacts(
2222
actionCoords: ActionCoords,
2323
types: String? = null,
24+
metadata: String? = null,
2425
): VersionArtifacts? {
2526
with(actionCoords) {
26-
val jars = buildJars(types = types) ?: return null
27+
val jars = buildJars(types = types, metadata = metadata) ?: return null
2728
val pom = buildPomFile()
2829
val mainJarSize by lazy { jars.mainJar().size }
2930
val mainJarMd5Checksum by lazy { jars.mainJar().md5Checksum() }

0 commit comments

Comments
 (0)