Skip to content

Commit f39757d

Browse files
committed
feat(abg): Support different binding versions on different server routes
1 parent b9c49a9 commit f39757d

File tree

10 files changed

+221
-31
lines changed

10 files changed

+221
-31
lines changed

.github/workflows/bindings-server.main.kts

+10
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ workflow(
9191

9292
cleanMavenLocal()
9393

94+
run(
95+
name = "Execute the script using the bindings from the server with v1 route",
96+
command = """
97+
mv .github/workflows/test-script-consuming-jit-bindings-v1.main.do-not-compile.kts .github/workflows/test-script-consuming-jit-bindings-v1.main.kts
98+
.github/workflows/test-script-consuming-jit-bindings-v1.main.kts
99+
""".trimIndent(),
100+
)
101+
102+
cleanMavenLocal()
103+
94104
run(
95105
name = "Execute the script using bindings but without dependency on library",
96106
command = """

.github/workflows/bindings-server.yaml

+10-2
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,22 @@ jobs:
5757
name: 'Clean Maven Local to fetch required POMs again'
5858
run: 'rm -rf ~/.m2/repository/'
5959
- id: 'step-7'
60+
name: 'Execute the script using the bindings from the server with v1 route'
61+
run: |-
62+
mv .github/workflows/test-script-consuming-jit-bindings-v1.main.do-not-compile.kts .github/workflows/test-script-consuming-jit-bindings-v1.main.kts
63+
.github/workflows/test-script-consuming-jit-bindings-v1.main.kts
64+
- id: 'step-8'
65+
name: 'Clean Maven Local to fetch required POMs again'
66+
run: 'rm -rf ~/.m2/repository/'
67+
- id: 'step-9'
6068
name: 'Execute the script using bindings but without dependency on library'
6169
run: |-
6270
mv .github/workflows/test-served-bindings-depend-on-library.main.do-not-compile.kts .github/workflows/test-served-bindings-depend-on-library.main.kts
6371
.github/workflows/test-served-bindings-depend-on-library.main.kts
64-
- id: 'step-8'
72+
- id: 'step-10'
6573
name: 'Fetch maven-metadata.xml for top-level action'
6674
run: 'curl --fail http://localhost:8080/actions/checkout/maven-metadata.xml | grep ''<version>v4</version>'''
67-
- id: 'step-9'
75+
- id: 'step-11'
6876
name: 'Fetch maven-metadata.xml for nested action'
6977
run: 'curl --fail http://localhost:8080/actions/cache__save/maven-metadata.xml | grep ''<version>v4</version>'''
7078
deploy:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env kotlin
2+
@file:Repository("https://repo.maven.apache.org/maven2/")
3+
@file:DependsOn("io.github.typesafegithub:github-workflows-kt:1.13.0")
4+
5+
@file:Repository("http://localhost:8080/v1")
6+
7+
// Regular, top-level action.
8+
@file:DependsOn("actions:checkout:v4")
9+
10+
// Nested action.
11+
@file:DependsOn("gradle:actions__setup-gradle:v3")
12+
13+
// Using specific version.
14+
@file:DependsOn("actions:cache:v3.3.3")
15+
16+
// Always untyped action.
17+
@file:DependsOn("typesafegithub:always-untyped-action-for-tests:v1")
18+
19+
import io.github.typesafegithub.workflows.actions.actions.Cache
20+
import io.github.typesafegithub.workflows.actions.actions.Checkout
21+
import io.github.typesafegithub.workflows.actions.actions.Checkout_Untyped
22+
import io.github.typesafegithub.workflows.actions.gradle.ActionsSetupGradle
23+
import io.github.typesafegithub.workflows.actions.typesafegithub.AlwaysUntypedActionForTests_Untyped
24+
25+
println(Checkout_Untyped(fetchTags_Untyped = "false"))
26+
println(Checkout(fetchTags = false))
27+
println(Checkout(fetchTags_Untyped = "false"))
28+
println(AlwaysUntypedActionForTests_Untyped(foobar_Untyped = "baz"))
29+
println(ActionsSetupGradle())
30+
println(Cache(path = listOf("some-path"), key = "some-key"))
31+
32+
// Ensure that 'copy(...)' method is exposed.
33+
Checkout(fetchTags = false).copy(fetchTags = true)

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/gen
6969
}
7070

7171
public final class io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationKt {
72-
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;
73-
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;
72+
public static final fun generateBinding (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lkotlin/Pair;)Ljava/util/List;
73+
public static synthetic fun generateBinding$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Lio/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision;Lio/github/typesafegithub/workflows/actionbindinggenerator/metadata/Metadata;Lkotlin/Pair;ILjava/lang/Object;)Ljava/util/List;
7474
}
7575

7676
public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Input {
@@ -179,3 +179,14 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/met
179179
public abstract interface class io/github/typesafegithub/workflows/actionbindinggenerator/typing/Typing {
180180
}
181181

182+
public final class io/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion : java/lang/Enum {
183+
public static final field V1 Lio/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion;
184+
public static fun getEntries ()Lkotlin/enums/EnumEntries;
185+
public final fun getLibraryVersion ()Ljava/lang/String;
186+
public final fun isDeprecated ()Z
187+
public final fun isExperimental ()Z
188+
public fun toString ()Ljava/lang/String;
189+
public static fun valueOf (Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion;
190+
public static fun values ()[Lio/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion;
191+
}
192+

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

+81-12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.typing.provideT
3232
import io.github.typesafegithub.workflows.actionbindinggenerator.utils.removeTrailingWhitespacesForEachLine
3333
import io.github.typesafegithub.workflows.actionbindinggenerator.utils.toCamelCase
3434
import io.github.typesafegithub.workflows.actionbindinggenerator.utils.toKotlinPackageName
35+
import io.github.typesafegithub.workflows.actionbindinggenerator.versioning.BindingVersion
36+
import io.github.typesafegithub.workflows.actionbindinggenerator.versioning.BindingVersion.V1
3537

3638
public data class ActionBinding(
3739
val kotlinCode: String,
@@ -54,6 +56,7 @@ private object Properties {
5456
}
5557

5658
public fun ActionCoords.generateBinding(
59+
bindingVersion: BindingVersion = V1,
5760
metadataRevision: MetadataRevision,
5861
metadata: Metadata? = null,
5962
inputTypings: Pair<Map<String, Typing>, TypingActualSource?>? = null,
@@ -69,10 +72,11 @@ public fun ActionCoords.generateBinding(
6972

7073
val actionBindingSourceCodeUntyped =
7174
generateActionBindingSourceCode(
72-
metadataProcessed,
73-
this,
74-
emptyMap(),
75-
classNameUntyped,
75+
metadata = metadataProcessed,
76+
coords = this,
77+
bindingVersion = bindingVersion,
78+
inputTypings = emptyMap(),
79+
className = classNameUntyped,
7680
untypedClass = true,
7781
replaceWith = inputTypingsResolved.second?.let { CodeBlock.of("ReplaceWith(%S)", className) },
7882
)
@@ -90,6 +94,7 @@ public fun ActionCoords.generateBinding(
9094
generateActionBindingSourceCode(
9195
metadata = metadataProcessed,
9296
coords = this,
97+
bindingVersion = bindingVersion,
9398
inputTypings = inputTypingsResolved.first,
9499
className = className,
95100
)
@@ -120,6 +125,7 @@ private fun Metadata.removeDeprecatedInputsIfNameClash(): Metadata {
120125
private fun generateActionBindingSourceCode(
121126
metadata: Metadata,
122127
coords: ActionCoords,
128+
bindingVersion: BindingVersion,
123129
inputTypings: Map<String, Typing>,
124130
className: String,
125131
untypedClass: Boolean = false,
@@ -136,7 +142,7 @@ private fun generateActionBindingSourceCode(
136142
changes will be overwritten with the next binding code regeneration.
137143
See https://github.com/typesafegithub/github-workflows-kt for more info.
138144
""".trimIndent(),
139-
).addType(generateActionClass(metadata, coords, inputTypings, className, untypedClass, replaceWith))
145+
).addType(generateActionClass(metadata, coords, bindingVersion, inputTypings, className, untypedClass, replaceWith))
140146
.addSuppressAnnotation(metadata)
141147
.indent(" ")
142148
.build()
@@ -165,6 +171,7 @@ private fun FileSpec.Builder.addSuppressAnnotation(metadata: Metadata) =
165171
private fun generateActionClass(
166172
metadata: Metadata,
167173
coords: ActionCoords,
174+
bindingVersion: BindingVersion,
168175
inputTypings: Map<String, Typing>,
169176
className: String,
170177
untypedClass: Boolean,
@@ -174,12 +181,13 @@ private fun generateActionClass(
174181
.classBuilder(className)
175182
.addModifiers(KModifier.DATA)
176183
.addKdocIfNotEmpty(actionKdoc(metadata, coords, untypedClass))
177-
.replaceWith(replaceWith)
184+
.deprecateBindingVersion(bindingVersion)
185+
.replaceWith(bindingVersion, replaceWith)
178186
.addClassConstructorAnnotation()
179187
.inheritsFromRegularAction(coords, metadata, className)
180188
.primaryConstructor(metadata.primaryConstructor(inputTypings, coords, className, untypedClass))
181189
.properties(metadata, coords, inputTypings, className, untypedClass)
182-
.addInitializerBlockIfNecessary(metadata, inputTypings, untypedClass)
190+
.addInitializerBlock(metadata, bindingVersion, inputTypings, untypedClass)
183191
.addFunction(metadata.secondaryConstructor(inputTypings, coords, className, untypedClass))
184192
.addFunction(metadata.buildToYamlArgumentsFunction(inputTypings, untypedClass))
185193
.addCustomTypes(inputTypings, coords, className)
@@ -374,8 +382,23 @@ private fun Metadata.linkedMapOfInputs(
374382
}
375383
}
376384

377-
private fun TypeSpec.Builder.replaceWith(replaceWith: CodeBlock?): TypeSpec.Builder {
378-
if (replaceWith != null) {
385+
private fun TypeSpec.Builder.deprecateBindingVersion(bindingVersion: BindingVersion): TypeSpec.Builder {
386+
if (bindingVersion.isDeprecated) {
387+
addAnnotation(
388+
AnnotationSpec
389+
.builder(Deprecated::class.asClassName())
390+
.addMember("%S", "Use a non-deprecated binding version in the repository URL")
391+
.build(),
392+
)
393+
}
394+
return this
395+
}
396+
397+
private fun TypeSpec.Builder.replaceWith(
398+
bindingVersion: BindingVersion,
399+
replaceWith: CodeBlock?,
400+
): TypeSpec.Builder {
401+
if (!bindingVersion.isDeprecated && replaceWith != null) {
379402
addAnnotation(
380403
AnnotationSpec
381404
.builder(Deprecated::class.asClassName())
@@ -533,15 +556,61 @@ private fun ParameterSpec.Builder.defaultValueIfNullable(
533556
return this
534557
}
535558

536-
private fun TypeSpec.Builder.addInitializerBlockIfNecessary(
559+
private fun TypeSpec.Builder.addInitializerBlock(
537560
metadata: Metadata,
561+
bindingVersion: BindingVersion,
538562
inputTypings: Map<String, Typing>,
539563
untypedClass: Boolean,
540564
): TypeSpec.Builder {
541-
if (untypedClass || metadata.inputs.isEmpty() || metadata.inputs.none { inputTypings.containsKey(it.key) }) {
565+
if (!bindingVersion.isDeprecated &&
566+
!bindingVersion.isExperimental &&
567+
(untypedClass || metadata.inputs.isEmpty() || metadata.inputs.none { inputTypings.containsKey(it.key) })
568+
) {
542569
return this
543570
}
544-
addInitializerBlock(metadata.initializerBlock(inputTypings))
571+
572+
val initializerBlock = CodeBlock.builder()
573+
if (bindingVersion.isDeprecated) {
574+
val firstStableVersion = BindingVersion.entries.find { !it.isDeprecated && !it.isExperimental }
575+
initializerBlock.addStatement(
576+
"println(%S)",
577+
"""
578+
WARNING: The used binding version $bindingVersion is deprecated! First stable version is $firstStableVersion.
579+
""".trimIndent(),
580+
)
581+
initializerBlock.beginControlFlow("""if (System.getenv("GITHUB_ACTIONS").toBoolean())""")
582+
initializerBlock.addStatement(
583+
"println(%S)",
584+
"""
585+
586+
::warning title=Deprecated Binding Version Used::The used binding version $bindingVersion is deprecated! First stable version is $firstStableVersion.
587+
""".trimIndent(),
588+
)
589+
initializerBlock.endControlFlow()
590+
initializerBlock.add("\n")
591+
}
592+
if (bindingVersion.isExperimental) {
593+
val lastStableVersion = BindingVersion.entries.findLast { !it.isDeprecated && !it.isExperimental }
594+
initializerBlock.addStatement(
595+
"println(%S)",
596+
"""
597+
WARNING: The used binding version $bindingVersion is experimental! Last stable version is $lastStableVersion.
598+
""".trimIndent(),
599+
)
600+
initializerBlock.beginControlFlow("""if (System.getenv("GITHUB_ACTIONS").toBoolean())""")
601+
initializerBlock.addStatement(
602+
"println(%S)",
603+
"""
604+
605+
::warning title=Experimental Binding Version Used::The used binding version $bindingVersion is experimental! Last stable version is $lastStableVersion.
606+
""".trimIndent(),
607+
)
608+
initializerBlock.endControlFlow()
609+
initializerBlock.add("\n")
610+
}
611+
initializerBlock.add(metadata.initializerBlock(inputTypings))
612+
613+
addInitializerBlock(initializerBlock.build())
545614
return this
546615
}
547616

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.github.typesafegithub.workflows.actionbindinggenerator.versioning
2+
3+
public enum class BindingVersion(
4+
public val isDeprecated: Boolean = false,
5+
public val isExperimental: Boolean = true,
6+
public val libraryVersion: String,
7+
) {
8+
V1(isExperimental = false, libraryVersion = "3.0.0"),
9+
;
10+
11+
override fun toString(): String = super.toString().lowercase()
12+
}

0 commit comments

Comments
 (0)