Skip to content

Commit 36b7981

Browse files
committed
Also make it possible to supply the action manifest
1 parent 25b3d62 commit 36b7981

File tree

7 files changed

+134
-20
lines changed

7 files changed

+134
-20
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: 106 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ 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.server.application.ApplicationCall
1618
import io.ktor.server.request.httpMethod
19+
import io.ktor.server.request.receiveMultipart
1720
import io.ktor.server.request.receiveText
1821
import io.ktor.server.response.respondBytes
1922
import io.ktor.server.response.respondText
@@ -23,13 +26,20 @@ import io.ktor.server.routing.get
2326
import io.ktor.server.routing.head
2427
import io.ktor.server.routing.post
2528
import io.ktor.server.routing.route
29+
import io.ktor.utils.io.readRemaining
30+
import io.ktor.utils.io.readText
2631
import io.micrometer.core.instrument.Tag
2732
import io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics
2833
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry
2934
import it.krzeminski.snakeyaml.engine.kmp.api.Load
35+
import kotlinx.coroutines.flow.map
36+
import kotlinx.coroutines.flow.toList
3037
import java.util.UUID.randomUUID
3138
import kotlin.time.Duration.Companion.hours
3239

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

3545
typealias ArtifactResult = Result<Map<String, Artifact>>
@@ -112,27 +122,113 @@ private fun Route.postArtifact(prometheusRegistry: PrometheusMeterRegistry) {
112122
val owner = "${call.parameters["owner"]}__types__${randomUUID()}"
113123
val name = call.parameters["name"]!!
114124
val version = call.parameters["version"]!!
115-
val types = call.receiveText()
116-
runCatching {
117-
Load().loadOne(types)
118-
}.onFailure {
119-
call.respondText(
120-
text = "Exception while parsing supplied typings:\n${it.stackTraceToString()}",
121-
status = HttpStatusCode.UnprocessableEntity,
122-
)
125+
126+
val (metadata, types) =
127+
runCatching {
128+
val parts =
129+
call
130+
.receiveMultipart()
131+
.asFlow()
132+
.map { it.name to it.asString() }
133+
.toList()
134+
.map { (name, result) ->
135+
name to
136+
when {
137+
result.isSuccess -> result.getOrThrow()
138+
else -> {
139+
call.respondText(
140+
text = HttpStatusCode.InternalServerError.description,
141+
status = HttpStatusCode.InternalServerError,
142+
)
143+
return@post
144+
}
145+
}
146+
}.associate { it }
147+
148+
if (parts.keys.any { (it != METADATA_PARAMETER) && (it != TYPES_PARAMETER) }) {
149+
call.respondText(
150+
text = "Only '$METADATA_PARAMETER' and '$TYPES_PARAMETER' are allowed as form data fields",
151+
status = HttpStatusCode.BadRequest,
152+
)
153+
return@post
154+
}
155+
if (!parts.containsKey(TYPES_PARAMETER)) {
156+
call.respondText(
157+
text = "'$TYPES_PARAMETER' field is mandatory",
158+
status = HttpStatusCode.BadRequest,
159+
)
160+
return@post
161+
}
162+
parts[METADATA_PARAMETER] to parts[TYPES_PARAMETER]!!
163+
}.recover {
164+
null to call.receiveText()
165+
}.getOrThrow()
166+
167+
if (!call.validateMetadata(metadata) || !call.validateTypes(types)) {
123168
return@post
124169
}
125-
call.toBindingArtifacts(refresh = true, owner = owner, types = types)
170+
171+
call.toBindingArtifacts(refresh = true, owner = owner, types = types, metadata = metadata)
126172
call.respondText(text = "$owner:$name:$version")
127173

128174
incrementArtifactCounter(prometheusRegistry, call)
129175
}
130176
}
131177

178+
private suspend fun PartData.asString() =
179+
runCatching {
180+
when (this) {
181+
is PartData.FileItem -> provider().readRemaining().readText()
182+
is PartData.FormItem -> value
183+
else -> {
184+
logger.error { "Unexpected part data ${this::class.simpleName}" }
185+
error("Unexpected part data ${this::class.simpleName}")
186+
}
187+
}
188+
}
189+
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+
132227
private suspend fun ApplicationCall.toBindingArtifacts(
133228
refresh: Boolean,
134229
owner: String = parameters["owner"]!!,
135230
types: String? = null,
231+
metadata: String? = null,
136232
): Map<String, Artifact>? {
137233
val actionCoords = parameters.extractActionCoords(extractVersion = true, owner = owner)
138234

@@ -142,7 +238,7 @@ private suspend fun ApplicationCall.toBindingArtifacts(
142238
}
143239
return (
144240
if (types != null) {
145-
bindingsCache.get(actionCoords) { runCatching { actionCoords.buildVersionArtifacts(types)!! } }
241+
bindingsCache.get(actionCoords) { runCatching { actionCoords.buildVersionArtifacts(types, metadata)!! } }
146242
} else {
147243
bindingsCache.get(actionCoords)
148244
}

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)