Skip to content

Commit 7cb9a46

Browse files
committed
feat(server): make it possible to POST custom typings for testing during typing creation
1 parent 1dc7506 commit 7cb9a46

File tree

11 files changed

+241
-28
lines changed

11 files changed

+241
-28
lines changed

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords {
2-
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
3-
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
2+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
3+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
44
public final fun component1 ()Ljava/lang/String;
55
public final fun component2 ()Ljava/lang/String;
66
public final fun component3 ()Ljava/lang/String;
77
public final fun component4 ()Ljava/lang/String;
8-
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;
9-
public static synthetic fun copy$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;
8+
public final fun component5 ()Ljava/lang/String;
9+
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;
10+
public static synthetic fun copy$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;
1011
public fun equals (Ljava/lang/Object;)Z
1112
public final fun getName ()Ljava/lang/String;
1213
public final fun getOwner ()Ljava/lang/String;
1314
public final fun getPath ()Ljava/lang/String;
15+
public final fun getTypesUuid ()Ljava/lang/String;
1416
public final fun getVersion ()Ljava/lang/String;
1517
public fun hashCode ()I
1618
public fun toString ()Ljava/lang/String;
@@ -46,6 +48,7 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/dom
4648

4749
public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource : java/lang/Enum {
4850
public static final field ACTION Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource;
51+
public static final field CUSTOM Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource;
4952
public static final field TYPING_CATALOG Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource;
5053
public static fun getEntries ()Lkotlin/enums/EnumEntries;
5154
public static fun valueOf (Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource;
@@ -72,8 +75,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/gen
7275
}
7376

7477
public final class io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationKt {
75-
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;
76-
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;
78+
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;
79+
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;
7780
}
7881

7982
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Input {

action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ public data class ActionCoords(
55
val name: String,
66
val version: String,
77
val path: String? = null,
8+
val typesUuid: String? = null,
89
)
910

1011
/**
@@ -13,7 +14,7 @@ public data class ActionCoords(
1314
*/
1415
public val ActionCoords.isTopLevel: Boolean get() = path == null
1516

16-
public val ActionCoords.prettyPrint: String get() = "$owner/$fullName@$version"
17+
public val ActionCoords.prettyPrint: String get() = "$owner/$fullName@$version${typesUuid?.let { " (types: $it)" } ?: ""}"
1718

1819
/**
1920
* For most actions, it's empty.

action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.domain
33
public enum class TypingActualSource {
44
ACTION,
55
TYPING_CATALOG,
6+
CUSTOM,
67
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ public fun ActionCoords.generateBinding(
6161
metadataRevision: MetadataRevision,
6262
metadata: Metadata? = null,
6363
inputTypings: Pair<Map<String, Typing>, TypingActualSource?>? = null,
64+
types: String? = null,
6465
): List<ActionBinding> {
6566
val metadataResolved = metadata ?: this.fetchMetadata(metadataRevision) ?: return emptyList()
6667
val metadataProcessed = metadataResolved.removeDeprecatedInputsIfNameClash()
6768

68-
val inputTypingsResolved = inputTypings ?: this.provideTypes(metadataRevision)
69+
val (inputTypingsResolved, typingActualSource) =
70+
inputTypings ?: this.provideTypes(metadataRevision, types = types)
6971

7072
val packageName = owner.toKotlinPackageName()
7173
val className = this.buildActionClassName()
@@ -78,7 +80,7 @@ public fun ActionCoords.generateBinding(
7880
emptyMap(),
7981
classNameUntyped,
8082
untypedClass = true,
81-
replaceWith = inputTypingsResolved.second?.let { CodeBlock.of("ReplaceWith(%S)", className) },
83+
replaceWith = typingActualSource?.let { CodeBlock.of("ReplaceWith(%S)", className) },
8284
)
8385

8486
return listOfNotNull(
@@ -89,12 +91,12 @@ public fun ActionCoords.generateBinding(
8991
packageName = packageName,
9092
typingActualSource = null,
9193
),
92-
inputTypingsResolved.second?.let {
94+
typingActualSource?.let {
9395
val actionBindingSourceCode =
9496
generateActionBindingSourceCode(
9597
metadata = metadataProcessed,
9698
coords = this,
97-
inputTypings = inputTypingsResolved.first,
99+
inputTypings = inputTypingsResolved,
98100
className = className,
99101
)
100102
ActionBinding(

action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.Metadata
77
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.NewestForVersion
88
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource
99
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.ACTION
10+
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.CUSTOM
1011
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.TYPING_CATALOG
1112
import io.github.typesafegithub.workflows.actionbindinggenerator.domain.subName
1213
import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.fetchUri
@@ -20,10 +21,12 @@ import java.net.URI
2021
internal fun ActionCoords.provideTypes(
2122
metadataRevision: MetadataRevision,
2223
fetchUri: (URI) -> String = ::fetchUri,
24+
types: String? = null,
2325
): Pair<Map<String, Typing>, TypingActualSource?> =
2426
(
25-
this.fetchTypingMetadata(metadataRevision, fetchUri)
26-
?: this.toMajorVersion().fetchFromTypingsFromCatalog(fetchUri)
27+
customTypingMetadata(types)
28+
?: this.fetchTypingMetadata(metadataRevision, fetchUri)
29+
?: this.toMajorVersion().fetchTypingsFromCatalog(fetchUri)
2730
)?.let { Pair(it.first.toTypesMap(), it.second) }
2831
?: Pair(emptyMap(), null)
2932

@@ -41,6 +44,9 @@ private fun ActionCoords.catalogMetadata() =
4144
private fun ActionCoords.actionTypesYamlUrl(gitRef: String) =
4245
"https://raw.githubusercontent.com/$owner/$name/$gitRef$subName/action-types.yaml"
4346

47+
private fun customTypingMetadata(types: String? = null) =
48+
types?.let { Pair(yaml.decodeFromStringOrDefaultIfEmpty(it, ActionTypes()), CUSTOM) }
49+
4450
private fun ActionCoords.fetchTypingMetadata(
4551
metadataRevision: MetadataRevision,
4652
fetchUri: (URI) -> String = ::fetchUri,
@@ -64,7 +70,7 @@ private fun ActionCoords.fetchTypingMetadata(
6470
return Pair(yaml.decodeFromStringOrDefaultIfEmpty(typesMetadataYaml, ActionTypes()), ACTION)
6571
}
6672

67-
private fun ActionCoords.fetchFromTypingsFromCatalog(fetchUri: (URI) -> String = ::fetchUri): Pair<ActionTypes, TypingActualSource>? =
73+
private fun ActionCoords.fetchTypingsFromCatalog(fetchUri: (URI) -> String = ::fetchUri): Pair<ActionTypes, TypingActualSource>? =
6874
(
6975
fetchTypingsFromUrl(url = actionTypesFromCatalog(), fetchUri = fetchUri)
7076
?: fetchTypingsForOlderVersionFromCatalog(fetchUri = fetchUri)

action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ class TypesProvidingTest :
8787
stored-in-typing-catalog:
8888
type: string
8989
""".trimIndent()
90+
val custom =
91+
"""
92+
inputs:
93+
custom:
94+
type: string
95+
""".trimIndent()
9096
val metadata =
9197
"""
9298
"versionsWithTypings":
@@ -311,6 +317,134 @@ class TypesProvidingTest :
311317
types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION)
312318
}
313319

320+
test("only custom") {
321+
// Given
322+
val fetchUri: (URI) -> String = { throw IOException() }
323+
val actionCoord = ActionCoords("some-owner", "some-name", "v3")
324+
325+
// When
326+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = custom)
327+
328+
// Then
329+
types shouldBe Pair(mapOf("custom" to StringTyping), TypingActualSource.CUSTOM)
330+
}
331+
332+
test("only custom for subaction") {
333+
// Given
334+
val fetchUri: (URI) -> String = { throw IOException() }
335+
val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub")
336+
337+
// When
338+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = custom)
339+
340+
// Then
341+
types shouldBe Pair(mapOf("custom" to StringTyping), TypingActualSource.CUSTOM)
342+
}
343+
344+
test("hosted by action, stored in typing catalog, and custom") {
345+
// Given
346+
val fetchUri: (URI) -> String = {
347+
when (it) {
348+
URI(
349+
"https://raw.githubusercontent.com/some-owner/some-name/" +
350+
"some-hash/action-types.yml",
351+
),
352+
-> hostedByActionYml
353+
URI(
354+
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
355+
"main/typings/some-owner/some-name/v3/action-types.yml",
356+
),
357+
-> storedInTypingCatalog
358+
else -> throw IOException()
359+
}
360+
}
361+
val actionCoord = ActionCoords("some-owner", "some-name", "v3")
362+
363+
// When
364+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = custom)
365+
366+
// Then
367+
types shouldBe Pair(mapOf("custom" to StringTyping), TypingActualSource.CUSTOM)
368+
}
369+
370+
test("hosted by subaction, stored in typing catalog, and custom") {
371+
// Given
372+
val fetchUri: (URI) -> String = {
373+
when (it) {
374+
URI(
375+
"https://raw.githubusercontent.com/some-owner/some-name/" +
376+
"some-hash/some-sub/action-types.yml",
377+
),
378+
-> hostedByActionYml
379+
URI(
380+
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
381+
"main/typings/some-owner/some-name/v3/some-sub/action-types.yml",
382+
),
383+
-> storedInTypingCatalog
384+
else -> throw IOException()
385+
}
386+
}
387+
val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub")
388+
389+
// When
390+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = custom)
391+
392+
// Then
393+
types shouldBe Pair(mapOf("custom" to StringTyping), TypingActualSource.CUSTOM)
394+
}
395+
396+
test("hosted by action, stored in typing catalog, and empty custom") {
397+
// Given
398+
val fetchUri: (URI) -> String = {
399+
when (it) {
400+
URI(
401+
"https://raw.githubusercontent.com/some-owner/some-name/" +
402+
"some-hash/action-types.yml",
403+
),
404+
-> hostedByActionYml
405+
URI(
406+
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
407+
"main/typings/some-owner/some-name/v3/action-types.yml",
408+
),
409+
-> storedInTypingCatalog
410+
else -> throw IOException()
411+
}
412+
}
413+
val actionCoord = ActionCoords("some-owner", "some-name", "v3")
414+
415+
// When
416+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = "")
417+
418+
// Then
419+
types shouldBe Pair(emptyMap(), TypingActualSource.CUSTOM)
420+
}
421+
422+
test("hosted by subaction, stored in typing catalog, and empty custom") {
423+
// Given
424+
val fetchUri: (URI) -> String = {
425+
when (it) {
426+
URI(
427+
"https://raw.githubusercontent.com/some-owner/some-name/" +
428+
"some-hash/some-sub/action-types.yml",
429+
),
430+
-> hostedByActionYml
431+
URI(
432+
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
433+
"main/typings/some-owner/some-name/v3/some-sub/action-types.yml",
434+
),
435+
-> storedInTypingCatalog
436+
else -> throw IOException()
437+
}
438+
}
439+
val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub")
440+
441+
// When
442+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = "")
443+
444+
// Then
445+
types shouldBe Pair(emptyMap(), TypingActualSource.CUSTOM)
446+
}
447+
314448
test("only stored in typing catalog for older version") {
315449
// Given
316450
val fetchUri: (URI) -> String = {
@@ -511,6 +645,26 @@ class TypesProvidingTest :
511645
)
512646
}
513647

648+
test("only custom") {
649+
// Given
650+
val fetchUri: (URI) -> String = { throw IOException() }
651+
val actionCoord = ActionCoords("some-owner", "some-name", "v3")
652+
653+
// When
654+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri, types = typingYml)
655+
656+
// Then
657+
types shouldBe
658+
Pair(
659+
mapOf(
660+
"granted-scopes" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))),
661+
"granted-scopes2" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))),
662+
"granted-scopes3" to ListOfTypings("""\n""", EnumTyping("GrantedScopes", listOf("read", "write"))),
663+
),
664+
TypingActualSource.CUSTOM,
665+
)
666+
}
667+
514668
test("billion laughs attack is prevented") {
515669
// Given
516670
val billionLaughsAttack =

docs/user-guide/using-actions.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ For every action, a binding will be generated. However, some less popular action
2323
their inputs, so by default all inputs are of type `String`, have the suffix `_Untyped`, and additionally the class
2424
name will have an `_Untyped` suffix. The nullability of the inputs will be according to their required status.
2525

26-
There are two ways of configuring typings:
26+
There are two and a half ways of configuring typings:
2727
1. Recommended: a typing manifest (`action-typing.yml`) in the action's repo, see
2828
[github-actions-typing](https://github.com/typesafegithub/github-actions-typing/). Thanks to this, the action's owner
2929
is responsible for providing and maintaining the typings defined in a technology-agnostic way, to be used
@@ -34,6 +34,17 @@ There are two ways of configuring typings:
3434
[github-actions-typing-catalog](https://github.com/typesafegithub/github-actions-typing-catalog),
3535
a community-maintained place to host the typings. You can contribute or fix typings for your favorite action by
3636
sending a PR.
37+
3. Temporary: while developing a typing manifest it might be a good idea to test the result without needing to
38+
release the action in question or merge a PR in the catalog. For this you can `POST` the typing manifest you have
39+
on disk to the binding server using any valid URL for the action in question, for example using
40+
```bash
41+
curl --data-binary @action-types.yml https://bindings.krzeminski.it/pbrisbin/setup-tool-action/v2/setup-tool-action-v2.pom
42+
```
43+
The binding server generates a binding with only the given type manifest and answer with some unique coordinates
44+
that you can use in a test workflow script. The binding will be available the normal cache time on the binding
45+
server and locally as long as you do not delete it from your local Maven repository where it is cached. After
46+
the cache period on the server ended requesting the same coordinates will return a binding as if no typing
47+
information is available at all.
3748

3849
Once there are any typings in place for the action, the `_Untyped` suffixed class is marked `@Deprecated`, and a class
3950
without that suffix is created additionally. In that class for each input that does not have type information available

jit-binding-server/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies {
1414
implementation("io.ktor:ktor-client-cio")
1515
implementation("io.ktor:ktor-client-content-negotiation")
1616
implementation("io.ktor:ktor-serialization-kotlinx-json")
17+
implementation("it.krzeminski:snakeyaml-engine-kmp:3.0.2")
1718
implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-2.0:2.3.0-alpha")
1819
implementation("io.opentelemetry:opentelemetry-sdk:1.42.1")
1920
implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.42.1")

0 commit comments

Comments
 (0)