Skip to content

Commit 1f808fe

Browse files
committed
Also make it possible to supply the action manifest
1 parent 1e82a53 commit 1f808fe

File tree

8 files changed

+136
-18
lines changed

8 files changed

+136
-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: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import io.github.typesafegithub.workflows.mavenbinding.TextArtifact
1010
import io.github.typesafegithub.workflows.mavenbinding.buildVersionArtifacts
1111
import io.ktor.http.ContentType
1212
import io.ktor.http.HttpStatusCode
13+
import io.ktor.http.content.asFlow
1314
import io.ktor.server.application.ApplicationCall
1415
import io.ktor.server.request.httpMethod
16+
import io.ktor.server.request.receiveMultipart
1517
import io.ktor.server.request.receiveText
1618
import io.ktor.server.response.respondBytes
1719
import io.ktor.server.response.respondText
@@ -27,9 +29,14 @@ import io.micrometer.prometheusmetrics.PrometheusMeterRegistry
2729
import it.krzeminski.snakeyaml.engine.kmp.api.Load
2830
import kotlinx.coroutines.CoroutineScope
2931
import kotlinx.coroutines.Dispatchers
32+
import kotlinx.coroutines.flow.map
33+
import kotlinx.coroutines.flow.toList
3034
import kotlinx.coroutines.launch
3135
import java.util.UUID.randomUUID
3236

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

3542
typealias CachedVersionArtifact = Map<String, Artifact>?
@@ -123,33 +130,108 @@ private fun Route.postArtifact(
123130
val owner = "${call.parameters["owner"]}__types__${randomUUID()}"
124131
val name = call.parameters["name"]!!
125132
val version = call.parameters["version"]!!
126-
val types = call.receiveText()
127-
runCatching {
128-
Load().loadOne(types)
129-
}.onFailure {
130-
call.respondText(
131-
text = "Exception while parsing supplied typings:\n${it.stackTraceToString()}",
132-
status = HttpStatusCode.UnprocessableEntity,
133-
)
133+
134+
val (metadata, types) =
135+
runCatching {
136+
val parts =
137+
call
138+
.receiveMultipart()
139+
.asFlow()
140+
.map { it.name to it.asString() }
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 (!call.validateMetadata(metadata) || !call.validateTypes(types)) {
134176
return@post
135177
}
178+
136179
call.toBindingArtifacts(
137180
refresh = true,
138181
bindingsCache = bindingsCache,
139182
owner = owner,
140183
types = types,
184+
metadata = metadata,
141185
)
142186
call.respondText(text = "$owner:$name:$version")
143187

144188
prometheusRegistry?.incrementArtifactCounter(call)
145189
}
146190
}
147191

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

@@ -159,7 +241,7 @@ private suspend fun ApplicationCall.toBindingArtifacts(
159241
}
160242
return (
161243
if (types != null) {
162-
bindingsCache.get(actionCoords) { buildVersionArtifacts(actionCoords, types) }
244+
bindingsCache.get(actionCoords) { buildVersionArtifacts(actionCoords, types, metadata) }
163245
} else {
164246
bindingsCache.get(actionCoords)
165247
}
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
@@ -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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ data class JarArtifact(
1515
fun buildVersionArtifacts(
1616
actionCoords: ActionCoords,
1717
types: String? = null,
18+
metadata: String? = null,
1819
): Map<String, Artifact>? {
1920
with(actionCoords) {
20-
val jars = buildJars(types = types) ?: return null
21+
val jars = buildJars(types = types, metadata = metadata) ?: return null
2122
val pom = buildPomFile()
2223
val mainJarSize by lazy { jars.mainJar().size }
2324
val mainJarMd5Checksum by lazy { jars.mainJar().md5Checksum() }

0 commit comments

Comments
 (0)