Skip to content

Commit aa57f7c

Browse files
committed
Also make it possible to supply the action manifest
1 parent 2af29cb commit aa57f7c

File tree

7 files changed

+131
-17
lines changed

7 files changed

+131
-17
lines changed

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

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

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

9494
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Input {
@@ -161,8 +161,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/met
161161
}
162162

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

168168
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 actionYaml=@action.yml -F types=@action-types.yml 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: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ import io.github.typesafegithub.workflows.mavenbinding.TextArtifact
1212
import io.github.typesafegithub.workflows.mavenbinding.buildVersionArtifacts
1313
import io.ktor.http.ContentType
1414
import io.ktor.http.HttpStatusCode
15+
import io.ktor.http.content.PartData
16+
import io.ktor.http.content.asFlow
1517
import io.ktor.http.parameters
1618
import io.ktor.server.application.ApplicationCall
1719
import io.ktor.server.request.httpMethod
20+
import io.ktor.server.request.receiveMultipart
1821
import io.ktor.server.request.receiveText
1922
import io.ktor.server.response.respondBytes
2023
import io.ktor.server.response.respondText
@@ -24,13 +27,20 @@ import io.ktor.server.routing.get
2427
import io.ktor.server.routing.head
2528
import io.ktor.server.routing.post
2629
import io.ktor.server.routing.route
30+
import io.ktor.utils.io.readRemaining
31+
import io.ktor.utils.io.readText
2732
import io.micrometer.core.instrument.Tag
2833
import io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics
2934
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry
3035
import it.krzeminski.snakeyaml.engine.kmp.api.Load
36+
import kotlinx.coroutines.flow.map
37+
import kotlinx.coroutines.flow.toList
3138
import java.util.UUID.randomUUID
3239
import kotlin.time.Duration.Companion.hours
3340

41+
private const val METADATA_PARAMETER = "actionYaml"
42+
private const val TYPES_PARAMETER = "types"
43+
3444
private val logger = logger { }
3545

3646
typealias ArtifactResult = Result<Map<String, Artifact>>
@@ -109,17 +119,90 @@ private fun Route.postArtifact(prometheusRegistry: PrometheusMeterRegistry) {
109119
val owner = "${call.parameters["owner"]}__types__${randomUUID()}"
110120
val name = call.parameters["name"]!!
111121
val version = call.parameters["version"]!!
112-
val types = call.receiveText()
122+
123+
val (metadata, types) =
124+
runCatching {
125+
val parts =
126+
call
127+
.receiveMultipart()
128+
.asFlow()
129+
.map {
130+
it.name to
131+
runCatching {
132+
when (it) {
133+
is PartData.FileItem -> it.provider().readRemaining().readText()
134+
is PartData.FormItem -> it.value
135+
else -> {
136+
logger.error { "Unexpected part data ${it::class.simpleName}" }
137+
error("Unexpected part data ${it::class.simpleName}")
138+
}
139+
}
140+
}
141+
}.toList()
142+
.map { (name, result) ->
143+
name to
144+
when {
145+
result.isSuccess -> result.getOrThrow()
146+
else -> {
147+
call.respondText(
148+
text = HttpStatusCode.InternalServerError.description,
149+
status = HttpStatusCode.InternalServerError,
150+
)
151+
return@post
152+
}
153+
}
154+
}.associate { it }
155+
156+
if (parts.keys.any { (it != METADATA_PARAMETER) && (it != TYPES_PARAMETER) }) {
157+
call.respondText(
158+
text = "Only '$METADATA_PARAMETER' and '$TYPES_PARAMETER' are allowed as form data fields",
159+
status = HttpStatusCode.BadRequest,
160+
)
161+
return@post
162+
}
163+
if (!parts.containsKey(TYPES_PARAMETER)) {
164+
call.respondText(
165+
text = "'$TYPES_PARAMETER' field is mandatory",
166+
status = HttpStatusCode.BadRequest,
167+
)
168+
return@post
169+
}
170+
parts[METADATA_PARAMETER] to parts[TYPES_PARAMETER]!!
171+
}.recover {
172+
null to call.receiveText()
173+
}.getOrThrow()
174+
175+
if (metadata != null) {
176+
if (metadata.isEmpty()) {
177+
call.respondText(
178+
text = "Supplied $METADATA_PARAMETER is empty",
179+
status = HttpStatusCode.UnprocessableEntity,
180+
)
181+
return@post
182+
}
183+
184+
runCatching {
185+
Load().loadOne(metadata)
186+
}.onFailure {
187+
call.respondText(
188+
text = "Exception while parsing supplied $METADATA_PARAMETER:\n${it.stackTraceToString()}",
189+
status = HttpStatusCode.UnprocessableEntity,
190+
)
191+
return@post
192+
}
193+
}
194+
113195
runCatching {
114196
Load().loadOne(types)
115197
}.onFailure {
116198
call.respondText(
117-
text = "Exception while parsing supplied typings:\n${it.stackTraceToString()}",
199+
text = "Exception while parsing supplied $TYPES_PARAMETER:\n${it.stackTraceToString()}",
118200
status = HttpStatusCode.UnprocessableEntity,
119201
)
120202
return@post
121203
}
122-
call.toBindingArtifacts(refresh = true, owner = owner, types = types)
204+
205+
call.toBindingArtifacts(refresh = true, owner = owner, types = types, metadata = metadata)
123206
call.respondText(text = "$owner:$name:$version")
124207

125208
incrementArtifactCounter(prometheusRegistry, call)
@@ -130,16 +213,29 @@ private suspend fun ApplicationCall.toBindingArtifacts(
130213
refresh: Boolean,
131214
owner: String = parameters["owner"]!!,
132215
types: String? = null,
216+
metadata: String? = null,
133217
): Map<String, Artifact>? {
134218
val actionCoords = parameters.extractActionCoords(extractVersion = true, owner = owner)
135219

136220
logger.info { "➡️ Requesting ${actionCoords.prettyPrint}" }
137221
return if (refresh) {
138-
actionCoords.buildVersionArtifacts(types ?: actionCoords.typesUuid?.let { "" }).also {
139-
bindingsCache.put(actionCoords, runCatching { it!! })
140-
}
222+
actionCoords
223+
.buildVersionArtifacts(
224+
types ?: actionCoords.typesUuid?.let { "" },
225+
metadata,
226+
).also {
227+
bindingsCache.put(actionCoords, runCatching { it!! })
228+
}
141229
} else {
142-
bindingsCache.get(actionCoords) { runCatching { actionCoords.buildVersionArtifacts(types ?: actionCoords.typesUuid?.let { "" })!! } }.getOrNull()
230+
bindingsCache
231+
.get(actionCoords) {
232+
runCatching {
233+
actionCoords.buildVersionArtifacts(
234+
types ?: actionCoords.typesUuid?.let { "" },
235+
metadata,
236+
)!!
237+
}
238+
}.getOrNull()
143239
}
144240
}
145241

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
@@ -26,9 +26,12 @@ internal data class Jars(
2626
val sourcesJar: () -> ByteArray,
2727
)
2828

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

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ data class JarArtifact(
1212
val data: () -> ByteArray,
1313
) : Artifact
1414

15-
fun ActionCoords.buildVersionArtifacts(types: String? = null): Map<String, Artifact>? {
16-
val jars = buildJars(types = types) ?: return null
15+
fun ActionCoords.buildVersionArtifacts(
16+
types: String? = null,
17+
metadata: String? = null,
18+
): Map<String, Artifact>? {
19+
val jars = buildJars(types = types, metadata = metadata) ?: return null
1720
val pom = buildPomFile()
1821
val module = buildModuleFile()
1922
return mapOf(

0 commit comments

Comments
 (0)