Skip to content

Commit 83d25db

Browse files
authored
feat(abg)!: include untyped bindings (#1593)
Part of #1585. For every binding JAR, include an untyped binding. If the typings are available, additionally include a typed binding. This change breaks clients that use actions without any typings. It forces them to either add `_Untyped` to the class name, or add the typings to the action or the typing catalog.
1 parent 8f16be4 commit 83d25db

File tree

4 files changed

+195
-14
lines changed

4 files changed

+195
-14
lines changed

.github/workflows/test-script-consuming-jit-bindings.main.do-not-compile.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515

1616
import io.github.typesafegithub.workflows.actions.actions.Cache
1717
import io.github.typesafegithub.workflows.actions.actions.Checkout
18+
import io.github.typesafegithub.workflows.actions.actions.Checkout_Untyped
1819
import io.github.typesafegithub.workflows.actions.gradle.ActionsSetupGradle
1920

20-
println(Checkout())
21+
println(Checkout_Untyped(fetchTags = "false"))
22+
println(Checkout(fetchTags = false))
2123
println(ActionsSetupGradle())
2224
println(Cache(path = listOf("some-path"), key = "some-key"))

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

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,45 @@ public fun ActionCoords.generateBinding(
6363

6464
val inputTypingsResolved = inputTypings ?: this.provideTypes(metadataRevision)
6565

66-
val className = this.buildActionClassName()
67-
val actionBindingSourceCode =
68-
generateActionBindingSourceCode(metadataProcessed, this, inputTypingsResolved.first, className)
66+
val classNameUntyped = this.buildActionClassName() + "_Untyped"
67+
val actionBindingSourceCodeUntyped =
68+
generateActionBindingSourceCode(metadataProcessed, this, emptyMap(), classNameUntyped, untyped = true)
69+
70+
val classNameAndSourceCodeTyped =
71+
if (inputTypingsResolved.second != null) {
72+
val className = this.buildActionClassName()
73+
val actionBindingSourceCode =
74+
generateActionBindingSourceCode(
75+
metadataProcessed,
76+
this,
77+
inputTypingsResolved.first,
78+
className,
79+
untyped = false,
80+
)
81+
Pair(className, actionBindingSourceCode)
82+
} else {
83+
null
84+
}
85+
6986
val packageName = owner.toKotlinPackageName()
70-
return listOf(
87+
88+
return listOfNotNull(
7189
ActionBinding(
72-
kotlinCode = actionBindingSourceCode,
73-
filePath = "kotlin/io/github/typesafegithub/workflows/actions/$packageName/$className.kt",
74-
className = className,
90+
kotlinCode = actionBindingSourceCodeUntyped,
91+
filePath = "kotlin/io/github/typesafegithub/workflows/actions/$packageName/$classNameUntyped.kt",
92+
className = classNameUntyped,
7593
packageName = packageName,
76-
typingActualSource = inputTypingsResolved.second,
94+
typingActualSource = null,
7795
),
96+
classNameAndSourceCodeTyped?.let { (className, actionBindingSourceCode) ->
97+
ActionBinding(
98+
kotlinCode = actionBindingSourceCode,
99+
filePath = "kotlin/io/github/typesafegithub/workflows/actions/$packageName/$className.kt",
100+
className = className,
101+
packageName = packageName,
102+
typingActualSource = inputTypingsResolved.second,
103+
)
104+
},
78105
)
79106
}
80107

@@ -96,6 +123,7 @@ private fun generateActionBindingSourceCode(
96123
coords: ActionCoords,
97124
inputTypings: Map<String, Typing>,
98125
className: String,
126+
untyped: Boolean,
99127
): String {
100128
val fileSpec =
101129
FileSpec
@@ -108,7 +136,7 @@ private fun generateActionBindingSourceCode(
108136
changes will be overwritten with the next binding code regeneration.
109137
See https://github.com/typesafegithub/github-workflows-kt for more info.
110138
""".trimIndent(),
111-
).addType(generateActionClass(metadata, coords, inputTypings, className))
139+
).addType(generateActionClass(metadata, coords, inputTypings, className, untyped))
112140
.addSuppressAnnotation(metadata)
113141
.indent(" ")
114142
.build()
@@ -139,11 +167,12 @@ private fun generateActionClass(
139167
coords: ActionCoords,
140168
inputTypings: Map<String, Typing>,
141169
className: String,
170+
untyped: Boolean,
142171
): TypeSpec =
143172
TypeSpec
144173
.classBuilder(className)
145174
.addModifiers(KModifier.DATA)
146-
.addKdoc(actionKdoc(metadata, coords))
175+
.addKdoc(actionKdoc(metadata, coords, untyped))
147176
.inheritsFromRegularAction(coords, metadata, className)
148177
.primaryConstructor(metadata.primaryConstructor(inputTypings, coords, className))
149178
.properties(metadata, coords, inputTypings, className)
@@ -384,14 +413,44 @@ private fun ParameterSpec.Builder.defaultValueIfNullable(input: Input): Paramete
384413
private fun actionKdoc(
385414
metadata: Metadata,
386415
coords: ActionCoords,
387-
) = """
416+
untyped: Boolean,
417+
) = (
418+
if (untyped) {
419+
"""
420+
|```text
421+
|!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
422+
|!!! WARNING !!!
423+
|!!! !!!
424+
|!!! This action binding has no typings provided. All inputs will !!!
425+
|!!! have a default type of String. !!!
426+
|!!! To be able to use this action in a type-safe way, ask the !!!
427+
|!!! action's owner to provide the typings using !!!
428+
|!!! !!!
429+
|!!! https://github.com/typesafegithub/github-actions-typing !!!
430+
|!!! !!!
431+
|!!! or if it's impossible, contribute typings to a community-driven !!!
432+
|!!! !!!
433+
|!!! https://github.com/typesafegithub/github-actions-typing-catalog !!!
434+
|!!! !!!
435+
|!!! This '_Untyped' binding will be available even once the typings !!!
436+
|!!! are added. !!!
437+
|!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
438+
|```
439+
|
440+
|
441+
""".trimMargin()
442+
} else {
443+
""
444+
}
445+
) +
446+
"""
388447
|Action: ${metadata.name.escapedForComments}
389448
|
390449
|${metadata.description.escapedForComments.removeTrailingWhitespacesForEachLine()}
391450
|
392451
|[Action on GitHub](https://github.com/${coords.owner}/${coords.name.substringBefore(
393-
'/',
394-
)}${if ("/" in coords.name) "/tree/${coords.version}/${coords.name.substringAfter('/')}" else ""})
452+
'/',
453+
)}${if ("/" in coords.name) "/tree/${coords.version}/${coords.name.substringAfter('/')}" else ""})
395454
""".trimMargin()
396455

397456
private fun Map<String, Typing>.getInputTyping(key: String) = this[key] ?: StringTyping
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// This file was generated using action-binding-generator. Don't change it by hand, otherwise your
2+
// changes will be overwritten with the next binding code regeneration.
3+
// See https://github.com/typesafegithub/github-workflows-kt for more info.
4+
@file:Suppress(
5+
"DataClassPrivateConstructor",
6+
"UNUSED_PARAMETER",
7+
)
8+
9+
package io.github.typesafegithub.workflows.actions.johnsmith
10+
11+
import io.github.typesafegithub.workflows.domain.actions.Action
12+
import io.github.typesafegithub.workflows.domain.actions.RegularAction
13+
import java.util.LinkedHashMap
14+
import kotlin.String
15+
import kotlin.Suppress
16+
import kotlin.Unit
17+
import kotlin.collections.Map
18+
import kotlin.collections.toList
19+
import kotlin.collections.toTypedArray
20+
21+
/**
22+
* ```text
23+
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
24+
* !!! WARNING !!!
25+
* !!! !!!
26+
* !!! This action binding has no typings provided. All inputs will !!!
27+
* !!! have a default type of String. !!!
28+
* !!! To be able to use this action in a type-safe way, ask the !!!
29+
* !!! action's owner to provide the typings using !!!
30+
* !!! !!!
31+
* !!! https://github.com/typesafegithub/github-actions-typing !!!
32+
* !!! !!!
33+
* !!! or if it's impossible, contribute typings to a community-driven !!!
34+
* !!! !!!
35+
* !!! https://github.com/typesafegithub/github-actions-typing-catalog !!!
36+
* !!! !!!
37+
* !!! This '_Untyped' binding will be available even once the typings !!!
38+
* !!! are added. !!!
39+
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
40+
* ```
41+
*
42+
* Action: Do something cool
43+
*
44+
* This is a test description that should be put in the KDoc comment for a class
45+
*
46+
* [Action on GitHub](https://github.com/john-smith/action-with-no-typings)
47+
*
48+
* @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by
49+
* the binding
50+
* @param _customVersion Allows overriding action's version, for example to use a specific minor
51+
* version, or a newer version that the binding doesn't yet know about
52+
*/
53+
public data class ActionWithNoTypings_Untyped private constructor(
54+
public val foo: String,
55+
public val bar: String? = null,
56+
/**
57+
* Type-unsafe map where you can put any inputs that are not yet supported by the binding
58+
*/
59+
public val _customInputs: Map<String, String> = mapOf(),
60+
/**
61+
* Allows overriding action's version, for example to use a specific minor version, or a newer
62+
* version that the binding doesn't yet know about
63+
*/
64+
public val _customVersion: String? = null,
65+
) : RegularAction<Action.Outputs>("john-smith", "action-with-no-typings", _customVersion ?: "v3") {
66+
public constructor(
67+
vararg pleaseUseNamedArguments: Unit,
68+
foo: String,
69+
bar: String? = null,
70+
_customInputs: Map<String, String> = mapOf(),
71+
_customVersion: String? = null,
72+
) : this(foo=foo, bar=bar, _customInputs=_customInputs, _customVersion=_customVersion)
73+
74+
@Suppress("SpreadOperator")
75+
override fun toYamlArguments(): LinkedHashMap<String, String> = linkedMapOf(
76+
*listOfNotNull(
77+
"foo" to foo,
78+
bar?.let { "bar" to it },
79+
*_customInputs.toList().toTypedArray(),
80+
).toTypedArray()
81+
)
82+
83+
override fun buildOutputObject(stepId: String): Action.Outputs = Outputs(stepId)
84+
}

action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationTest.kt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.typing.ListOfTy
1616
import io.github.typesafegithub.workflows.actionbindinggenerator.typing.StringTyping
1717
import io.github.typesafegithub.workflows.actionbindinggenerator.typing.Typing
1818
import io.kotest.core.spec.style.FunSpec
19+
import io.kotest.matchers.collections.shouldHaveSize
1920

2021
class GenerationTest :
2122
FunSpec({
@@ -396,6 +397,41 @@ class GenerationTest :
396397
// then
397398
binding.shouldContainAndMatchFile("ActionWithFancyCharsInDocs.kt")
398399
}
400+
401+
test("action with no typings has only an untyped binding") {
402+
// given
403+
val actionManifest =
404+
Metadata(
405+
name = "Do something cool",
406+
description = "This is a test description that should be put in the KDoc comment for a class",
407+
inputs =
408+
mapOf(
409+
"foo" to
410+
Input(
411+
required = true,
412+
default = null,
413+
),
414+
"bar" to
415+
Input(
416+
required = false,
417+
default = "test",
418+
),
419+
),
420+
)
421+
val coords = ActionCoords("john-smith", "action-with-no-typings", "v3")
422+
423+
// when
424+
val binding =
425+
coords.generateBinding(
426+
metadataRevision = NewestForVersion,
427+
metadata = actionManifest,
428+
inputTypings = Pair(emptyMap(), null),
429+
)
430+
431+
// then
432+
binding shouldHaveSize 1
433+
binding.shouldContainAndMatchFile("ActionWithNoTypings_Untyped.kt")
434+
}
399435
})
400436

401437
private fun Metadata.allInputsAsStrings(): Map<String, Typing> = this.inputs.mapValues { StringTyping }

0 commit comments

Comments
 (0)