From 71c6dd835dc43a05fcdb13c58e528482ae38f282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Fri, 23 Aug 2024 18:20:50 +0200 Subject: [PATCH] feat(abg)!: allow to wire typed outputs to matching typed inputs --- .github/workflows/bindings-server.main.kts | 19 +- .github/workflows/bindings-server.yaml | 16 +- .github/workflows/end-to-end-tests.main.kts | 17 +- .../api/action-binding-generator.api | 21 +- .../domain/ActionTypings.kt | 9 + .../generation/Generation.kt | 484 +++++++++++++----- .../typing/TypesProviding.kt | 16 +- .../versioning/BindingVersion.kt | 2 +- .../ActionWithAllTypesOfInputsBindingV1.kt | 119 +++-- ...onWithAllTypesOfInputsBindingV1_Untyped.kt | 57 ++- .../ActionWithAllTypesOfInputsBindingV2.kt | 338 ++++++++++-- ...onWithAllTypesOfInputsBindingV2_Untyped.kt | 260 +++++++++- .../ActionWithAllTypesOfInputsTest.kt | 15 +- ...ithDeprecatedInputAndNameClashBindingV1.kt | 6 +- ...ithDeprecatedInputAndNameClashBindingV2.kt | 21 +- .../ActionWithFancyCharsInDocsBindingV1.kt | 8 +- .../ActionWithFancyCharsInDocsBindingV2.kt | 29 +- .../ActionWithInputsSharingTypeBindingV1.kt | 16 +- .../ActionWithInputsSharingTypeBindingV2.kt | 45 +- .../ActionWithNoTypingsBindingV2_Untyped.kt | 25 +- .../ActionWithOutputsBindingV1.kt | 6 +- .../ActionWithOutputsBindingV2.kt | 26 +- .../ActionWithOutputsTest.kt | 7 +- .../ActionWithPartlyTypingsBindingV1.kt | 6 +- .../ActionWithPartlyTypingsBindingV2.kt | 44 +- ...ctionWithPartlyTypingsBindingV2_Untyped.kt | 41 +- .../ActionWithSomeOptionalInputsBindingV1.kt | 22 +- .../ActionWithSomeOptionalInputsBindingV2.kt | 69 ++- ...ActionWithRequiredStringInputsBindingV1.kt | 12 +- ...ActionWithRequiredStringInputsBindingV2.kt | 37 +- .../generation/GenerationTest.kt | 192 ++++--- .../typing/TypesProvidingTest.kt | 126 +++-- .../dsl/expressions/GenerateEventPayloads.kt | 2 +- docs/user-guide/job-outputs.md | 5 +- .../migrating-to-Maven-based-bindings.md | 4 + docs/user-guide/using-actions.md | 25 +- .../api/github-workflows-kt.api | 19 +- .../workflows/domain/AbstractResult.kt | 2 + .../workflows/domain/Expression.kt | 9 + .../workflows/domain/JobOutputs.kt | 13 +- .../workflows/domain/actions/Action.kt | 3 +- .../workflows/IntegrationTest.kt | 4 +- .../workflows/actions/CustomActionTest.kt | 3 +- .../docsnippets/JobOutputsSnippets.kt | 11 +- .../docsnippets/UsingActionsSnippets.kt | 3 +- .../workflows/domain/JobTest.kt | 22 +- .../workflows/domain/StepTest.kt | 4 +- 47 files changed, 1707 insertions(+), 533 deletions(-) create mode 100644 action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings.kt create mode 100644 github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/Expression.kt diff --git a/.github/workflows/bindings-server.main.kts b/.github/workflows/bindings-server.main.kts index 3c85caae3f..119c10d70a 100755 --- a/.github/workflows/bindings-server.main.kts +++ b/.github/workflows/bindings-server.main.kts @@ -99,15 +99,16 @@ workflow( """.trimIndent(), ) - cleanMavenLocal() - - run( - name = "Execute the script using the bindings from the server with v2 route", - command = """ - mv .github/workflows/test-script-consuming-jit-bindings-v2.main.do-not-compile.kts .github/workflows/test-script-consuming-jit-bindings-v2.main.kts - .github/workflows/test-script-consuming-jit-bindings-v2.main.kts - """.trimIndent(), - ) + // TODO: Reenable after release, currently, new class "Expression" cannot be found + //cleanMavenLocal() + + //run( + // name = "Execute the script using the bindings from the server with v2 route", + // command = """ + // mv .github/workflows/test-script-consuming-jit-bindings-v2.main.do-not-compile.kts .github/workflows/test-script-consuming-jit-bindings-v2.main.kts + // .github/workflows/test-script-consuming-jit-bindings-v2.main.kts + // """.trimIndent(), + //) cleanMavenLocal() diff --git a/.github/workflows/bindings-server.yaml b/.github/workflows/bindings-server.yaml index 0b1ce8d916..993d82ea07 100644 --- a/.github/workflows/bindings-server.yaml +++ b/.github/workflows/bindings-server.yaml @@ -65,30 +65,22 @@ jobs: name: 'Clean Maven Local to fetch required POMs again' run: 'rm -rf ~/.m2/repository/' - id: 'step-9' - name: 'Execute the script using the bindings from the server with v2 route' - run: |- - mv .github/workflows/test-script-consuming-jit-bindings-v2.main.do-not-compile.kts .github/workflows/test-script-consuming-jit-bindings-v2.main.kts - .github/workflows/test-script-consuming-jit-bindings-v2.main.kts - - id: 'step-10' - name: 'Clean Maven Local to fetch required POMs again' - run: 'rm -rf ~/.m2/repository/' - - id: 'step-11' name: 'Execute the script using bindings but without dependency on library' run: |- mv .github/workflows/test-served-bindings-depend-on-library.main.do-not-compile.kts .github/workflows/test-served-bindings-depend-on-library.main.kts .github/workflows/test-served-bindings-depend-on-library.main.kts - - id: 'step-12' + - id: 'step-10' name: 'Clean Maven Local to fetch required POMs again' run: 'rm -rf ~/.m2/repository/' - - id: 'step-13' + - id: 'step-11' name: 'Compile a Gradle project using the bindings from the server' run: |- cd .github/workflows/test-gradle-project-using-bindings-server ./gradlew build - - id: 'step-14' + - id: 'step-12' name: 'Fetch maven-metadata.xml for top-level action' run: 'curl --fail http://localhost:8080/actions/checkout/maven-metadata.xml | grep ''v4''' - - id: 'step-15' + - id: 'step-13' name: 'Fetch maven-metadata.xml for nested action' run: 'curl --fail http://localhost:8080/actions/cache__save/maven-metadata.xml | grep ''v4''' deploy: diff --git a/.github/workflows/end-to-end-tests.main.kts b/.github/workflows/end-to-end-tests.main.kts index 527ce09694..146d961022 100755 --- a/.github/workflows/end-to-end-tests.main.kts +++ b/.github/workflows/end-to-end-tests.main.kts @@ -15,6 +15,7 @@ import io.github.typesafegithub.workflows.actions.actions.* import io.github.typesafegithub.workflows.actions.gradle.ActionsSetupGradle import io.github.typesafegithub.workflows.actions.wandalen.WretryAction import io.github.typesafegithub.workflows.annotations.ExperimentalKotlinLogicStep +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.JobOutputs import io.github.typesafegithub.workflows.domain.Mode import io.github.typesafegithub.workflows.domain.Permission @@ -81,9 +82,9 @@ workflow( Permission.Contents to Mode.None, ), outputs = object : JobOutputs() { - var scriptKey by output() - var scriptKey2 by output() - var scriptResult by output() + var scriptKey by output() + var scriptKey2 by output() + var scriptResult by output() }, ) { run( @@ -179,7 +180,7 @@ workflow( name = "Some step consuming other step's output", action = Checkout( sshKey = expr(addAndCommit.outputs.pythonVersion), - path = expr(addAndCommit.outputs["my-unsafe-output"]), + path = addAndCommit.outputs["my-unsafe-output"].expressionString, ), ) @@ -234,7 +235,7 @@ workflow( ) jobOutputs.scriptKey = scriptStep.outputs["key"] jobOutputs.scriptKey2 = scriptStep.outputs["key2"] - jobOutputs.scriptResult = scriptStep.outputs.result + jobOutputs.scriptResult = Expression(scriptStep.outputs.result) } job( @@ -250,9 +251,9 @@ workflow( run( name = "use output of script", command = """ - echo ${expr { testJob1.outputs.scriptKey }} - echo ${expr { testJob1.outputs.scriptKey2 }} - echo ${expr { testJob1.outputs.scriptResult }} + echo ${testJob1.outputs.scriptKey.expressionString} + echo ${expr(testJob1.outputs.scriptKey2.expression)} + echo ${expr { testJob1.outputs.scriptResult.expression }} """.trimIndent(), ) diff --git a/action-binding-generator/api/action-binding-generator.api b/action-binding-generator/api/action-binding-generator.api index 0f40016c49..ee3e952422 100644 --- a/action-binding-generator/api/action-binding-generator.api +++ b/action-binding-generator/api/action-binding-generator.api @@ -23,6 +23,23 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/dom public static final fun isTopLevel (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;)Z } +public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings { + public fun ()V + public fun (Ljava/util/Map;Ljava/util/Map;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource;)V + public synthetic fun (Ljava/util/Map;Ljava/util/Map;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Map; + public final fun component2 ()Ljava/util/Map; + public final fun component3 ()Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource; + public final fun copy (Ljava/util/Map;Ljava/util/Map;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings; + public static synthetic fun copy$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings;Ljava/util/Map;Ljava/util/Map;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings; + public fun equals (Ljava/lang/Object;)Z + public final fun getInputTypings ()Ljava/util/Map; + public final fun getOutputTypings ()Ljava/util/Map; + public final fun getSource ()Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/CommitHash : io/github/typesafegithub/workflows/actionbindinggenerator/domain/MetadataRevision { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; @@ -72,8 +89,8 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/gen } public final class io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationKt { - 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; - 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; + 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;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings;)Ljava/util/List; + 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;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings;ILjava/lang/Object;)Ljava/util/List; } public final class io/github/typesafegithub/workflows/actionbindinggenerator/metadata/Input { diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings.kt new file mode 100644 index 0000000000..cb6dafab99 --- /dev/null +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionTypings.kt @@ -0,0 +1,9 @@ +package io.github.typesafegithub.workflows.actionbindinggenerator.domain + +import io.github.typesafegithub.workflows.actionbindinggenerator.typing.Typing + +public data class ActionTypings( + val inputTypings: Map = emptyMap(), + val outputTypings: Map = emptyMap(), + val source: TypingActualSource? = null, +) diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt index 9e3baf7b21..3ad22a805b 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/Generation.kt @@ -12,10 +12,12 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.plusParameter import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec +import com.squareup.kotlinpoet.WildcardTypeName import com.squareup.kotlinpoet.asClassName import com.squareup.kotlinpoet.asTypeName import com.squareup.kotlinpoet.buildCodeBlock import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionTypings import io.github.typesafegithub.workflows.actionbindinggenerator.domain.MetadataRevision import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource import io.github.typesafegithub.workflows.actionbindinggenerator.domain.fullName @@ -28,6 +30,7 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.Input import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.Metadata import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.fetchMetadata import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.shouldBeRequiredInBinding +import io.github.typesafegithub.workflows.actionbindinggenerator.typing.ListOfTypings import io.github.typesafegithub.workflows.actionbindinggenerator.typing.StringTyping import io.github.typesafegithub.workflows.actionbindinggenerator.typing.Typing import io.github.typesafegithub.workflows.actionbindinggenerator.typing.asString @@ -39,6 +42,7 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.utils.toCamelCa import io.github.typesafegithub.workflows.actionbindinggenerator.utils.toKotlinPackageName import io.github.typesafegithub.workflows.actionbindinggenerator.versioning.BindingVersion import io.github.typesafegithub.workflows.actionbindinggenerator.versioning.BindingVersion.V1 +import io.github.typesafegithub.workflows.actionbindinggenerator.versioning.BindingVersion.V2 public data class ActionBinding( val kotlinCode: String, @@ -64,12 +68,12 @@ public fun ActionCoords.generateBinding( bindingVersion: BindingVersion = V1, metadataRevision: MetadataRevision, metadata: Metadata? = null, - inputTypings: Pair, TypingActualSource?>? = null, + typings: ActionTypings? = null, ): List { val metadataResolved = metadata ?: this.fetchMetadata(metadataRevision) ?: return emptyList() val metadataProcessed = metadataResolved.removeDeprecatedInputsIfNameClash() - val inputTypingsResolved = inputTypings ?: this.provideTypes(metadataRevision) + val typingsResolved = typings ?: this.provideTypes(metadataRevision) val packageName = owner.toKotlinPackageName() val className = this.buildActionClassName() @@ -81,9 +85,10 @@ public fun ActionCoords.generateBinding( coords = this, bindingVersion = bindingVersion, inputTypings = emptyMap(), + outputTypings = emptyMap(), className = classNameUntyped, untypedClass = true, - replaceWith = inputTypingsResolved.second?.let { CodeBlock.of("ReplaceWith(%S)", className) }, + replaceWith = typingsResolved.source?.let { CodeBlock.of("ReplaceWith(%S)", className) }, ) return listOfNotNull( @@ -94,13 +99,14 @@ public fun ActionCoords.generateBinding( packageName = packageName, typingActualSource = null, ), - inputTypingsResolved.second?.let { + typingsResolved.source?.let { val actionBindingSourceCode = generateActionBindingSourceCode( metadata = metadataProcessed, coords = this, bindingVersion = bindingVersion, - inputTypings = inputTypingsResolved.first, + inputTypings = typingsResolved.inputTypings, + outputTypings = typingsResolved.outputTypings, className = className, ) ActionBinding( @@ -132,6 +138,7 @@ private fun generateActionBindingSourceCode( coords: ActionCoords, bindingVersion: BindingVersion, inputTypings: Map, + outputTypings: Map, className: String, untypedClass: Boolean = false, replaceWith: CodeBlock? = null, @@ -147,8 +154,18 @@ private fun generateActionBindingSourceCode( changes will be overwritten with the next binding code regeneration. See https://github.com/typesafegithub/github-workflows-kt for more info. """.trimIndent(), - ).addType(generateActionClass(metadata, coords, bindingVersion, inputTypings, className, untypedClass, replaceWith)) - .addSuppressAnnotation(metadata) + ).addType( + generateActionClass( + metadata, + coords, + bindingVersion, + inputTypings, + outputTypings, + className, + untypedClass, + replaceWith, + ), + ).addSuppressAnnotation(bindingVersion, metadata, untypedClass) .indent(" ") .build() return buildString { @@ -156,28 +173,28 @@ private fun generateActionBindingSourceCode( } } -private fun FileSpec.Builder.addSuppressAnnotation(metadata: Metadata) = - apply { - val isDeprecatedInputUsed = metadata.inputs.values.any { it.deprecationMessage.isNullOrBlank().not() } - - addAnnotation( - AnnotationSpec - .builder(Suppress::class.asClassName()) - .addMember(CodeBlock.of("%S", "DataClassPrivateConstructor")) - .addMember(CodeBlock.of("%S", "UNUSED_PARAMETER")) - .apply { - if (isDeprecatedInputUsed) { - addMember(CodeBlock.of("%S", "DEPRECATION")) - } - }.build(), - ) +private fun FileSpec.Builder.addSuppressAnnotation( + bindingVersion: BindingVersion, + metadata: Metadata, + untypedClass: Boolean, +): FileSpec.Builder { + val suppress = AnnotationSpec.builder(Suppress::class.asClassName()) + suppress.addMember(CodeBlock.of("%S", "DataClassPrivateConstructor")) + suppress.addMember(CodeBlock.of("%S", "UNUSED_PARAMETER")) + val isDeprecatedInputUsed = metadata.inputs.values.any { it.deprecationMessage.isNullOrBlank().not() } + if (((bindingVersion >= V2) && !untypedClass && metadata.inputs.isNotEmpty()) || isDeprecatedInputUsed) { + suppress.addMember(CodeBlock.of("%S", "DEPRECATION")) } + addAnnotation(suppress.build()) + return this +} private fun generateActionClass( metadata: Metadata, coords: ActionCoords, bindingVersion: BindingVersion, inputTypings: Map, + outputTypings: Map, className: String, untypedClass: Boolean, replaceWith: CodeBlock?, @@ -190,29 +207,33 @@ private fun generateActionClass( .replaceWith(bindingVersion, replaceWith) .addClassConstructorAnnotation() .inheritsFromRegularAction(coords, metadata, className) - .primaryConstructor(metadata.primaryConstructor(inputTypings, coords, className, untypedClass)) - .properties(metadata, coords, inputTypings, className, untypedClass) + .primaryConstructor(metadata.primaryConstructor(bindingVersion, inputTypings, coords, className, untypedClass)) + .properties(bindingVersion, metadata, coords, inputTypings, className, untypedClass) .addInitializerBlock(metadata, bindingVersion, coords, inputTypings, untypedClass) - .addFunction(metadata.secondaryConstructor(inputTypings, coords, className, untypedClass)) - .addFunction(metadata.buildToYamlArgumentsFunction(inputTypings, untypedClass)) - .addCustomTypes(inputTypings, coords, className) - .addOutputClassIfNecessary(metadata) + .addFunction(metadata.secondaryConstructor(bindingVersion, inputTypings, coords, className, untypedClass)) + .addFunction(metadata.buildToYamlArgumentsFunction(bindingVersion, inputTypings, untypedClass)) + .addCustomTypes(inputTypings, outputTypings, coords, className) + .addOutputClassIfNecessary(bindingVersion, metadata, coords, outputTypings) .addBuildOutputObjectFunctionIfNecessary(metadata) .build() private fun TypeSpec.Builder.addCustomTypes( - typings: Map, + inputTypings: Map, + outputTypings: Map, coords: ActionCoords, className: String, ): TypeSpec.Builder { - typings + (inputTypings.entries + outputTypings.entries) .mapNotNull { (inputName, typing) -> typing.buildCustomType(coords, inputName, className) } .distinctBy { it.name } .forEach { addType(it) } return this } +private val Expression = ClassName("io.github.typesafegithub.workflows.domain", "Expression") + private fun TypeSpec.Builder.properties( + bindingVersion: BindingVersion, metadata: Metadata, coords: ActionCoords, inputTypings: Map, @@ -221,31 +242,92 @@ private fun TypeSpec.Builder.properties( ): TypeSpec.Builder { metadata.inputs.forEach { (key, input) -> val typedInput = inputTypings.containsKey(key) + val propertyBaseName = key.toCamelCase() if (!untypedClass && typedInput) { addProperty( PropertySpec .builder( - key.toCamelCase(), - inputTypings.getInputType( + propertyBaseName, + inputTypings + .getInputType( + bindingVersion, + key, + input, + coords, + className, + untypedClass = false, + typedInput = true, + ), + ).initializer("%N", propertyBaseName) + .annotateDeprecated(bindingVersion, input) + .build(), + ) + } + addProperty( + PropertySpec + .builder( + "${propertyBaseName}_Untyped", + null + .getInputType( + bindingVersion, key, input, coords, className, - untypedClass = false, - typedInput = true, + untypedClass, + typedInput, ), - ).initializer(key.toCamelCase()) - .annotateDeprecated(input) + ).initializer("%N", "${propertyBaseName}_Untyped") + .annotateDeprecated(bindingVersion, input, typedInput) + .build(), + ) + if (bindingVersion >= V2) { + addProperty( + PropertySpec + .builder( + "${propertyBaseName}Expression", + Expression + .parameterizedBy( + inputTypings + .getInputType( + bindingVersion, + key, + input, + coords, + className, + untypedClass, + typedInput, + ).copy(nullable = false), + ).copy(nullable = true), + ).initializer("%N", "${propertyBaseName}Expression") + .annotateDeprecated(bindingVersion, input) .build(), ) + if (inputTypings[key] is ListOfTypings) { + addProperty( + PropertySpec + .builder( + "${propertyBaseName}Expressions", + List::class + .asClassName() + .parameterizedBy( + Expression + .parameterizedBy( + (inputTypings[key] as ListOfTypings) + .typing + .getClassName( + actionPackageName = coords.owner.toKotlinPackageName(), + actionClassName = coords.buildActionClassName(), + fieldName = key, + ), + ), + ).copy(nullable = true), + ).initializer("%N", "${propertyBaseName}Expressions") + .annotateDeprecated(bindingVersion, input) + .build(), + ) + } } - addProperty( - PropertySpec - .builder("${key.toCamelCase()}_Untyped", null.getInputType(key, input, coords, className, untypedClass, typedInput)) - .initializer("${key.toCamelCase()}_Untyped") - .annotateDeprecated(input) - .build(), - ) } addProperty(PropertySpec.builder(CUSTOM_INPUTS, Types.mapStringString).initializer(CUSTOM_INPUTS).build()) addProperty(PropertySpec.builder(CUSTOM_VERSION, Types.nullableString).initializer(CUSTOM_VERSION).build()) @@ -254,7 +336,12 @@ private fun TypeSpec.Builder.properties( private val OutputsBase = ClassName("io.github.typesafegithub.workflows.domain.actions", "Action", "Outputs") -private fun TypeSpec.Builder.addOutputClassIfNecessary(metadata: Metadata): TypeSpec.Builder { +private fun TypeSpec.Builder.addOutputClassIfNecessary( + bindingVersion: BindingVersion, + metadata: Metadata, + coords: ActionCoords, + outputTypings: Map, +): TypeSpec.Builder { if (metadata.outputs.isEmpty()) { return this } @@ -264,12 +351,39 @@ private fun TypeSpec.Builder.addOutputClassIfNecessary(metadata: Metadata): Type .builder("stepId", String::class) .build() val propertiesFromOutputs = - metadata.outputs.map { (key, value) -> - PropertySpec - .builder(key.toCamelCase(), String::class) - .initializer("\"steps.\$stepId.outputs.$key\"") - .addKdocIfNotEmpty(value.description.escapedForComments.removeTrailingWhitespacesForEachLine()) - .build() + if (bindingVersion <= V1) { + metadata.outputs.map { (key, value) -> + PropertySpec + .builder(key.toCamelCase(), String::class) + .initializer("\"steps.\$stepId.outputs.$key\"") + .addKdocIfNotEmpty(value.description.escapedForComments.removeTrailingWhitespacesForEachLine()) + .build() + } + } else { + metadata.outputs.flatMap { (key, value) -> + val outputClassName = + outputTypings[key] + ?.getClassName(coords.owner.toKotlinPackageName(), coords.buildActionClassName(), key) + val propertyBaseName = key.toCamelCase() + listOfNotNull( + outputClassName?.let { + PropertySpec + .builder( + propertyBaseName, + Expression.parameterizedBy(it), + ).initializer("%T(\"steps.\$stepId.outputs.%L\")", Expression, key) + .addKdocIfNotEmpty(value.description.escapedForComments.removeTrailingWhitespacesForEachLine()) + .build() + }, + PropertySpec + .builder( + "${propertyBaseName}_Untyped", + Expression.parameterizedBy(Any::class.asClassName()), + ).initializer("%T(\"steps.\$stepId.outputs.%L\")", Expression, key) + .addKdocIfNotEmpty(value.description.escapedForComments.removeTrailingWhitespacesForEachLine()) + .build(), + ) + } } addType( TypeSpec @@ -314,26 +428,32 @@ private fun TypeSpec.Builder.addBuildOutputObjectFunctionIfNecessary(metadata: M .returns(if (metadata.outputs.isEmpty()) OutputsBase else ClassName("", "Outputs")) .addModifiers(KModifier.OVERRIDE) .addParameter("stepId", String::class) - .addCode(CodeBlock.of("return Outputs(stepId)")) + .addCode("return Outputs(stepId)") .build(), ) return this } -private fun PropertySpec.Builder.annotateDeprecated(input: Input) = - apply { - if (input.deprecationMessage != null) { - addAnnotation( - AnnotationSpec - .builder(Deprecated::class.asClassName()) - .addMember("%S", input.deprecationMessage) - .build(), - ) - } +private fun PropertySpec.Builder.annotateDeprecated( + bindingVersion: BindingVersion, + input: Input, + untypedSibling: Boolean = false, +) = apply { + if (((bindingVersion >= V2) && untypedSibling) || (input.deprecationMessage != null)) { + addAnnotation( + AnnotationSpec + .builder(Deprecated::class.asClassName()) + .addMember( + "%S", + input.deprecationMessage ?: "Use the typed property or expression property instead", + ).build(), + ) } +} private fun Metadata.buildToYamlArgumentsFunction( + bindingVersion: BindingVersion, inputTypings: Map, untypedClass: Boolean, ) = FunSpec @@ -343,12 +463,13 @@ private fun Metadata.buildToYamlArgumentsFunction( .addAnnotation( AnnotationSpec .builder(Suppress::class) - .addMember("\"SpreadOperator\"") + .addMember("%S", "SpreadOperator") .build(), - ).addCode(linkedMapOfInputs(inputTypings, untypedClass)) + ).addCode(linkedMapOfInputs(bindingVersion, inputTypings, untypedClass)) .build() private fun Metadata.linkedMapOfInputs( + bindingVersion: BindingVersion, inputTypings: Map, untypedClass: Boolean, ) = if (inputs.isEmpty()) { @@ -360,16 +481,36 @@ private fun Metadata.linkedMapOfInputs( add("*listOfNotNull(\n") indent() inputs.forEach { (key, value) -> - val propertyName = key.toCamelCase() + val propertyBaseName = key.toCamelCase() if (!untypedClass && inputTypings.containsKey(key)) { val asStringCode = inputTypings.getInputTyping(key).asString() - add("%N?.let { %S to it$asStringCode },\n", propertyName, key) + add("%N?.let { %S to it$asStringCode },\n", propertyBaseName, key) } val asStringCode = null.getInputTyping(key).asString() - if (value.shouldBeRequiredInBinding() && !value.shouldBeNullable(untypedClass, inputTypings.containsKey(key))) { - add("%S to %N$asStringCode,\n", key, "${propertyName}_Untyped") + if (value.shouldBeRequiredInBinding() && + !value.shouldBeNullable( + bindingVersion, + untypedClass, + inputTypings.containsKey(key), + ) + ) { + add("%S to %N$asStringCode,\n", key, "${propertyBaseName}_Untyped") } else { - add("%N?.let { %S to it$asStringCode },\n", "${propertyName}_Untyped", key) + add("%N?.let { %S to it$asStringCode },\n", "${propertyBaseName}_Untyped", key) + } + if (bindingVersion >= V2) { + add("%N?.let { %S to it.expressionString },\n", "${propertyBaseName}Expression", key) + if (inputTypings[key] is ListOfTypings) { + add( + "%N?.let { %S to it.joinToString(%S, transform = %T::expressionString) },\n", + "${propertyBaseName}Expressions", + key, + " ", + Expression.parameterizedBy( + WildcardTypeName.producerOf(Any::class.asClassName().copy(nullable = true)), + ), + ) + } } } add("*$CUSTOM_INPUTS.%M().%M(),\n", Types.mapToList, Types.listToArray) @@ -443,6 +584,7 @@ private fun TypeSpec.Builder.inheritsFromRegularAction( } private fun Metadata.primaryConstructor( + bindingVersion: BindingVersion, inputTypings: Map, coords: ActionCoords, className: String, @@ -451,10 +593,11 @@ private fun Metadata.primaryConstructor( FunSpec .constructorBuilder() .addModifiers(KModifier.PRIVATE) - .addParameters(buildCommonConstructorParameters(inputTypings, coords, className, untypedClass)) + .addParameters(buildCommonConstructorParameters(bindingVersion, inputTypings, coords, className, untypedClass)) .build() private fun Metadata.secondaryConstructor( + bindingVersion: BindingVersion, inputTypings: Map, coords: ActionCoords, className: String, @@ -467,22 +610,30 @@ private fun Metadata.secondaryConstructor( .builder("pleaseUseNamedArguments", Unit::class) .addModifiers(KModifier.VARARG) .build(), - ).addParameters(buildCommonConstructorParameters(inputTypings, coords, className, untypedClass)) + ).addParameters(buildCommonConstructorParameters(bindingVersion, inputTypings, coords, className, untypedClass)) .callThisConstructor( inputs .keys .flatMap { inputName -> + val propertyBaseName = inputName.toCamelCase() val typedInput = inputTypings.containsKey(inputName) listOfNotNull( - untypedClass.takeIf { !it && typedInput }?.let { inputName.toCamelCase() }, - "${inputName.toCamelCase()}_Untyped", + untypedClass.takeIf { !it && typedInput }?.let { propertyBaseName }, + "${propertyBaseName}_Untyped", + if (bindingVersion >= V2) "${propertyBaseName}Expression" else null, + if (bindingVersion >= V2) { + (inputTypings[inputName] as? ListOfTypings)?.let { "${propertyBaseName}Expressions" } + } else { + null + }, ) }.plus(CUSTOM_INPUTS) .plus(CUSTOM_VERSION) - .map { CodeBlock.of("%N = %N", it, it) }, + .map { CodeBlock.of("%1N = %1N", it) }, ).build() private fun Metadata.buildCommonConstructorParameters( + bindingVersion: BindingVersion, inputTypings: Map, coords: ActionCoords, className: String, @@ -490,6 +641,7 @@ private fun Metadata.buildCommonConstructorParameters( ): List = inputs .flatMap { (key, input) -> + val propertyBaseName = key.toCamelCase() val typedInput = inputTypings.containsKey(key) val description = input.description.escapedForComments.removeTrailingWhitespacesForEachLine() val kdoc = @@ -507,26 +659,82 @@ private fun Metadata.buildCommonConstructorParameters( untypedClass.takeIf { !it && typedInput }?.let { ParameterSpec .builder( - key.toCamelCase(), - inputTypings.getInputType( + propertyBaseName, + inputTypings + .getInputType( + bindingVersion, + key, + input, + coords, + className, + untypedClass = false, + typedInput = true, + ), + ).defaultValue("null") + .addKdocIfNotEmpty(kdoc) + .build() + }, + ParameterSpec + .builder( + "${propertyBaseName}_Untyped", + null + .getInputType( + bindingVersion, key, input, coords, className, - untypedClass = false, - typedInput = true, + untypedClass, + typedInput, ), + ).defaultValueIfNullable(bindingVersion, input, untypedClass, typedInput) + .addKdocIfNotEmpty(kdoc) + .build(), + bindingVersion.takeIf { it >= V2 }?.let { + ParameterSpec + .builder( + "${propertyBaseName}Expression", + Expression + .parameterizedBy( + inputTypings + .getInputType( + bindingVersion, + key, + input, + coords, + className, + untypedClass, + typedInput, + ).copy(nullable = false), + ).copy(nullable = true), ).defaultValue("null") .addKdocIfNotEmpty(kdoc) .build() }, - ParameterSpec - .builder( - "${key.toCamelCase()}_Untyped", - null.getInputType(key, input, coords, className, untypedClass, typedInput), - ).defaultValueIfNullable(input, untypedClass, typedInput) - .addKdocIfNotEmpty(kdoc) - .build(), + bindingVersion.takeIf { it >= V2 }?.let { + (inputTypings[key] as? ListOfTypings)?.let { listOfTypings -> + ParameterSpec + .builder( + "${propertyBaseName}Expressions", + List::class + .asClassName() + .parameterizedBy( + Expression + .parameterizedBy( + listOfTypings + .typing + .getClassName( + actionPackageName = coords.owner.toKotlinPackageName(), + actionClassName = coords.buildActionClassName(), + fieldName = key, + ), + ), + ).copy(nullable = true), + ).defaultValue("null") + .addKdocIfNotEmpty(kdoc) + .build() + } + }, ) }.plus( ParameterSpec @@ -545,11 +753,12 @@ private fun Metadata.buildCommonConstructorParameters( ) private fun ParameterSpec.Builder.defaultValueIfNullable( + bindingVersion: BindingVersion, input: Input, untypedClass: Boolean, typedInput: Boolean, ): ParameterSpec.Builder { - if (input.shouldBeNullable(untypedClass, typedInput)) { + if (input.shouldBeNullable(bindingVersion, untypedClass, typedInput)) { defaultValue("null") } return this @@ -564,7 +773,11 @@ private fun TypeSpec.Builder.addInitializerBlock( ): TypeSpec.Builder { if (!bindingVersion.isDeprecated && !bindingVersion.isExperimental && - (untypedClass || metadata.inputs.isEmpty() || metadata.inputs.none { inputTypings.containsKey(it.key) }) + if (bindingVersion <= V1) { + untypedClass || metadata.inputs.isEmpty() || metadata.inputs.none { inputTypings.containsKey(it.key) } + } else { + metadata.inputs.isEmpty() + } ) { return this } @@ -609,47 +822,67 @@ private fun TypeSpec.Builder.addInitializerBlock( endControlFlow() add("\n") } - add(metadata.initializerBlock(inputTypings)) + add(metadata.initializerBlock(bindingVersion, inputTypings, untypedClass)) }, ) return this } -private fun Metadata.initializerBlock(inputTypings: Map) = - buildCodeBlock { - var first = true - inputs - .filter { inputTypings.containsKey(it.key) } - .forEach { (key, input) -> - if (!first) { - add("\n") - } - first = false - val propertyName = key.toCamelCase() - add( - """ - require(!((%1N != null) && (%1L_Untyped != null))) { - %2S - } - - """.trimIndent(), - propertyName, - "Only $propertyName or ${propertyName}_Untyped must be set, but not both", +private fun Metadata.initializerBlock( + bindingVersion: BindingVersion, + inputTypings: Map, + untypedClass: Boolean, +) = buildCodeBlock { + var first = true + inputs + .filter { (bindingVersion >= V2) || inputTypings.containsKey(it.key) } + .forEach { (key, input) -> + if (!first) { + add("\n") + } + first = false + val typedInput = inputTypings.containsKey(key) + val inputProperties = + listOfNotNull( + if (!untypedClass && typedInput) "%1N" else null, + "%1L_Untyped", + if (bindingVersion >= V2) "%1LExpression" else null, + if ((bindingVersion >= V2) && (inputTypings[key] is ListOfTypings)) "%1LExpressions" else null, ) - if (input.shouldBeRequiredInBinding()) { - add( - """ - require((%1N != null) || (%1L_Untyped != null)) { - %2S - } - - """.trimIndent(), - propertyName, - "Either $propertyName or ${propertyName}_Untyped must be set, one of them is required", + val inputPropertyNames = + listOfNotNull( + if (!untypedClass && typedInput) "%1L" else null, + "%1L_Untyped", + if (bindingVersion >= V2) "%1LExpression" else null, + if ((bindingVersion >= V2) && (inputTypings[key] is ListOfTypings)) "%1LExpressions" else null, + ) + val propertyBaseName = key.toCamelCase() + beginControlFlow("require(listOfNotNull(${inputProperties.joinToString()}).size <= 1)", propertyBaseName) + addStatement( + "%S", + "Only one of ${ + CodeBlock.of( + "${inputPropertyNames.dropLast(1).joinToString()}, and ${inputPropertyNames.last()}", + propertyBaseName, ) - } + } must be set, but not multiple", + ) + endControlFlow() + if (input.shouldBeRequiredInBinding()) { + beginControlFlow("require(${inputProperties.joinToString(" || ") { "($it != null)" }})", propertyBaseName) + addStatement( + "%S", + "Either ${ + CodeBlock.of( + "${inputPropertyNames.dropLast(1).joinToString()}, or ${inputPropertyNames.last()}", + propertyBaseName, + ) + } must be set, one of them is required", + ) + endControlFlow() } - } + } +} private fun actionKdoc( metadata: Metadata, @@ -695,6 +928,7 @@ private fun actionKdoc( private fun Map?.getInputTyping(key: String) = this?.get(key) ?: StringTyping private fun Map?.getInputType( + bindingVersion: BindingVersion, key: String, input: Input, coords: ActionCoords, @@ -703,13 +937,19 @@ private fun Map?.getInputType( typedInput: Boolean, ) = getInputTyping(key) .getClassName(coords.owner.toKotlinPackageName(), className, key) - .copy(nullable = input.shouldBeNullable(untypedClass, typedInput)) + .copy(nullable = input.shouldBeNullable(bindingVersion, untypedClass, typedInput)) private fun Input.shouldBeNullable( + bindingVersion: BindingVersion, untypedClass: Boolean, typedInput: Boolean, -) = (untypedClass && !shouldBeRequiredInBinding()) || - (!untypedClass && (typedInput || !shouldBeRequiredInBinding())) +) = // untyped class => according to required status + (untypedClass && !shouldBeRequiredInBinding()) || + // typed class, typed input => null + // typed class, untyped input => according to required status + (!untypedClass && (typedInput || !shouldBeRequiredInBinding())) || + // expression siblings + (bindingVersion >= V2) private val String.escapedForComments get() = diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt index 1aa83dde2c..87db9a497c 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt @@ -3,6 +3,7 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.typing import com.charleskorn.kaml.Yaml import io.github.oshai.kotlinlogging.KotlinLogging.logger import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionTypings import io.github.typesafegithub.workflows.actionbindinggenerator.domain.CommitHash import io.github.typesafegithub.workflows.actionbindinggenerator.domain.MetadataRevision import io.github.typesafegithub.workflows.actionbindinggenerator.domain.NewestForVersion @@ -23,12 +24,13 @@ private val logger = logger { } internal fun ActionCoords.provideTypes( metadataRevision: MetadataRevision, fetchUri: (URI) -> String = ::fetchUri, -): Pair, TypingActualSource?> = +): ActionTypings = ( this.fetchTypingMetadata(metadataRevision, fetchUri) ?: this.toMajorVersion().fetchFromTypingsFromCatalog(fetchUri) - )?.let { Pair(it.first.toTypesMap(), it.second) } - ?: Pair(emptyMap(), null) + )?.let { (typings, typingActualSource) -> + ActionTypings(typings.toInputTypesMap(), typings.toOutputTypesMap(), typingActualSource) + } ?: ActionTypings() private fun ActionCoords.actionTypesYmlUrl(gitRef: String) = "https://raw.githubusercontent.com/$owner/$name/$gitRef$subName/action-types.yml" @@ -111,11 +113,15 @@ private fun fetchTypingsFromUrl( return yaml.decodeFromStringOrDefaultIfEmpty(typesMetadataYml, ActionTypes()) } -internal fun ActionTypes.toTypesMap(): Map = - inputs.mapValues { (key, value) -> +private fun Map.toTypesMap(): Map = + mapValues { (key, value) -> value.toTyping(key) } +private fun ActionTypes.toInputTypesMap(): Map = inputs.toTypesMap() + +private fun ActionTypes.toOutputTypesMap(): Map = outputs.toTypesMap() + private fun ActionCoords.toMajorVersion(): ActionCoords = this.copy(version = this.version.substringBefore(".")) private fun ActionType.toTyping(fieldName: String): Typing = diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion.kt index 160c0101a0..15a6982cda 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/versioning/BindingVersion.kt @@ -6,7 +6,7 @@ public enum class BindingVersion( public val libraryVersion: String, ) { V1(isExperimental = false, libraryVersion = "3.0.1"), - V2(libraryVersion = "3.0.1"), + V2(libraryVersion = "4.0.0"), ; override fun toString(): String = super.toString().lowercase() diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV1.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV1.kt index 756be74002..784e9d0ab2 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV1.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV1.kt @@ -165,73 +165,73 @@ public data class ActionWithAllTypesOfInputsBindingV1 private constructor( public val _customVersion: String? = null, ) : RegularAction("john-smith", "action-with-all-types-of-inputs-binding-v1", _customVersion ?: "v3") { init { - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped).size <= 1) { + "Only one of fooBar, and fooBar_Untyped must be set, but not multiple" } require((fooBar != null) || (fooBar_Untyped != null)) { - "Either fooBar or fooBar_Untyped must be set, one of them is required" + "Either fooBar, or fooBar_Untyped must be set, one of them is required" } - require(!((bazGoo != null) && (bazGoo_Untyped != null))) { - "Only bazGoo or bazGoo_Untyped must be set, but not both" + require(listOfNotNull(bazGoo, bazGoo_Untyped).size <= 1) { + "Only one of bazGoo, and bazGoo_Untyped must be set, but not multiple" } require((bazGoo != null) || (bazGoo_Untyped != null)) { - "Either bazGoo or bazGoo_Untyped must be set, one of them is required" + "Either bazGoo, or bazGoo_Untyped must be set, one of them is required" } - require(!((binKin != null) && (binKin_Untyped != null))) { - "Only binKin or binKin_Untyped must be set, but not both" + require(listOfNotNull(binKin, binKin_Untyped).size <= 1) { + "Only one of binKin, and binKin_Untyped must be set, but not multiple" } - require(!((intPint != null) && (intPint_Untyped != null))) { - "Only intPint or intPint_Untyped must be set, but not both" + require(listOfNotNull(intPint, intPint_Untyped).size <= 1) { + "Only one of intPint, and intPint_Untyped must be set, but not multiple" } require((intPint != null) || (intPint_Untyped != null)) { - "Either intPint or intPint_Untyped must be set, one of them is required" + "Either intPint, or intPint_Untyped must be set, one of them is required" } - require(!((floPint != null) && (floPint_Untyped != null))) { - "Only floPint or floPint_Untyped must be set, but not both" + require(listOfNotNull(floPint, floPint_Untyped).size <= 1) { + "Only one of floPint, and floPint_Untyped must be set, but not multiple" } require((floPint != null) || (floPint_Untyped != null)) { - "Either floPint or floPint_Untyped must be set, one of them is required" + "Either floPint, or floPint_Untyped must be set, one of them is required" } - require(!((finBin != null) && (finBin_Untyped != null))) { - "Only finBin or finBin_Untyped must be set, but not both" + require(listOfNotNull(finBin, finBin_Untyped).size <= 1) { + "Only one of finBin, and finBin_Untyped must be set, but not multiple" } require((finBin != null) || (finBin_Untyped != null)) { - "Either finBin or finBin_Untyped must be set, one of them is required" + "Either finBin, or finBin_Untyped must be set, one of them is required" } - require(!((gooZen != null) && (gooZen_Untyped != null))) { - "Only gooZen or gooZen_Untyped must be set, but not both" + require(listOfNotNull(gooZen, gooZen_Untyped).size <= 1) { + "Only one of gooZen, and gooZen_Untyped must be set, but not multiple" } require((gooZen != null) || (gooZen_Untyped != null)) { - "Either gooZen or gooZen_Untyped must be set, one of them is required" + "Either gooZen, or gooZen_Untyped must be set, one of them is required" } - require(!((bahEnum != null) && (bahEnum_Untyped != null))) { - "Only bahEnum or bahEnum_Untyped must be set, but not both" + require(listOfNotNull(bahEnum, bahEnum_Untyped).size <= 1) { + "Only one of bahEnum, and bahEnum_Untyped must be set, but not multiple" } require((bahEnum != null) || (bahEnum_Untyped != null)) { - "Either bahEnum or bahEnum_Untyped must be set, one of them is required" + "Either bahEnum, or bahEnum_Untyped must be set, one of them is required" } - require(!((listStrings != null) && (listStrings_Untyped != null))) { - "Only listStrings or listStrings_Untyped must be set, but not both" + require(listOfNotNull(listStrings, listStrings_Untyped).size <= 1) { + "Only one of listStrings, and listStrings_Untyped must be set, but not multiple" } - require(!((listInts != null) && (listInts_Untyped != null))) { - "Only listInts or listInts_Untyped must be set, but not both" + require(listOfNotNull(listInts, listInts_Untyped).size <= 1) { + "Only one of listInts, and listInts_Untyped must be set, but not multiple" } - require(!((listEnums != null) && (listEnums_Untyped != null))) { - "Only listEnums or listEnums_Untyped must be set, but not both" + require(listOfNotNull(listEnums, listEnums_Untyped).size <= 1) { + "Only one of listEnums, and listEnums_Untyped must be set, but not multiple" } - require(!((listIntSpecial != null) && (listIntSpecial_Untyped != null))) { - "Only listIntSpecial or listIntSpecial_Untyped must be set, but not both" + require(listOfNotNull(listIntSpecial, listIntSpecial_Untyped).size <= 1) { + "Only one of listIntSpecial, and listIntSpecial_Untyped must be set, but not multiple" } } @@ -362,8 +362,63 @@ public data class ActionWithAllTypesOfInputsBindingV1 private constructor( stepId: String, ) : Action.Outputs(stepId) { /** - * Cool output! + * Short description output + */ + public val fooBar: String = "steps.$stepId.outputs.foo-bar" + + /** + * First boolean input! output */ public val bazGoo: String = "steps.$stepId.outputs.baz-goo" + + /** + * Boolean and nullable output + */ + public val binKin: String = "steps.$stepId.outputs.bin-kin" + + /** + * Integer output + */ + public val intPint: String = "steps.$stepId.outputs.int-pint" + + /** + * Float output + */ + public val floPint: String = "steps.$stepId.outputs.flo-pint" + + /** + * Enumeration output + */ + public val finBin: String = "steps.$stepId.outputs.fin-bin" + + /** + * Integer with special value output + */ + public val gooZen: String = "steps.$stepId.outputs.goo-zen" + + /** + * Enum with custom naming output + */ + public val bahEnum: String = "steps.$stepId.outputs.bah-enum" + + /** + * List of strings output + */ + public val listStrings: String = "steps.$stepId.outputs.list-strings" + + /** + * List of integers output + */ + public val listInts: String = "steps.$stepId.outputs.list-ints" + + /** + * List of enums output + */ + public val listEnums: String = "steps.$stepId.outputs.list-enums" + + /** + * List of integer with special values output + */ + public val listIntSpecial: String = "steps.$stepId.outputs.list-int-special" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV1_Untyped.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV1_Untyped.kt index 5be32947be..15aa4280e1 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV1_Untyped.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV1_Untyped.kt @@ -168,8 +168,63 @@ public data class ActionWithAllTypesOfInputsBindingV1_Untyped private constructo stepId: String, ) : Action.Outputs(stepId) { /** - * Cool output! + * Short description output + */ + public val fooBar: String = "steps.$stepId.outputs.foo-bar" + + /** + * First boolean input! output */ public val bazGoo: String = "steps.$stepId.outputs.baz-goo" + + /** + * Boolean and nullable output + */ + public val binKin: String = "steps.$stepId.outputs.bin-kin" + + /** + * Integer output + */ + public val intPint: String = "steps.$stepId.outputs.int-pint" + + /** + * Float output + */ + public val floPint: String = "steps.$stepId.outputs.flo-pint" + + /** + * Enumeration output + */ + public val finBin: String = "steps.$stepId.outputs.fin-bin" + + /** + * Integer with special value output + */ + public val gooZen: String = "steps.$stepId.outputs.goo-zen" + + /** + * Enum with custom naming output + */ + public val bahEnum: String = "steps.$stepId.outputs.bah-enum" + + /** + * List of strings output + */ + public val listStrings: String = "steps.$stepId.outputs.list-strings" + + /** + * List of integers output + */ + public val listInts: String = "steps.$stepId.outputs.list-ints" + + /** + * List of enums output + */ + public val listEnums: String = "steps.$stepId.outputs.list-enums" + + /** + * List of integer with special values output + */ + public val listIntSpecial: String = "steps.$stepId.outputs.list-int-special" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV2.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV2.kt index a56caad041..7d3e7307e3 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV2.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV2.kt @@ -4,14 +4,18 @@ @file:Suppress( "DataClassPrivateConstructor", "UNUSED_PARAMETER", + "DEPRECATION", ) package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap +import kotlin.Any import kotlin.Boolean +import kotlin.Deprecated import kotlin.ExposedCopyVisibility import kotlin.Float import kotlin.Int @@ -32,28 +36,44 @@ import kotlin.collections.toTypedArray * * @param fooBar <required> Short description * @param fooBar_Untyped <required> Short description + * @param fooBarExpression <required> Short description * @param bazGoo <required> First boolean input! * @param bazGoo_Untyped <required> First boolean input! + * @param bazGooExpression <required> First boolean input! * @param binKin Boolean and nullable * @param binKin_Untyped Boolean and nullable + * @param binKinExpression Boolean and nullable * @param intPint <required> Integer * @param intPint_Untyped <required> Integer + * @param intPintExpression <required> Integer * @param floPint <required> Float * @param floPint_Untyped <required> Float + * @param floPintExpression <required> Float * @param finBin <required> Enumeration * @param finBin_Untyped <required> Enumeration + * @param finBinExpression <required> Enumeration * @param gooZen <required> Integer with special value * @param gooZen_Untyped <required> Integer with special value + * @param gooZenExpression <required> Integer with special value * @param bahEnum <required> Enum with custom naming * @param bahEnum_Untyped <required> Enum with custom naming + * @param bahEnumExpression <required> Enum with custom naming * @param listStrings List of strings * @param listStrings_Untyped List of strings + * @param listStringsExpression List of strings + * @param listStringsExpressions List of strings * @param listInts List of integers * @param listInts_Untyped List of integers + * @param listIntsExpression List of integers + * @param listIntsExpressions List of integers * @param listEnums List of enums * @param listEnums_Untyped List of enums + * @param listEnumsExpression List of enums + * @param listEnumsExpressions List of enums * @param listIntSpecial List of integer with special values * @param listIntSpecial_Untyped List of integer with special values + * @param listIntSpecialExpression List of integer with special values + * @param listIntSpecialExpressions List of integer with special values * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -66,7 +86,12 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * <required> Short description */ + @Deprecated("Use the typed property or expression property instead") public val fooBar_Untyped: String? = null, + /** + * <required> Short description + */ + public val fooBarExpression: Expression? = null, /** * <required> First boolean input! */ @@ -74,7 +99,12 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * <required> First boolean input! */ + @Deprecated("Use the typed property or expression property instead") public val bazGoo_Untyped: String? = null, + /** + * <required> First boolean input! + */ + public val bazGooExpression: Expression? = null, /** * Boolean and nullable */ @@ -82,7 +112,12 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * Boolean and nullable */ + @Deprecated("Use the typed property or expression property instead") public val binKin_Untyped: String? = null, + /** + * Boolean and nullable + */ + public val binKinExpression: Expression? = null, /** * <required> Integer */ @@ -90,7 +125,12 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * <required> Integer */ + @Deprecated("Use the typed property or expression property instead") public val intPint_Untyped: String? = null, + /** + * <required> Integer + */ + public val intPintExpression: Expression? = null, /** * <required> Float */ @@ -98,7 +138,12 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * <required> Float */ + @Deprecated("Use the typed property or expression property instead") public val floPint_Untyped: String? = null, + /** + * <required> Float + */ + public val floPintExpression: Expression? = null, /** * <required> Enumeration */ @@ -106,7 +151,12 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * <required> Enumeration */ + @Deprecated("Use the typed property or expression property instead") public val finBin_Untyped: String? = null, + /** + * <required> Enumeration + */ + public val finBinExpression: Expression? = null, /** * <required> Integer with special value */ @@ -114,7 +164,12 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * <required> Integer with special value */ + @Deprecated("Use the typed property or expression property instead") public val gooZen_Untyped: String? = null, + /** + * <required> Integer with special value + */ + public val gooZenExpression: Expression? = null, /** * <required> Enum with custom naming */ @@ -122,7 +177,12 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * <required> Enum with custom naming */ + @Deprecated("Use the typed property or expression property instead") public val bahEnum_Untyped: String? = null, + /** + * <required> Enum with custom naming + */ + public val bahEnumExpression: Expression? = null, /** * List of strings */ @@ -130,7 +190,16 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * List of strings */ + @Deprecated("Use the typed property or expression property instead") public val listStrings_Untyped: String? = null, + /** + * List of strings + */ + public val listStringsExpression: Expression>? = null, + /** + * List of strings + */ + public val listStringsExpressions: List>? = null, /** * List of integers */ @@ -138,7 +207,16 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * List of integers */ + @Deprecated("Use the typed property or expression property instead") public val listInts_Untyped: String? = null, + /** + * List of integers + */ + public val listIntsExpression: Expression>? = null, + /** + * List of integers + */ + public val listIntsExpressions: List>? = null, /** * List of enums */ @@ -146,7 +224,18 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * List of enums */ + @Deprecated("Use the typed property or expression property instead") public val listEnums_Untyped: String? = null, + /** + * List of enums + */ + public val listEnumsExpression: + Expression>? = null, + /** + * List of enums + */ + public val listEnumsExpressions: + List>? = null, /** * List of integer with special values */ @@ -154,7 +243,18 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( /** * List of integer with special values */ + @Deprecated("Use the typed property or expression property instead") public val listIntSpecial_Untyped: String? = null, + /** + * List of integer with special values + */ + public val listIntSpecialExpression: + Expression>? = null, + /** + * List of integer with special values + */ + public val listIntSpecialExpressions: + List>? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -173,73 +273,73 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( """.trimMargin()) } - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped, fooBarExpression).size <= 1) { + "Only one of fooBar, fooBar_Untyped, and fooBarExpression must be set, but not multiple" } - require((fooBar != null) || (fooBar_Untyped != null)) { - "Either fooBar or fooBar_Untyped must be set, one of them is required" + require((fooBar != null) || (fooBar_Untyped != null) || (fooBarExpression != null)) { + "Either fooBar, fooBar_Untyped, or fooBarExpression must be set, one of them is required" } - require(!((bazGoo != null) && (bazGoo_Untyped != null))) { - "Only bazGoo or bazGoo_Untyped must be set, but not both" + require(listOfNotNull(bazGoo, bazGoo_Untyped, bazGooExpression).size <= 1) { + "Only one of bazGoo, bazGoo_Untyped, and bazGooExpression must be set, but not multiple" } - require((bazGoo != null) || (bazGoo_Untyped != null)) { - "Either bazGoo or bazGoo_Untyped must be set, one of them is required" + require((bazGoo != null) || (bazGoo_Untyped != null) || (bazGooExpression != null)) { + "Either bazGoo, bazGoo_Untyped, or bazGooExpression must be set, one of them is required" } - require(!((binKin != null) && (binKin_Untyped != null))) { - "Only binKin or binKin_Untyped must be set, but not both" + require(listOfNotNull(binKin, binKin_Untyped, binKinExpression).size <= 1) { + "Only one of binKin, binKin_Untyped, and binKinExpression must be set, but not multiple" } - require(!((intPint != null) && (intPint_Untyped != null))) { - "Only intPint or intPint_Untyped must be set, but not both" + require(listOfNotNull(intPint, intPint_Untyped, intPintExpression).size <= 1) { + "Only one of intPint, intPint_Untyped, and intPintExpression must be set, but not multiple" } - require((intPint != null) || (intPint_Untyped != null)) { - "Either intPint or intPint_Untyped must be set, one of them is required" + require((intPint != null) || (intPint_Untyped != null) || (intPintExpression != null)) { + "Either intPint, intPint_Untyped, or intPintExpression must be set, one of them is required" } - require(!((floPint != null) && (floPint_Untyped != null))) { - "Only floPint or floPint_Untyped must be set, but not both" + require(listOfNotNull(floPint, floPint_Untyped, floPintExpression).size <= 1) { + "Only one of floPint, floPint_Untyped, and floPintExpression must be set, but not multiple" } - require((floPint != null) || (floPint_Untyped != null)) { - "Either floPint or floPint_Untyped must be set, one of them is required" + require((floPint != null) || (floPint_Untyped != null) || (floPintExpression != null)) { + "Either floPint, floPint_Untyped, or floPintExpression must be set, one of them is required" } - require(!((finBin != null) && (finBin_Untyped != null))) { - "Only finBin or finBin_Untyped must be set, but not both" + require(listOfNotNull(finBin, finBin_Untyped, finBinExpression).size <= 1) { + "Only one of finBin, finBin_Untyped, and finBinExpression must be set, but not multiple" } - require((finBin != null) || (finBin_Untyped != null)) { - "Either finBin or finBin_Untyped must be set, one of them is required" + require((finBin != null) || (finBin_Untyped != null) || (finBinExpression != null)) { + "Either finBin, finBin_Untyped, or finBinExpression must be set, one of them is required" } - require(!((gooZen != null) && (gooZen_Untyped != null))) { - "Only gooZen or gooZen_Untyped must be set, but not both" + require(listOfNotNull(gooZen, gooZen_Untyped, gooZenExpression).size <= 1) { + "Only one of gooZen, gooZen_Untyped, and gooZenExpression must be set, but not multiple" } - require((gooZen != null) || (gooZen_Untyped != null)) { - "Either gooZen or gooZen_Untyped must be set, one of them is required" + require((gooZen != null) || (gooZen_Untyped != null) || (gooZenExpression != null)) { + "Either gooZen, gooZen_Untyped, or gooZenExpression must be set, one of them is required" } - require(!((bahEnum != null) && (bahEnum_Untyped != null))) { - "Only bahEnum or bahEnum_Untyped must be set, but not both" + require(listOfNotNull(bahEnum, bahEnum_Untyped, bahEnumExpression).size <= 1) { + "Only one of bahEnum, bahEnum_Untyped, and bahEnumExpression must be set, but not multiple" } - require((bahEnum != null) || (bahEnum_Untyped != null)) { - "Either bahEnum or bahEnum_Untyped must be set, one of them is required" + require((bahEnum != null) || (bahEnum_Untyped != null) || (bahEnumExpression != null)) { + "Either bahEnum, bahEnum_Untyped, or bahEnumExpression must be set, one of them is required" } - require(!((listStrings != null) && (listStrings_Untyped != null))) { - "Only listStrings or listStrings_Untyped must be set, but not both" + require(listOfNotNull(listStrings, listStrings_Untyped, listStringsExpression, listStringsExpressions).size <= 1) { + "Only one of listStrings, listStrings_Untyped, listStringsExpression, and listStringsExpressions must be set, but not multiple" } - require(!((listInts != null) && (listInts_Untyped != null))) { - "Only listInts or listInts_Untyped must be set, but not both" + require(listOfNotNull(listInts, listInts_Untyped, listIntsExpression, listIntsExpressions).size <= 1) { + "Only one of listInts, listInts_Untyped, listIntsExpression, and listIntsExpressions must be set, but not multiple" } - require(!((listEnums != null) && (listEnums_Untyped != null))) { - "Only listEnums or listEnums_Untyped must be set, but not both" + require(listOfNotNull(listEnums, listEnums_Untyped, listEnumsExpression, listEnumsExpressions).size <= 1) { + "Only one of listEnums, listEnums_Untyped, listEnumsExpression, and listEnumsExpressions must be set, but not multiple" } - require(!((listIntSpecial != null) && (listIntSpecial_Untyped != null))) { - "Only listIntSpecial or listIntSpecial_Untyped must be set, but not both" + require(listOfNotNull(listIntSpecial, listIntSpecial_Untyped, listIntSpecialExpression, listIntSpecialExpressions).size <= 1) { + "Only one of listIntSpecial, listIntSpecial_Untyped, listIntSpecialExpression, and listIntSpecialExpressions must be set, but not multiple" } } @@ -247,59 +347,91 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( vararg pleaseUseNamedArguments: Unit, fooBar: String? = null, fooBar_Untyped: String? = null, + fooBarExpression: Expression? = null, bazGoo: Boolean? = null, bazGoo_Untyped: String? = null, + bazGooExpression: Expression? = null, binKin: Boolean? = null, binKin_Untyped: String? = null, + binKinExpression: Expression? = null, intPint: Int? = null, intPint_Untyped: String? = null, + intPintExpression: Expression? = null, floPint: Float? = null, floPint_Untyped: String? = null, + floPintExpression: Expression? = null, finBin: ActionWithAllTypesOfInputsBindingV2.Bin? = null, finBin_Untyped: String? = null, + finBinExpression: Expression? = null, gooZen: ActionWithAllTypesOfInputsBindingV2.Zen? = null, gooZen_Untyped: String? = null, + gooZenExpression: Expression? = null, bahEnum: ActionWithAllTypesOfInputsBindingV2.BahEnum? = null, bahEnum_Untyped: String? = null, + bahEnumExpression: Expression? = null, listStrings: List? = null, listStrings_Untyped: String? = null, + listStringsExpression: Expression>? = null, + listStringsExpressions: List>? = null, listInts: List? = null, listInts_Untyped: String? = null, + listIntsExpression: Expression>? = null, + listIntsExpressions: List>? = null, listEnums: List? = null, listEnums_Untyped: String? = null, + listEnumsExpression: Expression>? = null, + listEnumsExpressions: List>? = null, listIntSpecial: List? = null, listIntSpecial_Untyped: String? = null, + listIntSpecialExpression: Expression>? = null, + listIntSpecialExpressions: List>? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, bazGoo = bazGoo, bazGoo_Untyped = bazGoo_Untyped, binKin = binKin, binKin_Untyped = binKin_Untyped, intPint = intPint, intPint_Untyped = intPint_Untyped, floPint = floPint, floPint_Untyped = floPint_Untyped, finBin = finBin, finBin_Untyped = finBin_Untyped, gooZen = gooZen, gooZen_Untyped = gooZen_Untyped, bahEnum = bahEnum, bahEnum_Untyped = bahEnum_Untyped, listStrings = listStrings, listStrings_Untyped = listStrings_Untyped, listInts = listInts, listInts_Untyped = listInts_Untyped, listEnums = listEnums, listEnums_Untyped = listEnums_Untyped, listIntSpecial = listIntSpecial, listIntSpecial_Untyped = listIntSpecial_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, fooBarExpression = fooBarExpression, bazGoo = bazGoo, bazGoo_Untyped = bazGoo_Untyped, bazGooExpression = bazGooExpression, binKin = binKin, binKin_Untyped = binKin_Untyped, binKinExpression = binKinExpression, intPint = intPint, intPint_Untyped = intPint_Untyped, intPintExpression = intPintExpression, floPint = floPint, floPint_Untyped = floPint_Untyped, floPintExpression = floPintExpression, finBin = finBin, finBin_Untyped = finBin_Untyped, finBinExpression = finBinExpression, gooZen = gooZen, gooZen_Untyped = gooZen_Untyped, gooZenExpression = gooZenExpression, bahEnum = bahEnum, bahEnum_Untyped = bahEnum_Untyped, bahEnumExpression = bahEnumExpression, listStrings = listStrings, listStrings_Untyped = listStrings_Untyped, listStringsExpression = listStringsExpression, listStringsExpressions = listStringsExpressions, listInts = listInts, listInts_Untyped = listInts_Untyped, listIntsExpression = listIntsExpression, listIntsExpressions = listIntsExpressions, listEnums = listEnums, listEnums_Untyped = listEnums_Untyped, listEnumsExpression = listEnumsExpression, listEnumsExpressions = listEnumsExpressions, listIntSpecial = listIntSpecial, listIntSpecial_Untyped = listIntSpecial_Untyped, listIntSpecialExpression = listIntSpecialExpression, listIntSpecialExpressions = listIntSpecialExpressions, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( fooBar?.let { "foo-bar" to it }, fooBar_Untyped?.let { "foo-bar" to it }, + fooBarExpression?.let { "foo-bar" to it.expressionString }, bazGoo?.let { "baz-goo" to it.toString() }, bazGoo_Untyped?.let { "baz-goo" to it }, + bazGooExpression?.let { "baz-goo" to it.expressionString }, binKin?.let { "bin-kin" to it.toString() }, binKin_Untyped?.let { "bin-kin" to it }, + binKinExpression?.let { "bin-kin" to it.expressionString }, intPint?.let { "int-pint" to it.toString() }, intPint_Untyped?.let { "int-pint" to it }, + intPintExpression?.let { "int-pint" to it.expressionString }, floPint?.let { "flo-pint" to it.toString() }, floPint_Untyped?.let { "flo-pint" to it }, + floPintExpression?.let { "flo-pint" to it.expressionString }, finBin?.let { "fin-bin" to it.stringValue }, finBin_Untyped?.let { "fin-bin" to it }, + finBinExpression?.let { "fin-bin" to it.expressionString }, gooZen?.let { "goo-zen" to it.integerValue.toString() }, gooZen_Untyped?.let { "goo-zen" to it }, + gooZenExpression?.let { "goo-zen" to it.expressionString }, bahEnum?.let { "bah-enum" to it.stringValue }, bahEnum_Untyped?.let { "bah-enum" to it }, + bahEnumExpression?.let { "bah-enum" to it.expressionString }, listStrings?.let { "list-strings" to it.joinToString(",") }, listStrings_Untyped?.let { "list-strings" to it }, + listStringsExpression?.let { "list-strings" to it.expressionString }, + listStringsExpressions?.let { "list-strings" to it.joinToString(" ", transform = Expression<*>::expressionString) }, listInts?.let { "list-ints" to it.joinToString(",") { it.toString() } }, listInts_Untyped?.let { "list-ints" to it }, + listIntsExpression?.let { "list-ints" to it.expressionString }, + listIntsExpressions?.let { "list-ints" to it.joinToString(" ", transform = Expression<*>::expressionString) }, listEnums?.let { "list-enums" to it.joinToString(",") { it.stringValue } }, listEnums_Untyped?.let { "list-enums" to it }, + listEnumsExpression?.let { "list-enums" to it.expressionString }, + listEnumsExpressions?.let { "list-enums" to it.joinToString(" ", transform = Expression<*>::expressionString) }, listIntSpecial?.let { "list-int-special" to it.joinToString(",") { it.integerValue.toString() } }, listIntSpecial_Untyped?.let { "list-int-special" to it }, + listIntSpecialExpression?.let { "list-int-special" to it.expressionString }, + listIntSpecialExpressions?.let { "list-int-special" to it.joinToString(" ", transform = Expression<*>::expressionString) }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) @@ -370,8 +502,132 @@ public data class ActionWithAllTypesOfInputsBindingV2 private constructor( stepId: String, ) : Action.Outputs(stepId) { /** - * Cool output! + * Short description output + */ + public val fooBar: Expression = Expression("steps.$stepId.outputs.foo-bar") + + /** + * Short description output + */ + public val fooBar_Untyped: Expression = Expression("steps.$stepId.outputs.foo-bar") + + /** + * First boolean input! output + */ + public val bazGoo: Expression = Expression("steps.$stepId.outputs.baz-goo") + + /** + * First boolean input! output + */ + public val bazGoo_Untyped: Expression = Expression("steps.$stepId.outputs.baz-goo") + + /** + * Boolean and nullable output + */ + public val binKin: Expression = Expression("steps.$stepId.outputs.bin-kin") + + /** + * Boolean and nullable output + */ + public val binKin_Untyped: Expression = Expression("steps.$stepId.outputs.bin-kin") + + /** + * Integer output + */ + public val intPint: Expression = Expression("steps.$stepId.outputs.int-pint") + + /** + * Integer output + */ + public val intPint_Untyped: Expression = Expression("steps.$stepId.outputs.int-pint") + + /** + * Float output + */ + public val floPint: Expression = Expression("steps.$stepId.outputs.flo-pint") + + /** + * Float output + */ + public val floPint_Untyped: Expression = Expression("steps.$stepId.outputs.flo-pint") + + /** + * Enumeration output + */ + public val finBin: Expression = + Expression("steps.$stepId.outputs.fin-bin") + + /** + * Enumeration output + */ + public val finBin_Untyped: Expression = Expression("steps.$stepId.outputs.fin-bin") + + /** + * Integer with special value output + */ + public val gooZen: Expression = + Expression("steps.$stepId.outputs.goo-zen") + + /** + * Integer with special value output + */ + public val gooZen_Untyped: Expression = Expression("steps.$stepId.outputs.goo-zen") + + /** + * Enum with custom naming output + */ + public val bahEnum: Expression = + Expression("steps.$stepId.outputs.bah-enum") + + /** + * Enum with custom naming output + */ + public val bahEnum_Untyped: Expression = Expression("steps.$stepId.outputs.bah-enum") + + /** + * List of strings output + */ + public val listStrings: Expression> = + Expression("steps.$stepId.outputs.list-strings") + + /** + * List of strings output + */ + public val listStrings_Untyped: Expression = + Expression("steps.$stepId.outputs.list-strings") + + /** + * List of integers output + */ + public val listInts: Expression> = Expression("steps.$stepId.outputs.list-ints") + + /** + * List of integers output + */ + public val listInts_Untyped: Expression = Expression("steps.$stepId.outputs.list-ints") + + /** + * List of enums output + */ + public val listEnums: Expression> = + Expression("steps.$stepId.outputs.list-enums") + + /** + * List of enums output + */ + public val listEnums_Untyped: Expression = + Expression("steps.$stepId.outputs.list-enums") + + /** + * List of integer with special values output + */ + public val listIntSpecial: Expression> = + Expression("steps.$stepId.outputs.list-int-special") + + /** + * List of integer with special values output */ - public val bazGoo: String = "steps.$stepId.outputs.baz-goo" + public val listIntSpecial_Untyped: Expression = + Expression("steps.$stepId.outputs.list-int-special") } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV2_Untyped.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV2_Untyped.kt index 596d9c327a..d127abcf91 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV2_Untyped.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsBindingV2_Untyped.kt @@ -8,9 +8,11 @@ package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap +import kotlin.Any import kotlin.Deprecated import kotlin.ExposedCopyVisibility import kotlin.String @@ -48,17 +50,29 @@ import kotlin.collections.toTypedArray * [Action on GitHub](https://github.com/john-smith/action-with-all-types-of-inputs-binding-v2) * * @param fooBar_Untyped Short description + * @param fooBarExpression Short description * @param bazGoo_Untyped First boolean input! + * @param bazGooExpression First boolean input! * @param binKin_Untyped Boolean and nullable + * @param binKinExpression Boolean and nullable * @param intPint_Untyped Integer + * @param intPintExpression Integer * @param floPint_Untyped Float + * @param floPintExpression Float * @param finBin_Untyped Enumeration + * @param finBinExpression Enumeration * @param gooZen_Untyped Integer with special value + * @param gooZenExpression Integer with special value * @param bahEnum_Untyped Enum with custom naming + * @param bahEnumExpression Enum with custom naming * @param listStrings_Untyped List of strings + * @param listStringsExpression List of strings * @param listInts_Untyped List of integers + * @param listIntsExpression List of integers * @param listEnums_Untyped List of enums + * @param listEnumsExpression List of enums * @param listIntSpecial_Untyped List of integer with special values + * @param listIntSpecialExpression List of integer with special values * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -71,51 +85,99 @@ public data class ActionWithAllTypesOfInputsBindingV2_Untyped private constructo /** * Short description */ - public val fooBar_Untyped: String, + public val fooBar_Untyped: String? = null, + /** + * Short description + */ + public val fooBarExpression: Expression? = null, /** * First boolean input! */ - public val bazGoo_Untyped: String, + public val bazGoo_Untyped: String? = null, + /** + * First boolean input! + */ + public val bazGooExpression: Expression? = null, /** * Boolean and nullable */ public val binKin_Untyped: String? = null, + /** + * Boolean and nullable + */ + public val binKinExpression: Expression? = null, + /** + * Integer + */ + public val intPint_Untyped: String? = null, /** * Integer */ - public val intPint_Untyped: String, + public val intPintExpression: Expression? = null, /** * Float */ - public val floPint_Untyped: String, + public val floPint_Untyped: String? = null, + /** + * Float + */ + public val floPintExpression: Expression? = null, + /** + * Enumeration + */ + public val finBin_Untyped: String? = null, /** * Enumeration */ - public val finBin_Untyped: String, + public val finBinExpression: Expression? = null, + /** + * Integer with special value + */ + public val gooZen_Untyped: String? = null, /** * Integer with special value */ - public val gooZen_Untyped: String, + public val gooZenExpression: Expression? = null, + /** + * Enum with custom naming + */ + public val bahEnum_Untyped: String? = null, /** * Enum with custom naming */ - public val bahEnum_Untyped: String, + public val bahEnumExpression: Expression? = null, /** * List of strings */ public val listStrings_Untyped: String? = null, + /** + * List of strings + */ + public val listStringsExpression: Expression? = null, /** * List of integers */ public val listInts_Untyped: String? = null, + /** + * List of integers + */ + public val listIntsExpression: Expression? = null, /** * List of enums */ public val listEnums_Untyped: String? = null, + /** + * List of enums + */ + public val listEnumsExpression: Expression? = null, /** * List of integer with special values */ public val listIntSpecial_Untyped: String? = null, + /** + * List of integer with special values + */ + public val listIntSpecialExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -134,41 +196,133 @@ public data class ActionWithAllTypesOfInputsBindingV2_Untyped private constructo """.trimMargin()) } + require(listOfNotNull(fooBar_Untyped, fooBarExpression).size <= 1) { + "Only one of fooBar_Untyped, and fooBarExpression must be set, but not multiple" + } + require((fooBar_Untyped != null) || (fooBarExpression != null)) { + "Either fooBar_Untyped, or fooBarExpression must be set, one of them is required" + } + + require(listOfNotNull(bazGoo_Untyped, bazGooExpression).size <= 1) { + "Only one of bazGoo_Untyped, and bazGooExpression must be set, but not multiple" + } + require((bazGoo_Untyped != null) || (bazGooExpression != null)) { + "Either bazGoo_Untyped, or bazGooExpression must be set, one of them is required" + } + + require(listOfNotNull(binKin_Untyped, binKinExpression).size <= 1) { + "Only one of binKin_Untyped, and binKinExpression must be set, but not multiple" + } + + require(listOfNotNull(intPint_Untyped, intPintExpression).size <= 1) { + "Only one of intPint_Untyped, and intPintExpression must be set, but not multiple" + } + require((intPint_Untyped != null) || (intPintExpression != null)) { + "Either intPint_Untyped, or intPintExpression must be set, one of them is required" + } + + require(listOfNotNull(floPint_Untyped, floPintExpression).size <= 1) { + "Only one of floPint_Untyped, and floPintExpression must be set, but not multiple" + } + require((floPint_Untyped != null) || (floPintExpression != null)) { + "Either floPint_Untyped, or floPintExpression must be set, one of them is required" + } + + require(listOfNotNull(finBin_Untyped, finBinExpression).size <= 1) { + "Only one of finBin_Untyped, and finBinExpression must be set, but not multiple" + } + require((finBin_Untyped != null) || (finBinExpression != null)) { + "Either finBin_Untyped, or finBinExpression must be set, one of them is required" + } + + require(listOfNotNull(gooZen_Untyped, gooZenExpression).size <= 1) { + "Only one of gooZen_Untyped, and gooZenExpression must be set, but not multiple" + } + require((gooZen_Untyped != null) || (gooZenExpression != null)) { + "Either gooZen_Untyped, or gooZenExpression must be set, one of them is required" + } + + require(listOfNotNull(bahEnum_Untyped, bahEnumExpression).size <= 1) { + "Only one of bahEnum_Untyped, and bahEnumExpression must be set, but not multiple" + } + require((bahEnum_Untyped != null) || (bahEnumExpression != null)) { + "Either bahEnum_Untyped, or bahEnumExpression must be set, one of them is required" + } + + require(listOfNotNull(listStrings_Untyped, listStringsExpression).size <= 1) { + "Only one of listStrings_Untyped, and listStringsExpression must be set, but not multiple" + } + + require(listOfNotNull(listInts_Untyped, listIntsExpression).size <= 1) { + "Only one of listInts_Untyped, and listIntsExpression must be set, but not multiple" + } + + require(listOfNotNull(listEnums_Untyped, listEnumsExpression).size <= 1) { + "Only one of listEnums_Untyped, and listEnumsExpression must be set, but not multiple" + } + + require(listOfNotNull(listIntSpecial_Untyped, listIntSpecialExpression).size <= 1) { + "Only one of listIntSpecial_Untyped, and listIntSpecialExpression must be set, but not multiple" + } } public constructor( vararg pleaseUseNamedArguments: Unit, - fooBar_Untyped: String, - bazGoo_Untyped: String, + fooBar_Untyped: String? = null, + fooBarExpression: Expression? = null, + bazGoo_Untyped: String? = null, + bazGooExpression: Expression? = null, binKin_Untyped: String? = null, - intPint_Untyped: String, - floPint_Untyped: String, - finBin_Untyped: String, - gooZen_Untyped: String, - bahEnum_Untyped: String, + binKinExpression: Expression? = null, + intPint_Untyped: String? = null, + intPintExpression: Expression? = null, + floPint_Untyped: String? = null, + floPintExpression: Expression? = null, + finBin_Untyped: String? = null, + finBinExpression: Expression? = null, + gooZen_Untyped: String? = null, + gooZenExpression: Expression? = null, + bahEnum_Untyped: String? = null, + bahEnumExpression: Expression? = null, listStrings_Untyped: String? = null, + listStringsExpression: Expression? = null, listInts_Untyped: String? = null, + listIntsExpression: Expression? = null, listEnums_Untyped: String? = null, + listEnumsExpression: Expression? = null, listIntSpecial_Untyped: String? = null, + listIntSpecialExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(fooBar_Untyped = fooBar_Untyped, bazGoo_Untyped = bazGoo_Untyped, binKin_Untyped = binKin_Untyped, intPint_Untyped = intPint_Untyped, floPint_Untyped = floPint_Untyped, finBin_Untyped = finBin_Untyped, gooZen_Untyped = gooZen_Untyped, bahEnum_Untyped = bahEnum_Untyped, listStrings_Untyped = listStrings_Untyped, listInts_Untyped = listInts_Untyped, listEnums_Untyped = listEnums_Untyped, listIntSpecial_Untyped = listIntSpecial_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(fooBar_Untyped = fooBar_Untyped, fooBarExpression = fooBarExpression, bazGoo_Untyped = bazGoo_Untyped, bazGooExpression = bazGooExpression, binKin_Untyped = binKin_Untyped, binKinExpression = binKinExpression, intPint_Untyped = intPint_Untyped, intPintExpression = intPintExpression, floPint_Untyped = floPint_Untyped, floPintExpression = floPintExpression, finBin_Untyped = finBin_Untyped, finBinExpression = finBinExpression, gooZen_Untyped = gooZen_Untyped, gooZenExpression = gooZenExpression, bahEnum_Untyped = bahEnum_Untyped, bahEnumExpression = bahEnumExpression, listStrings_Untyped = listStrings_Untyped, listStringsExpression = listStringsExpression, listInts_Untyped = listInts_Untyped, listIntsExpression = listIntsExpression, listEnums_Untyped = listEnums_Untyped, listEnumsExpression = listEnumsExpression, listIntSpecial_Untyped = listIntSpecial_Untyped, listIntSpecialExpression = listIntSpecialExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( - "foo-bar" to fooBar_Untyped, - "baz-goo" to bazGoo_Untyped, + fooBar_Untyped?.let { "foo-bar" to it }, + fooBarExpression?.let { "foo-bar" to it.expressionString }, + bazGoo_Untyped?.let { "baz-goo" to it }, + bazGooExpression?.let { "baz-goo" to it.expressionString }, binKin_Untyped?.let { "bin-kin" to it }, - "int-pint" to intPint_Untyped, - "flo-pint" to floPint_Untyped, - "fin-bin" to finBin_Untyped, - "goo-zen" to gooZen_Untyped, - "bah-enum" to bahEnum_Untyped, + binKinExpression?.let { "bin-kin" to it.expressionString }, + intPint_Untyped?.let { "int-pint" to it }, + intPintExpression?.let { "int-pint" to it.expressionString }, + floPint_Untyped?.let { "flo-pint" to it }, + floPintExpression?.let { "flo-pint" to it.expressionString }, + finBin_Untyped?.let { "fin-bin" to it }, + finBinExpression?.let { "fin-bin" to it.expressionString }, + gooZen_Untyped?.let { "goo-zen" to it }, + gooZenExpression?.let { "goo-zen" to it.expressionString }, + bahEnum_Untyped?.let { "bah-enum" to it }, + bahEnumExpression?.let { "bah-enum" to it.expressionString }, listStrings_Untyped?.let { "list-strings" to it }, + listStringsExpression?.let { "list-strings" to it.expressionString }, listInts_Untyped?.let { "list-ints" to it }, + listIntsExpression?.let { "list-ints" to it.expressionString }, listEnums_Untyped?.let { "list-enums" to it }, + listEnumsExpression?.let { "list-enums" to it.expressionString }, listIntSpecial_Untyped?.let { "list-int-special" to it }, + listIntSpecialExpression?.let { "list-int-special" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) @@ -179,8 +333,66 @@ public data class ActionWithAllTypesOfInputsBindingV2_Untyped private constructo stepId: String, ) : Action.Outputs(stepId) { /** - * Cool output! + * Short description output + */ + public val fooBar_Untyped: Expression = Expression("steps.$stepId.outputs.foo-bar") + + /** + * First boolean input! output + */ + public val bazGoo_Untyped: Expression = Expression("steps.$stepId.outputs.baz-goo") + + /** + * Boolean and nullable output + */ + public val binKin_Untyped: Expression = Expression("steps.$stepId.outputs.bin-kin") + + /** + * Integer output + */ + public val intPint_Untyped: Expression = Expression("steps.$stepId.outputs.int-pint") + + /** + * Float output + */ + public val floPint_Untyped: Expression = Expression("steps.$stepId.outputs.flo-pint") + + /** + * Enumeration output + */ + public val finBin_Untyped: Expression = Expression("steps.$stepId.outputs.fin-bin") + + /** + * Integer with special value output + */ + public val gooZen_Untyped: Expression = Expression("steps.$stepId.outputs.goo-zen") + + /** + * Enum with custom naming output + */ + public val bahEnum_Untyped: Expression = Expression("steps.$stepId.outputs.bah-enum") + + /** + * List of strings output + */ + public val listStrings_Untyped: Expression = + Expression("steps.$stepId.outputs.list-strings") + + /** + * List of integers output + */ + public val listInts_Untyped: Expression = Expression("steps.$stepId.outputs.list-ints") + + /** + * List of enums output + */ + public val listEnums_Untyped: Expression = + Expression("steps.$stepId.outputs.list-enums") + + /** + * List of integer with special values output */ - public val bazGoo: String = "steps.$stepId.outputs.baz-goo" + public val listIntSpecial_Untyped: Expression = + Expression("steps.$stepId.outputs.list-int-special") } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsTest.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsTest.kt index 508421d597..f148ee677b 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsTest.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithAllTypesOfInputsTest.kt @@ -139,7 +139,10 @@ class ActionWithAllTypesOfInputsTest : DescribeSpec({ bindingVersion = bindingVersion, ) } - exception.message shouldBe "Either fooBar or fooBar_Untyped must be set, one of them is required" + when (bindingVersion) { + V1 -> exception.message shouldBe "Either fooBar, or fooBar_Untyped must be set, one of them is required" + V2 -> exception.message shouldBe "Either fooBar, fooBar_Untyped, or fooBarExpression must be set, one of them is required" + } } } @@ -158,7 +161,10 @@ class ActionWithAllTypesOfInputsTest : DescribeSpec({ ), ) } - exception.message shouldBe "Only fooBar or fooBar_Untyped must be set, but not both" + when (bindingVersion) { + V1 -> exception.message shouldBe "Only one of fooBar, and fooBar_Untyped must be set, but not multiple" + V2 -> exception.message shouldBe "Only one of fooBar, fooBar_Untyped, and fooBarExpression must be set, but not multiple" + } } } @@ -185,7 +191,10 @@ class ActionWithAllTypesOfInputsTest : DescribeSpec({ ), ) } - exception.message shouldBe "Only listStrings or listStrings_Untyped must be set, but not both" + when (bindingVersion) { + V1 -> exception.message shouldBe "Only one of listStrings, and listStrings_Untyped must be set, but not multiple" + V2 -> exception.message shouldBe "Only one of listStrings, listStrings_Untyped, listStringsExpression, and listStringsExpressions must be set, but not multiple" + } } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithDeprecatedInputAndNameClashBindingV1.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithDeprecatedInputAndNameClashBindingV1.kt index 011f5495d1..c0cac56036 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithDeprecatedInputAndNameClashBindingV1.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithDeprecatedInputAndNameClashBindingV1.kt @@ -51,11 +51,11 @@ public data class ActionWithDeprecatedInputAndNameClashBindingV1 private constru public val _customVersion: String? = null, ) : RegularAction("john-smith", "action-with-deprecated-input-and-name-clash-binding-v1", _customVersion ?: "v2") { init { - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped).size <= 1) { + "Only one of fooBar, and fooBar_Untyped must be set, but not multiple" } require((fooBar != null) || (fooBar_Untyped != null)) { - "Either fooBar or fooBar_Untyped must be set, one of them is required" + "Either fooBar, or fooBar_Untyped must be set, one of them is required" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithDeprecatedInputAndNameClashBindingV2.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithDeprecatedInputAndNameClashBindingV2.kt index 2c14723448..e12e3877ba 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithDeprecatedInputAndNameClashBindingV2.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithDeprecatedInputAndNameClashBindingV2.kt @@ -4,13 +4,16 @@ @file:Suppress( "DataClassPrivateConstructor", "UNUSED_PARAMETER", + "DEPRECATION", ) package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap +import kotlin.Deprecated import kotlin.ExposedCopyVisibility import kotlin.String import kotlin.Suppress @@ -28,6 +31,7 @@ import kotlin.collections.toTypedArray * * @param fooBar <required> Foo bar - new * @param fooBar_Untyped <required> Foo bar - new + * @param fooBarExpression <required> Foo bar - new * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -40,7 +44,12 @@ public data class ActionWithDeprecatedInputAndNameClashBindingV2 private constru /** * <required> Foo bar - new */ + @Deprecated("Use the typed property or expression property instead") public val fooBar_Untyped: String? = null, + /** + * <required> Foo bar - new + */ + public val fooBarExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -59,11 +68,11 @@ public data class ActionWithDeprecatedInputAndNameClashBindingV2 private constru """.trimMargin()) } - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped, fooBarExpression).size <= 1) { + "Only one of fooBar, fooBar_Untyped, and fooBarExpression must be set, but not multiple" } - require((fooBar != null) || (fooBar_Untyped != null)) { - "Either fooBar or fooBar_Untyped must be set, one of them is required" + require((fooBar != null) || (fooBar_Untyped != null) || (fooBarExpression != null)) { + "Either fooBar, fooBar_Untyped, or fooBarExpression must be set, one of them is required" } } @@ -71,15 +80,17 @@ public data class ActionWithDeprecatedInputAndNameClashBindingV2 private constru vararg pleaseUseNamedArguments: Unit, fooBar: String? = null, fooBar_Untyped: String? = null, + fooBarExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, fooBarExpression = fooBarExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( fooBar?.let { "fooBar" to it }, fooBar_Untyped?.let { "fooBar" to it }, + fooBarExpression?.let { "fooBar" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithFancyCharsInDocsBindingV1.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithFancyCharsInDocsBindingV1.kt index 2269ee2f66..88652f3e0a 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithFancyCharsInDocsBindingV1.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithFancyCharsInDocsBindingV1.kt @@ -61,12 +61,12 @@ public data class ActionWithFancyCharsInDocsBindingV1 private constructor( public val _customVersion: String? = null, ) : RegularAction("john-smith", "action-with-fancy-chars-in-docs-binding-v1", _customVersion ?: "v3") { init { - require(!((nestedKotlinComments != null) && (nestedKotlinComments_Untyped != null))) { - "Only nestedKotlinComments or nestedKotlinComments_Untyped must be set, but not both" + require(listOfNotNull(nestedKotlinComments, nestedKotlinComments_Untyped).size <= 1) { + "Only one of nestedKotlinComments, and nestedKotlinComments_Untyped must be set, but not multiple" } - require(!((percent != null) && (percent_Untyped != null))) { - "Only percent or percent_Untyped must be set, but not both" + require(listOfNotNull(percent, percent_Untyped).size <= 1) { + "Only one of percent, and percent_Untyped must be set, but not multiple" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithFancyCharsInDocsBindingV2.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithFancyCharsInDocsBindingV2.kt index 55329df926..058761cfd1 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithFancyCharsInDocsBindingV2.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithFancyCharsInDocsBindingV2.kt @@ -4,13 +4,16 @@ @file:Suppress( "DataClassPrivateConstructor", "UNUSED_PARAMETER", + "DEPRECATION", ) package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap +import kotlin.Deprecated import kotlin.ExposedCopyVisibility import kotlin.String import kotlin.Suppress @@ -28,8 +31,10 @@ import kotlin.collections.toTypedArray * * @param nestedKotlinComments This is a /* test */ * @param nestedKotlinComments_Untyped This is a /* test */ + * @param nestedKotlinCommentsExpression This is a /* test */ * @param percent For example "100%" * @param percent_Untyped For example "100%" + * @param percentExpression For example "100%" * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -42,7 +47,12 @@ public data class ActionWithFancyCharsInDocsBindingV2 private constructor( /** * This is a /* test */ */ + @Deprecated("Use the typed property or expression property instead") public val nestedKotlinComments_Untyped: String? = null, + /** + * This is a /* test */ + */ + public val nestedKotlinCommentsExpression: Expression? = null, /** * For example "100%" */ @@ -50,7 +60,12 @@ public data class ActionWithFancyCharsInDocsBindingV2 private constructor( /** * For example "100%" */ + @Deprecated("Use the typed property or expression property instead") public val percent_Untyped: String? = null, + /** + * For example "100%" + */ + public val percentExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -69,12 +84,12 @@ public data class ActionWithFancyCharsInDocsBindingV2 private constructor( """.trimMargin()) } - require(!((nestedKotlinComments != null) && (nestedKotlinComments_Untyped != null))) { - "Only nestedKotlinComments or nestedKotlinComments_Untyped must be set, but not both" + require(listOfNotNull(nestedKotlinComments, nestedKotlinComments_Untyped, nestedKotlinCommentsExpression).size <= 1) { + "Only one of nestedKotlinComments, nestedKotlinComments_Untyped, and nestedKotlinCommentsExpression must be set, but not multiple" } - require(!((percent != null) && (percent_Untyped != null))) { - "Only percent or percent_Untyped must be set, but not both" + require(listOfNotNull(percent, percent_Untyped, percentExpression).size <= 1) { + "Only one of percent, percent_Untyped, and percentExpression must be set, but not multiple" } } @@ -82,19 +97,23 @@ public data class ActionWithFancyCharsInDocsBindingV2 private constructor( vararg pleaseUseNamedArguments: Unit, nestedKotlinComments: String? = null, nestedKotlinComments_Untyped: String? = null, + nestedKotlinCommentsExpression: Expression? = null, percent: String? = null, percent_Untyped: String? = null, + percentExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(nestedKotlinComments = nestedKotlinComments, nestedKotlinComments_Untyped = nestedKotlinComments_Untyped, percent = percent, percent_Untyped = percent_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(nestedKotlinComments = nestedKotlinComments, nestedKotlinComments_Untyped = nestedKotlinComments_Untyped, nestedKotlinCommentsExpression = nestedKotlinCommentsExpression, percent = percent, percent_Untyped = percent_Untyped, percentExpression = percentExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( nestedKotlinComments?.let { "nested-kotlin-comments" to it }, nestedKotlinComments_Untyped?.let { "nested-kotlin-comments" to it }, + nestedKotlinCommentsExpression?.let { "nested-kotlin-comments" to it.expressionString }, percent?.let { "percent" to it }, percent_Untyped?.let { "percent" to it }, + percentExpression?.let { "percent" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithInputsSharingTypeBindingV1.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithInputsSharingTypeBindingV1.kt index a706fb9cf2..a31b3a6065 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithInputsSharingTypeBindingV1.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithInputsSharingTypeBindingV1.kt @@ -64,22 +64,22 @@ public data class ActionWithInputsSharingTypeBindingV1 private constructor( public val _customVersion: String? = null, ) : RegularAction("john-smith", "action-with-inputs-sharing-type-binding-v1", _customVersion ?: "v3") { init { - require(!((fooOne != null) && (fooOne_Untyped != null))) { - "Only fooOne or fooOne_Untyped must be set, but not both" + require(listOfNotNull(fooOne, fooOne_Untyped).size <= 1) { + "Only one of fooOne, and fooOne_Untyped must be set, but not multiple" } require((fooOne != null) || (fooOne_Untyped != null)) { - "Either fooOne or fooOne_Untyped must be set, one of them is required" + "Either fooOne, or fooOne_Untyped must be set, one of them is required" } - require(!((fooTwo != null) && (fooTwo_Untyped != null))) { - "Only fooTwo or fooTwo_Untyped must be set, but not both" + require(listOfNotNull(fooTwo, fooTwo_Untyped).size <= 1) { + "Only one of fooTwo, and fooTwo_Untyped must be set, but not multiple" } require((fooTwo != null) || (fooTwo_Untyped != null)) { - "Either fooTwo or fooTwo_Untyped must be set, one of them is required" + "Either fooTwo, or fooTwo_Untyped must be set, one of them is required" } - require(!((fooThree != null) && (fooThree_Untyped != null))) { - "Only fooThree or fooThree_Untyped must be set, but not both" + require(listOfNotNull(fooThree, fooThree_Untyped).size <= 1) { + "Only one of fooThree, and fooThree_Untyped must be set, but not multiple" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithInputsSharingTypeBindingV2.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithInputsSharingTypeBindingV2.kt index 026e146b24..a15a2b1db7 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithInputsSharingTypeBindingV2.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithInputsSharingTypeBindingV2.kt @@ -4,13 +4,16 @@ @file:Suppress( "DataClassPrivateConstructor", "UNUSED_PARAMETER", + "DEPRECATION", ) package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap +import kotlin.Deprecated import kotlin.ExposedCopyVisibility import kotlin.Int import kotlin.String @@ -29,8 +32,10 @@ import kotlin.collections.toTypedArray * * @param fooOne <required> * @param fooOne_Untyped <required> + * @param fooOneExpression <required> * @param fooTwo <required> * @param fooTwo_Untyped <required> + * @param fooTwoExpression <required> * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -43,7 +48,12 @@ public data class ActionWithInputsSharingTypeBindingV2 private constructor( /** * <required> */ + @Deprecated("Use the typed property or expression property instead") public val fooOne_Untyped: String? = null, + /** + * <required> + */ + public val fooOneExpression: Expression? = null, /** * <required> */ @@ -51,9 +61,16 @@ public data class ActionWithInputsSharingTypeBindingV2 private constructor( /** * <required> */ + @Deprecated("Use the typed property or expression property instead") public val fooTwo_Untyped: String? = null, + /** + * <required> + */ + public val fooTwoExpression: Expression? = null, public val fooThree: ActionWithInputsSharingTypeBindingV2.Foo? = null, + @Deprecated("Use the typed property or expression property instead") public val fooThree_Untyped: String? = null, + public val fooThreeExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -72,22 +89,22 @@ public data class ActionWithInputsSharingTypeBindingV2 private constructor( """.trimMargin()) } - require(!((fooOne != null) && (fooOne_Untyped != null))) { - "Only fooOne or fooOne_Untyped must be set, but not both" + require(listOfNotNull(fooOne, fooOne_Untyped, fooOneExpression).size <= 1) { + "Only one of fooOne, fooOne_Untyped, and fooOneExpression must be set, but not multiple" } - require((fooOne != null) || (fooOne_Untyped != null)) { - "Either fooOne or fooOne_Untyped must be set, one of them is required" + require((fooOne != null) || (fooOne_Untyped != null) || (fooOneExpression != null)) { + "Either fooOne, fooOne_Untyped, or fooOneExpression must be set, one of them is required" } - require(!((fooTwo != null) && (fooTwo_Untyped != null))) { - "Only fooTwo or fooTwo_Untyped must be set, but not both" + require(listOfNotNull(fooTwo, fooTwo_Untyped, fooTwoExpression).size <= 1) { + "Only one of fooTwo, fooTwo_Untyped, and fooTwoExpression must be set, but not multiple" } - require((fooTwo != null) || (fooTwo_Untyped != null)) { - "Either fooTwo or fooTwo_Untyped must be set, one of them is required" + require((fooTwo != null) || (fooTwo_Untyped != null) || (fooTwoExpression != null)) { + "Either fooTwo, fooTwo_Untyped, or fooTwoExpression must be set, one of them is required" } - require(!((fooThree != null) && (fooThree_Untyped != null))) { - "Only fooThree or fooThree_Untyped must be set, but not both" + require(listOfNotNull(fooThree, fooThree_Untyped, fooThreeExpression).size <= 1) { + "Only one of fooThree, fooThree_Untyped, and fooThreeExpression must be set, but not multiple" } } @@ -95,23 +112,29 @@ public data class ActionWithInputsSharingTypeBindingV2 private constructor( vararg pleaseUseNamedArguments: Unit, fooOne: ActionWithInputsSharingTypeBindingV2.Foo? = null, fooOne_Untyped: String? = null, + fooOneExpression: Expression? = null, fooTwo: ActionWithInputsSharingTypeBindingV2.Foo? = null, fooTwo_Untyped: String? = null, + fooTwoExpression: Expression? = null, fooThree: ActionWithInputsSharingTypeBindingV2.Foo? = null, fooThree_Untyped: String? = null, + fooThreeExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(fooOne = fooOne, fooOne_Untyped = fooOne_Untyped, fooTwo = fooTwo, fooTwo_Untyped = fooTwo_Untyped, fooThree = fooThree, fooThree_Untyped = fooThree_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(fooOne = fooOne, fooOne_Untyped = fooOne_Untyped, fooOneExpression = fooOneExpression, fooTwo = fooTwo, fooTwo_Untyped = fooTwo_Untyped, fooTwoExpression = fooTwoExpression, fooThree = fooThree, fooThree_Untyped = fooThree_Untyped, fooThreeExpression = fooThreeExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( fooOne?.let { "foo-one" to it.integerValue.toString() }, fooOne_Untyped?.let { "foo-one" to it }, + fooOneExpression?.let { "foo-one" to it.expressionString }, fooTwo?.let { "foo-two" to it.integerValue.toString() }, fooTwo_Untyped?.let { "foo-two" to it }, + fooTwoExpression?.let { "foo-two" to it.expressionString }, fooThree?.let { "foo-three" to it.integerValue.toString() }, fooThree_Untyped?.let { "foo-three" to it }, + fooThreeExpression?.let { "foo-three" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoTypingsBindingV2_Untyped.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoTypingsBindingV2_Untyped.kt index 97db7e9c94..4983040f21 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoTypingsBindingV2_Untyped.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoTypingsBindingV2_Untyped.kt @@ -8,6 +8,7 @@ package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap @@ -51,8 +52,10 @@ import kotlin.collections.toTypedArray */ @ExposedCopyVisibility public data class ActionWithNoTypingsBindingV2_Untyped private constructor( - public val foo_Untyped: String, + public val foo_Untyped: String? = null, + public val fooExpression: Expression? = null, public val bar_Untyped: String? = null, + public val barExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -71,21 +74,35 @@ public data class ActionWithNoTypingsBindingV2_Untyped private constructor( """.trimMargin()) } + require(listOfNotNull(foo_Untyped, fooExpression).size <= 1) { + "Only one of foo_Untyped, and fooExpression must be set, but not multiple" + } + require((foo_Untyped != null) || (fooExpression != null)) { + "Either foo_Untyped, or fooExpression must be set, one of them is required" + } + + require(listOfNotNull(bar_Untyped, barExpression).size <= 1) { + "Only one of bar_Untyped, and barExpression must be set, but not multiple" + } } public constructor( vararg pleaseUseNamedArguments: Unit, - foo_Untyped: String, + foo_Untyped: String? = null, + fooExpression: Expression? = null, bar_Untyped: String? = null, + barExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(foo_Untyped = foo_Untyped, bar_Untyped = bar_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(foo_Untyped = foo_Untyped, fooExpression = fooExpression, bar_Untyped = bar_Untyped, barExpression = barExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( - "foo" to foo_Untyped, + foo_Untyped?.let { "foo" to it }, + fooExpression?.let { "foo" to it.expressionString }, bar_Untyped?.let { "bar" to it }, + barExpression?.let { "bar" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsBindingV1.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsBindingV1.kt index 0dd782e010..dad9bf004b 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsBindingV1.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsBindingV1.kt @@ -51,11 +51,11 @@ public data class ActionWithOutputsBindingV1 private constructor( public val _customVersion: String? = null, ) : RegularAction("john-smith", "action-with-outputs-binding-v1", _customVersion ?: "v3") { init { - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped).size <= 1) { + "Only one of fooBar, and fooBar_Untyped must be set, but not multiple" } require((fooBar != null) || (fooBar_Untyped != null)) { - "Either fooBar or fooBar_Untyped must be set, one of them is required" + "Either fooBar, or fooBar_Untyped must be set, one of them is required" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsBindingV2.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsBindingV2.kt index 41d06e17f7..6da81821f0 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsBindingV2.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsBindingV2.kt @@ -4,13 +4,17 @@ @file:Suppress( "DataClassPrivateConstructor", "UNUSED_PARAMETER", + "DEPRECATION", ) package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap +import kotlin.Any +import kotlin.Deprecated import kotlin.ExposedCopyVisibility import kotlin.String import kotlin.Suppress @@ -28,6 +32,7 @@ import kotlin.collections.toTypedArray * * @param fooBar <required> Short description * @param fooBar_Untyped <required> Short description + * @param fooBarExpression <required> Short description * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -40,7 +45,12 @@ public data class ActionWithOutputsBindingV2 private constructor( /** * <required> Short description */ + @Deprecated("Use the typed property or expression property instead") public val fooBar_Untyped: String? = null, + /** + * <required> Short description + */ + public val fooBarExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -59,11 +69,11 @@ public data class ActionWithOutputsBindingV2 private constructor( """.trimMargin()) } - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped, fooBarExpression).size <= 1) { + "Only one of fooBar, fooBar_Untyped, and fooBarExpression must be set, but not multiple" } - require((fooBar != null) || (fooBar_Untyped != null)) { - "Either fooBar or fooBar_Untyped must be set, one of them is required" + require((fooBar != null) || (fooBar_Untyped != null) || (fooBarExpression != null)) { + "Either fooBar, fooBar_Untyped, or fooBarExpression must be set, one of them is required" } } @@ -71,15 +81,17 @@ public data class ActionWithOutputsBindingV2 private constructor( vararg pleaseUseNamedArguments: Unit, fooBar: String? = null, fooBar_Untyped: String? = null, + fooBarExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, fooBarExpression = fooBarExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( fooBar?.let { "foo-bar" to it }, fooBar_Untyped?.let { "foo-bar" to it }, + fooBarExpression?.let { "foo-bar" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) @@ -92,11 +104,11 @@ public data class ActionWithOutputsBindingV2 private constructor( /** * Cool output! */ - public val bazGoo: String = "steps.$stepId.outputs.baz-goo" + public val bazGoo_Untyped: Expression = Expression("steps.$stepId.outputs.baz-goo") /** * Another output... */ - public val looWoz: String = "steps.$stepId.outputs.loo-woz" + public val looWoz_Untyped: Expression = Expression("steps.$stepId.outputs.loo-woz") } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsTest.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsTest.kt index 6d5c4c571a..0935602caa 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsTest.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithOutputsTest.kt @@ -6,6 +6,7 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.versioning.Bind import io.github.typesafegithub.workflows.actionbindinggenerator.withAllBindingVersions import io.github.typesafegithub.workflows.actions.johnsmith.ActionWithOutputsBindingV1 import io.github.typesafegithub.workflows.actions.johnsmith.ActionWithOutputsBindingV2 +import io.github.typesafegithub.workflows.domain.Expression import io.kotest.core.spec.style.DescribeSpec import io.kotest.matchers.shouldBe @@ -30,11 +31,11 @@ class ActionWithOutputsTest : DescribeSpec({ V2 -> { outputs as ActionWithOutputsBindingV2.Outputs - outputs.bazGoo shouldBe "steps.someStepId.outputs.baz-goo" - outputs.looWoz shouldBe "steps.someStepId.outputs.loo-woz" + outputs.bazGoo_Untyped shouldBe Expression("steps.someStepId.outputs.baz-goo") + outputs.looWoz_Untyped shouldBe Expression("steps.someStepId.outputs.loo-woz") } } - outputs["custom-output"] shouldBe "steps.someStepId.outputs.custom-output" + outputs["custom-output"] shouldBe Expression("steps.someStepId.outputs.custom-output") } } }) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV1.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV1.kt index 002287e0ef..88f1b67c09 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV1.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV1.kt @@ -54,11 +54,11 @@ public data class ActionWithPartlyTypingsBindingV1 private constructor( public val _customVersion: String? = null, ) : RegularAction("john-smith", "action-with-partly-typings-binding-v1", _customVersion ?: "v3") { init { - require(!((foo != null) && (foo_Untyped != null))) { - "Only foo or foo_Untyped must be set, but not both" + require(listOfNotNull(foo, foo_Untyped).size <= 1) { + "Only one of foo, and foo_Untyped must be set, but not multiple" } require((foo != null) || (foo_Untyped != null)) { - "Either foo or foo_Untyped must be set, one of them is required" + "Either foo, or foo_Untyped must be set, one of them is required" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV2.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV2.kt index 317a813b5a..faa5065ea1 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV2.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV2.kt @@ -4,13 +4,16 @@ @file:Suppress( "DataClassPrivateConstructor", "UNUSED_PARAMETER", + "DEPRECATION", ) package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap +import kotlin.Deprecated import kotlin.ExposedCopyVisibility import kotlin.Int import kotlin.String @@ -29,6 +32,7 @@ import kotlin.collections.toTypedArray * * @param foo <required> * @param foo_Untyped <required> + * @param fooExpression <required> * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -41,9 +45,16 @@ public data class ActionWithPartlyTypingsBindingV2 private constructor( /** * <required> */ + @Deprecated("Use the typed property or expression property instead") public val foo_Untyped: String? = null, + /** + * <required> + */ + public val fooExpression: Expression? = null, public val bar_Untyped: String? = null, - public val baz_Untyped: String, + public val barExpression: Expression? = null, + public val baz_Untyped: String? = null, + public val bazExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -62,11 +73,22 @@ public data class ActionWithPartlyTypingsBindingV2 private constructor( """.trimMargin()) } - require(!((foo != null) && (foo_Untyped != null))) { - "Only foo or foo_Untyped must be set, but not both" + require(listOfNotNull(foo, foo_Untyped, fooExpression).size <= 1) { + "Only one of foo, foo_Untyped, and fooExpression must be set, but not multiple" + } + require((foo != null) || (foo_Untyped != null) || (fooExpression != null)) { + "Either foo, foo_Untyped, or fooExpression must be set, one of them is required" + } + + require(listOfNotNull(bar_Untyped, barExpression).size <= 1) { + "Only one of bar_Untyped, and barExpression must be set, but not multiple" + } + + require(listOfNotNull(baz_Untyped, bazExpression).size <= 1) { + "Only one of baz_Untyped, and bazExpression must be set, but not multiple" } - require((foo != null) || (foo_Untyped != null)) { - "Either foo or foo_Untyped must be set, one of them is required" + require((baz_Untyped != null) || (bazExpression != null)) { + "Either baz_Untyped, or bazExpression must be set, one of them is required" } } @@ -74,19 +96,25 @@ public data class ActionWithPartlyTypingsBindingV2 private constructor( vararg pleaseUseNamedArguments: Unit, foo: Int? = null, foo_Untyped: String? = null, + fooExpression: Expression? = null, bar_Untyped: String? = null, - baz_Untyped: String, + barExpression: Expression? = null, + baz_Untyped: String? = null, + bazExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(foo = foo, foo_Untyped = foo_Untyped, bar_Untyped = bar_Untyped, baz_Untyped = baz_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(foo = foo, foo_Untyped = foo_Untyped, fooExpression = fooExpression, bar_Untyped = bar_Untyped, barExpression = barExpression, baz_Untyped = baz_Untyped, bazExpression = bazExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( foo?.let { "foo" to it.toString() }, foo_Untyped?.let { "foo" to it }, + fooExpression?.let { "foo" to it.expressionString }, bar_Untyped?.let { "bar" to it }, - "baz" to baz_Untyped, + barExpression?.let { "bar" to it.expressionString }, + baz_Untyped?.let { "baz" to it }, + bazExpression?.let { "baz" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV2_Untyped.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV2_Untyped.kt index 8d2d2d786c..eec4eb75c7 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV2_Untyped.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithPartlyTypingsBindingV2_Untyped.kt @@ -8,6 +8,7 @@ package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap @@ -56,9 +57,12 @@ import kotlin.collections.toTypedArray ) @ExposedCopyVisibility public data class ActionWithPartlyTypingsBindingV2_Untyped private constructor( - public val foo_Untyped: String, + public val foo_Untyped: String? = null, + public val fooExpression: Expression? = null, public val bar_Untyped: String? = null, - public val baz_Untyped: String, + public val barExpression: Expression? = null, + public val baz_Untyped: String? = null, + public val bazExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -77,23 +81,46 @@ public data class ActionWithPartlyTypingsBindingV2_Untyped private constructor( """.trimMargin()) } + require(listOfNotNull(foo_Untyped, fooExpression).size <= 1) { + "Only one of foo_Untyped, and fooExpression must be set, but not multiple" + } + require((foo_Untyped != null) || (fooExpression != null)) { + "Either foo_Untyped, or fooExpression must be set, one of them is required" + } + + require(listOfNotNull(bar_Untyped, barExpression).size <= 1) { + "Only one of bar_Untyped, and barExpression must be set, but not multiple" + } + + require(listOfNotNull(baz_Untyped, bazExpression).size <= 1) { + "Only one of baz_Untyped, and bazExpression must be set, but not multiple" + } + require((baz_Untyped != null) || (bazExpression != null)) { + "Either baz_Untyped, or bazExpression must be set, one of them is required" + } } public constructor( vararg pleaseUseNamedArguments: Unit, - foo_Untyped: String, + foo_Untyped: String? = null, + fooExpression: Expression? = null, bar_Untyped: String? = null, - baz_Untyped: String, + barExpression: Expression? = null, + baz_Untyped: String? = null, + bazExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(foo_Untyped = foo_Untyped, bar_Untyped = bar_Untyped, baz_Untyped = baz_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(foo_Untyped = foo_Untyped, fooExpression = fooExpression, bar_Untyped = bar_Untyped, barExpression = barExpression, baz_Untyped = baz_Untyped, bazExpression = bazExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( - "foo" to foo_Untyped, + foo_Untyped?.let { "foo" to it }, + fooExpression?.let { "foo" to it.expressionString }, bar_Untyped?.let { "bar" to it }, - "baz" to baz_Untyped, + barExpression?.let { "bar" to it.expressionString }, + baz_Untyped?.let { "baz" to it }, + bazExpression?.let { "baz" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithSomeOptionalInputsBindingV1.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithSomeOptionalInputsBindingV1.kt index 941cc320e5..850aaf32c7 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithSomeOptionalInputsBindingV1.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithSomeOptionalInputsBindingV1.kt @@ -91,27 +91,27 @@ public data class ActionWithSomeOptionalInputsBindingV1 private constructor( public val _customVersion: String? = null, ) : RegularAction("john-smith", "action-with-some-optional-inputs-binding-v1", _customVersion ?: "v3") { init { - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped).size <= 1) { + "Only one of fooBar, and fooBar_Untyped must be set, but not multiple" } - require(!((bazGoo != null) && (bazGoo_Untyped != null))) { - "Only bazGoo or bazGoo_Untyped must be set, but not both" + require(listOfNotNull(bazGoo, bazGoo_Untyped).size <= 1) { + "Only one of bazGoo, and bazGoo_Untyped must be set, but not multiple" } - require(!((zooDar != null) && (zooDar_Untyped != null))) { - "Only zooDar or zooDar_Untyped must be set, but not both" + require(listOfNotNull(zooDar, zooDar_Untyped).size <= 1) { + "Only one of zooDar, and zooDar_Untyped must be set, but not multiple" } - require(!((cooPoo != null) && (cooPoo_Untyped != null))) { - "Only cooPoo or cooPoo_Untyped must be set, but not both" + require(listOfNotNull(cooPoo, cooPoo_Untyped).size <= 1) { + "Only one of cooPoo, and cooPoo_Untyped must be set, but not multiple" } - require(!((`package` != null) && (package_Untyped != null))) { - "Only package or package_Untyped must be set, but not both" + require(listOfNotNull(`package`, package_Untyped).size <= 1) { + "Only one of package, and package_Untyped must be set, but not multiple" } require((`package` != null) || (package_Untyped != null)) { - "Either package or package_Untyped must be set, one of them is required" + "Either package, or package_Untyped must be set, one of them is required" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithSomeOptionalInputsBindingV2.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithSomeOptionalInputsBindingV2.kt index 1ae94320d1..8dd4580a17 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithSomeOptionalInputsBindingV2.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithSomeOptionalInputsBindingV2.kt @@ -4,13 +4,16 @@ @file:Suppress( "DataClassPrivateConstructor", "UNUSED_PARAMETER", + "DEPRECATION", ) package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap +import kotlin.Deprecated import kotlin.ExposedCopyVisibility import kotlin.String import kotlin.Suppress @@ -28,14 +31,19 @@ import kotlin.collections.toTypedArray * * @param fooBar Required is default, default is set * @param fooBar_Untyped Required is default, default is set + * @param fooBarExpression Required is default, default is set * @param bazGoo Required is default, default is null * @param bazGoo_Untyped Required is default, default is null + * @param bazGooExpression Required is default, default is null * @param zooDar Required is false, default is set * @param zooDar_Untyped Required is false, default is set + * @param zooDarExpression Required is false, default is set * @param cooPoo Required is false, default is default * @param cooPoo_Untyped Required is false, default is default + * @param cooPooExpression Required is false, default is default * @param package <required> Required is true, default is default * @param package_Untyped <required> Required is true, default is default + * @param packageExpression <required> Required is true, default is default * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -48,7 +56,12 @@ public data class ActionWithSomeOptionalInputsBindingV2 private constructor( /** * Required is default, default is set */ + @Deprecated("Use the typed property or expression property instead") public val fooBar_Untyped: String? = null, + /** + * Required is default, default is set + */ + public val fooBarExpression: Expression? = null, /** * Required is default, default is null */ @@ -56,7 +69,12 @@ public data class ActionWithSomeOptionalInputsBindingV2 private constructor( /** * Required is default, default is null */ + @Deprecated("Use the typed property or expression property instead") public val bazGoo_Untyped: String? = null, + /** + * Required is default, default is null + */ + public val bazGooExpression: Expression? = null, /** * Required is false, default is set */ @@ -64,7 +82,12 @@ public data class ActionWithSomeOptionalInputsBindingV2 private constructor( /** * Required is false, default is set */ + @Deprecated("Use the typed property or expression property instead") public val zooDar_Untyped: String? = null, + /** + * Required is false, default is set + */ + public val zooDarExpression: Expression? = null, /** * Required is false, default is default */ @@ -72,7 +95,12 @@ public data class ActionWithSomeOptionalInputsBindingV2 private constructor( /** * Required is false, default is default */ + @Deprecated("Use the typed property or expression property instead") public val cooPoo_Untyped: String? = null, + /** + * Required is false, default is default + */ + public val cooPooExpression: Expression? = null, /** * <required> Required is true, default is default */ @@ -80,7 +108,12 @@ public data class ActionWithSomeOptionalInputsBindingV2 private constructor( /** * <required> Required is true, default is default */ + @Deprecated("Use the typed property or expression property instead") public val package_Untyped: String? = null, + /** + * <required> Required is true, default is default + */ + public val packageExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -99,27 +132,27 @@ public data class ActionWithSomeOptionalInputsBindingV2 private constructor( """.trimMargin()) } - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped, fooBarExpression).size <= 1) { + "Only one of fooBar, fooBar_Untyped, and fooBarExpression must be set, but not multiple" } - require(!((bazGoo != null) && (bazGoo_Untyped != null))) { - "Only bazGoo or bazGoo_Untyped must be set, but not both" + require(listOfNotNull(bazGoo, bazGoo_Untyped, bazGooExpression).size <= 1) { + "Only one of bazGoo, bazGoo_Untyped, and bazGooExpression must be set, but not multiple" } - require(!((zooDar != null) && (zooDar_Untyped != null))) { - "Only zooDar or zooDar_Untyped must be set, but not both" + require(listOfNotNull(zooDar, zooDar_Untyped, zooDarExpression).size <= 1) { + "Only one of zooDar, zooDar_Untyped, and zooDarExpression must be set, but not multiple" } - require(!((cooPoo != null) && (cooPoo_Untyped != null))) { - "Only cooPoo or cooPoo_Untyped must be set, but not both" + require(listOfNotNull(cooPoo, cooPoo_Untyped, cooPooExpression).size <= 1) { + "Only one of cooPoo, cooPoo_Untyped, and cooPooExpression must be set, but not multiple" } - require(!((`package` != null) && (package_Untyped != null))) { - "Only package or package_Untyped must be set, but not both" + require(listOfNotNull(`package`, package_Untyped, packageExpression).size <= 1) { + "Only one of package, package_Untyped, and packageExpression must be set, but not multiple" } - require((`package` != null) || (package_Untyped != null)) { - "Either package or package_Untyped must be set, one of them is required" + require((`package` != null) || (package_Untyped != null) || (packageExpression != null)) { + "Either package, package_Untyped, or packageExpression must be set, one of them is required" } } @@ -127,31 +160,41 @@ public data class ActionWithSomeOptionalInputsBindingV2 private constructor( vararg pleaseUseNamedArguments: Unit, fooBar: String? = null, fooBar_Untyped: String? = null, + fooBarExpression: Expression? = null, bazGoo: String? = null, bazGoo_Untyped: String? = null, + bazGooExpression: Expression? = null, zooDar: String? = null, zooDar_Untyped: String? = null, + zooDarExpression: Expression? = null, cooPoo: String? = null, cooPoo_Untyped: String? = null, + cooPooExpression: Expression? = null, `package`: String? = null, package_Untyped: String? = null, + packageExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, bazGoo = bazGoo, bazGoo_Untyped = bazGoo_Untyped, zooDar = zooDar, zooDar_Untyped = zooDar_Untyped, cooPoo = cooPoo, cooPoo_Untyped = cooPoo_Untyped, `package` = `package`, package_Untyped = package_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, fooBarExpression = fooBarExpression, bazGoo = bazGoo, bazGoo_Untyped = bazGoo_Untyped, bazGooExpression = bazGooExpression, zooDar = zooDar, zooDar_Untyped = zooDar_Untyped, zooDarExpression = zooDarExpression, cooPoo = cooPoo, cooPoo_Untyped = cooPoo_Untyped, cooPooExpression = cooPooExpression, `package` = `package`, package_Untyped = package_Untyped, packageExpression = packageExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( fooBar?.let { "foo-bar" to it }, fooBar_Untyped?.let { "foo-bar" to it }, + fooBarExpression?.let { "foo-bar" to it.expressionString }, bazGoo?.let { "baz-goo" to it }, bazGoo_Untyped?.let { "baz-goo" to it }, + bazGooExpression?.let { "baz-goo" to it.expressionString }, zooDar?.let { "zoo-dar" to it }, zooDar_Untyped?.let { "zoo-dar" to it }, + zooDarExpression?.let { "zoo-dar" to it.expressionString }, cooPoo?.let { "coo-poo" to it }, cooPoo_Untyped?.let { "coo-poo" to it }, + cooPooExpression?.let { "coo-poo" to it.expressionString }, `package`?.let { "package" to it }, package_Untyped?.let { "package" to it }, + packageExpression?.let { "package" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/SimpleActionWithRequiredStringInputsBindingV1.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/SimpleActionWithRequiredStringInputsBindingV1.kt index 2ab89083c1..eec87152c2 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/SimpleActionWithRequiredStringInputsBindingV1.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/SimpleActionWithRequiredStringInputsBindingV1.kt @@ -70,18 +70,18 @@ public data class SimpleActionWithRequiredStringInputsBindingV1 private construc public val _customVersion: String? = null, ) : RegularAction("john-smith", "simple-action-with-required-string-inputs-binding-v1", _customVersion ?: "v3") { init { - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped).size <= 1) { + "Only one of fooBar, and fooBar_Untyped must be set, but not multiple" } require((fooBar != null) || (fooBar_Untyped != null)) { - "Either fooBar or fooBar_Untyped must be set, one of them is required" + "Either fooBar, or fooBar_Untyped must be set, one of them is required" } - require(!((bazGoo != null) && (bazGoo_Untyped != null))) { - "Only bazGoo or bazGoo_Untyped must be set, but not both" + require(listOfNotNull(bazGoo, bazGoo_Untyped).size <= 1) { + "Only one of bazGoo, and bazGoo_Untyped must be set, but not multiple" } require((bazGoo != null) || (bazGoo_Untyped != null)) { - "Either bazGoo or bazGoo_Untyped must be set, one of them is required" + "Either bazGoo, or bazGoo_Untyped must be set, one of them is required" } } diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/SimpleActionWithRequiredStringInputsBindingV2.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/SimpleActionWithRequiredStringInputsBindingV2.kt index efbb2c7e5a..7bfbfc6bfe 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/SimpleActionWithRequiredStringInputsBindingV2.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/SimpleActionWithRequiredStringInputsBindingV2.kt @@ -9,6 +9,7 @@ package io.github.typesafegithub.workflows.actions.johnsmith +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action import io.github.typesafegithub.workflows.domain.actions.RegularAction import java.util.LinkedHashMap @@ -31,10 +32,13 @@ import kotlin.collections.toTypedArray * * @param fooBar <required> Short description * @param fooBar_Untyped <required> Short description + * @param fooBarExpression <required> Short description * @param bazGoo <required> Just another input * with multiline description * @param bazGoo_Untyped <required> Just another input * with multiline description + * @param bazGooExpression <required> Just another input + * with multiline description * @param _customInputs Type-unsafe map where you can put any inputs that are not yet supported by the binding * @param _customVersion Allows overriding action's version, for example to use a specific minor version, or a newer version that the binding doesn't yet know about */ @@ -47,7 +51,12 @@ public data class SimpleActionWithRequiredStringInputsBindingV2 private construc /** * <required> Short description */ + @Deprecated("Use the typed property or expression property instead") public val fooBar_Untyped: String? = null, + /** + * <required> Short description + */ + public val fooBarExpression: Expression? = null, /** * <required> Just another input * with multiline description @@ -60,6 +69,12 @@ public data class SimpleActionWithRequiredStringInputsBindingV2 private construc */ @Deprecated("this is deprecated") public val bazGoo_Untyped: String? = null, + /** + * <required> Just another input + * with multiline description + */ + @Deprecated("this is deprecated") + public val bazGooExpression: Expression? = null, /** * Type-unsafe map where you can put any inputs that are not yet supported by the binding */ @@ -78,18 +93,18 @@ public data class SimpleActionWithRequiredStringInputsBindingV2 private construc """.trimMargin()) } - require(!((fooBar != null) && (fooBar_Untyped != null))) { - "Only fooBar or fooBar_Untyped must be set, but not both" + require(listOfNotNull(fooBar, fooBar_Untyped, fooBarExpression).size <= 1) { + "Only one of fooBar, fooBar_Untyped, and fooBarExpression must be set, but not multiple" } - require((fooBar != null) || (fooBar_Untyped != null)) { - "Either fooBar or fooBar_Untyped must be set, one of them is required" + require((fooBar != null) || (fooBar_Untyped != null) || (fooBarExpression != null)) { + "Either fooBar, fooBar_Untyped, or fooBarExpression must be set, one of them is required" } - require(!((bazGoo != null) && (bazGoo_Untyped != null))) { - "Only bazGoo or bazGoo_Untyped must be set, but not both" + require(listOfNotNull(bazGoo, bazGoo_Untyped, bazGooExpression).size <= 1) { + "Only one of bazGoo, bazGoo_Untyped, and bazGooExpression must be set, but not multiple" } - require((bazGoo != null) || (bazGoo_Untyped != null)) { - "Either bazGoo or bazGoo_Untyped must be set, one of them is required" + require((bazGoo != null) || (bazGoo_Untyped != null) || (bazGooExpression != null)) { + "Either bazGoo, bazGoo_Untyped, or bazGooExpression must be set, one of them is required" } } @@ -97,19 +112,23 @@ public data class SimpleActionWithRequiredStringInputsBindingV2 private construc vararg pleaseUseNamedArguments: Unit, fooBar: String? = null, fooBar_Untyped: String? = null, + fooBarExpression: Expression? = null, bazGoo: String? = null, bazGoo_Untyped: String? = null, + bazGooExpression: Expression? = null, _customInputs: Map = mapOf(), _customVersion: String? = null, - ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, bazGoo = bazGoo, bazGoo_Untyped = bazGoo_Untyped, _customInputs = _customInputs, _customVersion = _customVersion) + ) : this(fooBar = fooBar, fooBar_Untyped = fooBar_Untyped, fooBarExpression = fooBarExpression, bazGoo = bazGoo, bazGoo_Untyped = bazGoo_Untyped, bazGooExpression = bazGooExpression, _customInputs = _customInputs, _customVersion = _customVersion) @Suppress("SpreadOperator") override fun toYamlArguments(): LinkedHashMap = linkedMapOf( *listOfNotNull( fooBar?.let { "foo-bar" to it }, fooBar_Untyped?.let { "foo-bar" to it }, + fooBarExpression?.let { "foo-bar" to it.expressionString }, bazGoo?.let { "baz-goo" to it }, bazGoo_Untyped?.let { "baz-goo" to it }, + bazGooExpression?.let { "baz-goo" to it.expressionString }, *_customInputs.toList().toTypedArray(), ).toTypedArray() ) diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationTest.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationTest.kt index 265d134923..fb13030853 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationTest.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/GenerationTest.kt @@ -1,6 +1,7 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.generation import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionTypings import io.github.typesafegithub.workflows.actionbindinggenerator.domain.NewestForVersion import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.ACTION import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.TYPING_CATALOG @@ -23,71 +24,72 @@ import io.kotest.matchers.collections.shouldHaveSize class GenerationTest : FunSpec({ - val actionManifestWithAllTypesOfInputsAndSomeOutput = + val allTypesOfInputs = + mapOf( + "foo-bar" to + Input( + description = "Short description", + required = true, + default = null, + ), + "baz-goo" to + Input( + description = "First boolean input!", + required = true, + default = null, + ), + "bin-kin" to + Input( + description = "Boolean and nullable", + required = false, + default = "test", + ), + "int-pint" to + Input( + description = "Integer", + required = true, + default = null, + ), + "flo-pint" to + Input( + description = "Float", + required = true, + default = null, + ), + "fin-bin" to + Input( + description = "Enumeration", + required = true, + default = null, + ), + "goo-zen" to + Input( + description = "Integer with special value", + required = true, + default = null, + ), + "bah-enum" to + Input( + description = "Enum with custom naming", + required = true, + default = null, + ), + "list-strings" to Input("List of strings"), + "list-ints" to Input("List of integers"), + "list-enums" to Input("List of enums"), + "list-int-special" to Input("List of integer with special values"), + ) + val actionManifestWithAllTypesOfInputsAndOutputs = Metadata( name = "Do something cool", description = "This is a test description that should be put in the KDoc comment for a class", - inputs = - mapOf( - "foo-bar" to - Input( - description = "Short description", - required = true, - default = null, - ), - "baz-goo" to - Input( - description = "First boolean input!", - required = true, - default = null, - ), - "bin-kin" to - Input( - description = "Boolean and nullable", - required = false, - default = "test", - ), - "int-pint" to - Input( - description = "Integer", - required = true, - default = null, - ), - "flo-pint" to - Input( - description = "Float", - required = true, - default = null, - ), - "fin-bin" to - Input( - description = "Enumeration", - required = true, - default = null, - ), - "goo-zen" to - Input( - description = "Integer with special value", - required = true, - default = null, - ), - "bah-enum" to - Input( - description = "Enum with custom naming", - required = true, - default = null, - ), - "list-strings" to Input("List of strings"), - "list-ints" to Input("List of integers"), - "list-enums" to Input("List of enums"), - "list-int-special" to Input("List of integer with special values"), - ), + inputs = allTypesOfInputs, outputs = - mapOf( - "baz-goo" to Output(description = "Cool output!"), - ), + allTypesOfInputs.mapValues { (_, value) -> + Output(description = "${value.description} output") + }, ) - val typingsForAllTypesOfInputs = + val typingsForAllTypes = mapOf( "foo-bar" to StringTyping, "baz-goo" to BooleanTyping, @@ -151,7 +153,11 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(actionManifest.allInputsAsStrings(), ACTION), + typings = + ActionTypings( + inputTypings = actionManifest.allInputsAsStrings(), + source = ACTION, + ), ) // then @@ -204,7 +210,11 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(actionManifest.allInputsAsStrings(), ACTION), + typings = + ActionTypings( + inputTypings = actionManifest.allInputsAsStrings(), + source = ACTION, + ), ) // then @@ -222,11 +232,12 @@ class GenerationTest : coords.generateBinding( bindingVersion = bindingVersion, metadataRevision = NewestForVersion, - metadata = actionManifestWithAllTypesOfInputsAndSomeOutput, - inputTypings = - Pair( - typingsForAllTypesOfInputs, - ACTION, + metadata = actionManifestWithAllTypesOfInputsAndOutputs, + typings = + ActionTypings( + inputTypings = typingsForAllTypes, + outputTypings = typingsForAllTypes, + source = ACTION, ), ) @@ -268,7 +279,11 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(actionManifest.allInputsAsStrings(), ACTION), + typings = + ActionTypings( + inputTypings = actionManifest.allInputsAsStrings(), + source = ACTION, + ), ) // then @@ -295,7 +310,7 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(emptyMap(), ACTION), + typings = ActionTypings(source = ACTION), ) // then @@ -322,7 +337,7 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(emptyMap(), ACTION), + typings = ActionTypings(source = ACTION), ) // then @@ -363,7 +378,11 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(actionManifest.allInputsAsStrings(), ACTION), + typings = + ActionTypings( + inputTypings = actionManifest.allInputsAsStrings(), + source = ACTION, + ), ) // then @@ -405,14 +424,15 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = - Pair( - mapOf( - "foo-one" to IntegerWithSpecialValueTyping("Foo", mapOf("Special1" to 3)), - "foo-two" to IntegerWithSpecialValueTyping("Foo", mapOf("Special1" to 3)), - "foo-three" to IntegerWithSpecialValueTyping("Foo", mapOf("Special1" to 3)), - ), - ACTION, + typings = + ActionTypings( + inputTypings = + mapOf( + "foo-one" to IntegerWithSpecialValueTyping("Foo", mapOf("Special1" to 3)), + "foo-two" to IntegerWithSpecialValueTyping("Foo", mapOf("Special1" to 3)), + "foo-three" to IntegerWithSpecialValueTyping("Foo", mapOf("Special1" to 3)), + ), + source = ACTION, ), ) @@ -448,7 +468,11 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(actionManifest.allInputsAsStrings(), ACTION), + typings = + ActionTypings( + inputTypings = actionManifest.allInputsAsStrings(), + source = ACTION, + ), ) // then @@ -485,7 +509,7 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(emptyMap(), null), + typings = ActionTypings(), ) // then @@ -527,7 +551,11 @@ class GenerationTest : bindingVersion = bindingVersion, metadataRevision = NewestForVersion, metadata = actionManifest, - inputTypings = Pair(mapOf("foo" to IntegerTyping), TYPING_CATALOG), + typings = + ActionTypings( + inputTypings = mapOf("foo" to IntegerTyping), + source = TYPING_CATALOG, + ), ) // then diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt index 4798402913..c2fabd750c 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProvidingTest.kt @@ -1,6 +1,7 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.typing import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionTypings import io.github.typesafegithub.workflows.actionbindinggenerator.domain.CommitHash import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource import io.kotest.assertions.throwables.shouldThrow @@ -55,7 +56,7 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), { actionTypesYml }) // Then - types.first shouldBe + types.inputTypings shouldBe mapOf( "name" to StringTyping, "verbose" to BooleanTyping, @@ -115,7 +116,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION) + types shouldBe + ActionTypings( + inputTypings = mapOf("hosted-by-action-yml" to StringTyping), + source = TypingActualSource.ACTION, + ) } test("only hosted by the subaction (.yml)") { @@ -135,7 +140,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION) + types shouldBe + ActionTypings( + inputTypings = mapOf("hosted-by-action-yml" to StringTyping), + source = TypingActualSource.ACTION, + ) } test("only hosted by the action (.yaml)") { @@ -152,7 +161,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("hosted-by-action-yaml" to StringTyping), TypingActualSource.ACTION) + types shouldBe + ActionTypings( + inputTypings = mapOf("hosted-by-action-yaml" to StringTyping), + source = TypingActualSource.ACTION, + ) } test("only hosted by the subaction (.yaml)") { @@ -172,7 +185,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("hosted-by-action-yaml" to StringTyping), TypingActualSource.ACTION) + types shouldBe + ActionTypings( + inputTypings = mapOf("hosted-by-action-yaml" to StringTyping), + source = TypingActualSource.ACTION, + ) } test("only hosted by the action, both extensions") { @@ -190,7 +207,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION) + types shouldBe + ActionTypings( + inputTypings = mapOf("hosted-by-action-yml" to StringTyping), + source = TypingActualSource.ACTION, + ) } test("only hosted by the subaction, both extensions") { @@ -214,7 +235,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION) + types shouldBe + ActionTypings( + inputTypings = mapOf("hosted-by-action-yml" to StringTyping), + source = TypingActualSource.ACTION, + ) } test("only stored in typing catalog") { @@ -235,7 +260,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("stored-in-typing-catalog" to StringTyping), TypingActualSource.TYPING_CATALOG) + types shouldBe + ActionTypings( + inputTypings = mapOf("stored-in-typing-catalog" to StringTyping), + source = TypingActualSource.TYPING_CATALOG, + ) } test("only stored in typing catalog for subaction") { @@ -256,7 +285,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("stored-in-typing-catalog" to StringTyping), TypingActualSource.TYPING_CATALOG) + types shouldBe + ActionTypings( + inputTypings = mapOf("stored-in-typing-catalog" to StringTyping), + source = TypingActualSource.TYPING_CATALOG, + ) } test("hosted by action and stored in typing catalog") { @@ -282,7 +315,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION) + types shouldBe + ActionTypings( + inputTypings = mapOf("hosted-by-action-yml" to StringTyping), + source = TypingActualSource.ACTION, + ) } test("hosted by subaction and stored in typing catalog") { @@ -308,7 +345,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION) + types shouldBe + ActionTypings( + inputTypings = mapOf("hosted-by-action-yml" to StringTyping), + source = TypingActualSource.ACTION, + ) } test("only stored in typing catalog for older version") { @@ -334,7 +375,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("stored-in-typing-catalog-for-older-version" to StringTyping), TypingActualSource.TYPING_CATALOG) + types shouldBe + ActionTypings( + inputTypings = mapOf("stored-in-typing-catalog-for-older-version" to StringTyping), + source = TypingActualSource.TYPING_CATALOG, + ) } test("only stored in typing catalog for older version of subaction") { @@ -360,7 +405,11 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(mapOf("stored-in-typing-catalog-for-older-version" to StringTyping), TypingActualSource.TYPING_CATALOG) + types shouldBe + ActionTypings( + inputTypings = mapOf("stored-in-typing-catalog-for-older-version" to StringTyping), + source = TypingActualSource.TYPING_CATALOG, + ) } test("metadata available but no version available") { @@ -381,7 +430,7 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(emptyMap(), null) + types shouldBe ActionTypings() } test("no typings at all") { @@ -393,7 +442,7 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(emptyMap(), null) + types shouldBe ActionTypings() } } @@ -438,13 +487,14 @@ class TypesProvidingTest : // Then types shouldBe - Pair( - mapOf( - "granted-scopes" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), - "granted-scopes2" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), - "granted-scopes3" to ListOfTypings("""\n""", EnumTyping("GrantedScopes", listOf("read", "write"))), - ), - TypingActualSource.ACTION, + ActionTypings( + inputTypings = + mapOf( + "granted-scopes" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), + "granted-scopes2" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), + "granted-scopes3" to ListOfTypings("""\n""", EnumTyping("GrantedScopes", listOf("read", "write"))), + ), + source = TypingActualSource.ACTION, ) } @@ -467,13 +517,14 @@ class TypesProvidingTest : // Then types shouldBe - Pair( - mapOf( - "granted-scopes" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), - "granted-scopes2" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), - "granted-scopes3" to ListOfTypings("""\n""", EnumTyping("GrantedScopes", listOf("read", "write"))), - ), - TypingActualSource.TYPING_CATALOG, + ActionTypings( + inputTypings = + mapOf( + "granted-scopes" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), + "granted-scopes2" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), + "granted-scopes3" to ListOfTypings("""\n""", EnumTyping("GrantedScopes", listOf("read", "write"))), + ), + source = TypingActualSource.TYPING_CATALOG, ) } @@ -501,13 +552,14 @@ class TypesProvidingTest : // Then types shouldBe - Pair( - mapOf( - "granted-scopes" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), - "granted-scopes2" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), - "granted-scopes3" to ListOfTypings("""\n""", EnumTyping("GrantedScopes", listOf("read", "write"))), - ), - TypingActualSource.TYPING_CATALOG, + ActionTypings( + inputTypings = + mapOf( + "granted-scopes" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), + "granted-scopes2" to ListOfTypings(",", EnumTyping("GrantedScopes", listOf("read", "write"))), + "granted-scopes3" to ListOfTypings("""\n""", EnumTyping("GrantedScopes", listOf("read", "write"))), + ), + source = TypingActualSource.TYPING_CATALOG, ) } @@ -567,6 +619,6 @@ class TypesProvidingTest : val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) // Then - types shouldBe Pair(emptyMap(), null) + types shouldBe ActionTypings(inputTypings = emptyMap(), source = null) } }) diff --git a/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/dsl/expressions/GenerateEventPayloads.kt b/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/dsl/expressions/GenerateEventPayloads.kt index 8059be5789..657e5cc8b4 100644 --- a/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/dsl/expressions/GenerateEventPayloads.kt +++ b/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/dsl/expressions/GenerateEventPayloads.kt @@ -158,7 +158,7 @@ fun Map.Entry.generatePropertySpec( is JsonObject -> PropertySpec .builder(child, ClassName(PACKAGE, payloadClassName("$key.$child", filename))) - .initializer("%L", payloadClassName("$key.$child", filename)) + .initializer("%N", payloadClassName("$key.$child", filename)) .build() is JsonArray -> { PropertySpec diff --git a/docs/user-guide/job-outputs.md b/docs/user-guide/job-outputs.md index 2fc4ba8038..c96fee7e73 100644 --- a/docs/user-guide/job-outputs.md +++ b/docs/user-guide/job-outputs.md @@ -1,9 +1,8 @@ # Job outputs -It's possible to pass output from a job in a somewhat type-safe way (that is: types aren't checked, but the field names -are). +It is possible to pass output from a job in a type-safe way. -First, define `outputs` parameter in `job` function, inheriting from `JobOutputs`: +First, define `outputs` parameter in `job` function, inheriting from `JobOutputs` with properly typed output properties: ```kotlin --8<-- "JobOutputsSnippets.kt:define-job-outputs-1" diff --git a/docs/user-guide/migrating-to-Maven-based-bindings.md b/docs/user-guide/migrating-to-Maven-based-bindings.md index 8b6826c8ac..3e930ef504 100644 --- a/docs/user-guide/migrating-to-Maven-based-bindings.md +++ b/docs/user-guide/migrating-to-Maven-based-bindings.md @@ -99,6 +99,10 @@ Then regenerate your YAML to ensure there are no changes. typing information there will only be the `_Untyped` property with nullability according to required status. 1. If you used `_customInputs` to set a non-String property to a GitHub Actions expression, you can now instead use the `_Untyped` property for that input. +1. If you used outputs anywhere, you will need to adjust your code, as outputs are now typed and of type `Expression`, + which can directly be wired to matching inputs' `...Expression` siblings or with the `_Untyped` sibling to any + input's `...Expression` sibling. If you need the pure expression, e.g. for usage within a bigger expression, + you can also get that from the `Expression` instance. 1. If a given binding has incorrect typing, please either ask the action owner to onboard [github-action-typing](https://github.com/typesafegithub/github-actions-typing/), or if it's not possible, contribute to [github-actions-typing-catalog](https://github.com/typesafegithub/github-actions-typing-catalog). diff --git a/docs/user-guide/using-actions.md b/docs/user-guide/using-actions.md index d90246f29c..4ff43128d4 100644 --- a/docs/user-guide/using-actions.md +++ b/docs/user-guide/using-actions.md @@ -21,7 +21,7 @@ To add a dependency on an action: For every action, a binding will be generated. However, some less popular actions don't have typings configured for their inputs, so by default all inputs are of type `String`, have the suffix `_Untyped`, and additionally the class -name will have an `_Untyped` suffix. The nullability of the inputs will be according to their required status. +name will have an `_Untyped` suffix. There are two ways of configuring typings: 1. Recommended: a typing manifest (`action-typing.yml`) in the action's repo, see @@ -37,11 +37,15 @@ There are two ways of configuring typings: Once there are any typings in place for the action, the `_Untyped` suffixed class is marked `@Deprecated`, and a class without that suffix is created additionally. In that class for each input that does not have type information available -there will still be only the property with `_Untyped` suffix and nullability according to required status. For each -input that does have type information available, there will still be the `_Untyped` property and additionally a -properly typed property. Both of these properties will be nullable. It is a runtime error to set both of these -properties as well as setting none if the input is required. The `_Untyped` properties are not marked `@Deprecated`, -as it could still make sense to use them, for example if you want to set the value from a GitHub Actions expression. +there will still be only the property with `_Untyped` suffix. For each input that does have type information available, +there will still be the `_Untyped` property and additionally a properly typed property. It is a runtime error to set +both of these properties as well as setting none if the input is required. + +All inputs also get another `Expression` suffixed property and list-typed inputs yet another `Expressions` suffixed +property. These properties can e.g. be set type-safely from outputs, as they are of type `Expression`. Each output also +has an `_Untyped` suffixed property that you can feed to any input expression property. Like with the `_Untyped` input +property, all up to four properties for the same input are mutually exclusive and for required inputs exactly one has +to be set, or a runtime error occurs. This approach supports dependency updating bots that support Kotlin Script's `.main.kts` files. E.g. Renovate is known to support it. @@ -108,7 +112,7 @@ This table lists which library versions are compatible with which repository URL |-------------------------------------|-------------------| | `https://bindings.krzeminski.it` | `3.0.0` and newer | | `https://bindings.krzeminski.it/v1` | `3.0.0` and newer | -| `https://bindings.krzeminski.it/v2` | `3.0.0` and newer | +| `https://bindings.krzeminski.it/v2` | `4.0.0` and newer | #### Breaking changes @@ -119,7 +123,12 @@ anyway as defined above. `https://bindings.krzeminski.it/v2` -: TBD +: - Outputs of actions are no longer plain `String`s with the GitHub expression. Instead they are of type + `Expression<*>` with the type parameter set according to the action typing definition. Those expressions can be + wired directly to the new `Expression` suffixed sibling properties each input property has now. In case you need + to wire non-matching types, you can use the `_Untyped` property for the output or get it by name. Those return + `Expression` which can be given to all types. You can also still get the plain expression from that + object to use it within a bigger expression. `https://bindings.krzeminski.it` / `https://bindings.krzeminski.it/v1` diff --git a/github-workflows-kt/api/github-workflows-kt.api b/github-workflows-kt/api/github-workflows-kt.api index a3ad8ecb22..978f726b9f 100644 --- a/github-workflows-kt/api/github-workflows-kt.api +++ b/github-workflows-kt/api/github-workflows-kt.api @@ -4,6 +4,7 @@ public abstract interface annotation class io/github/typesafegithub/workflows/an public abstract class io/github/typesafegithub/workflows/domain/AbstractResult { public final fun eq (Lio/github/typesafegithub/workflows/domain/AbstractResult$Status;)Ljava/lang/String; public final fun neq (Lio/github/typesafegithub/workflows/domain/AbstractResult$Status;)Ljava/lang/String; + public final fun toExpression ()Lio/github/typesafegithub/workflows/domain/Expression; public fun toString ()Ljava/lang/String; } @@ -149,6 +150,18 @@ public final class io/github/typesafegithub/workflows/domain/Environment { public fun toString ()Ljava/lang/String; } +public final class io/github/typesafegithub/workflows/domain/Expression { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lio/github/typesafegithub/workflows/domain/Expression; + public static synthetic fun copy$default (Lio/github/typesafegithub/workflows/domain/Expression;Ljava/lang/String;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/domain/Expression; + public fun equals (Ljava/lang/Object;)Z + public final fun getExpression ()Ljava/lang/String; + public final fun getExpressionString ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public final class io/github/typesafegithub/workflows/domain/Job : io/github/typesafegithub/workflows/dsl/HasCustomArguments { public fun (Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/domain/RunnerType;Ljava/util/List;Ljava/util/List;Lio/github/typesafegithub/workflows/domain/JobOutputs;Ljava/util/Map;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/lang/Integer;Lio/github/typesafegithub/workflows/domain/Concurrency;Lio/github/typesafegithub/workflows/domain/Container;Lio/github/typesafegithub/workflows/domain/Environment;Ljava/util/Map;Ljava/util/Map;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/domain/RunnerType;Ljava/util/List;Ljava/util/List;Lio/github/typesafegithub/workflows/domain/JobOutputs;Ljava/util/Map;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/lang/Integer;Lio/github/typesafegithub/workflows/domain/Concurrency;Lio/github/typesafegithub/workflows/domain/Container;Lio/github/typesafegithub/workflows/domain/Environment;Ljava/util/Map;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -208,9 +221,9 @@ public final class io/github/typesafegithub/workflows/domain/JobOutputs$EMPTY : public final class io/github/typesafegithub/workflows/domain/JobOutputs$Ref : kotlin/properties/ReadWriteProperty { public fun (Lio/github/typesafegithub/workflows/domain/JobOutputs;)V - public fun getValue (Lio/github/typesafegithub/workflows/domain/JobOutputs;Lkotlin/reflect/KProperty;)Ljava/lang/String; + public fun getValue (Lio/github/typesafegithub/workflows/domain/JobOutputs;Lkotlin/reflect/KProperty;)Lio/github/typesafegithub/workflows/domain/Expression; public synthetic fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object; - public fun setValue (Lio/github/typesafegithub/workflows/domain/JobOutputs;Lkotlin/reflect/KProperty;Ljava/lang/String;)V + public fun setValue (Lio/github/typesafegithub/workflows/domain/JobOutputs;Lkotlin/reflect/KProperty;Lio/github/typesafegithub/workflows/domain/Expression;)V public synthetic fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V } @@ -520,7 +533,7 @@ public abstract class io/github/typesafegithub/workflows/domain/actions/Action { public class io/github/typesafegithub/workflows/domain/actions/Action$Outputs { public fun (Ljava/lang/String;)V - public final fun get (Ljava/lang/String;)Ljava/lang/String; + public final fun get (Ljava/lang/String;)Lio/github/typesafegithub/workflows/domain/Expression; } public final class io/github/typesafegithub/workflows/domain/actions/CustomAction : io/github/typesafegithub/workflows/domain/actions/RegularAction { diff --git a/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/AbstractResult.kt b/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/AbstractResult.kt index 99aee4965a..062066cd75 100644 --- a/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/AbstractResult.kt +++ b/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/AbstractResult.kt @@ -7,6 +7,8 @@ public abstract class AbstractResult internal constructor( public infix fun neq(status: Status): String = "$value != $status" + public fun toExpression(): Expression = Expression(value) + override fun toString(): String = value public enum class Status { diff --git a/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/Expression.kt b/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/Expression.kt new file mode 100644 index 0000000000..a0ac3d758f --- /dev/null +++ b/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/Expression.kt @@ -0,0 +1,9 @@ +package io.github.typesafegithub.workflows.domain + +import io.github.typesafegithub.workflows.dsl.expressions.expr + +public data class Expression( + public val expression: String, +) { + public val expressionString: String = expr(expression) +} diff --git a/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/JobOutputs.kt b/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/JobOutputs.kt index 824b19c08b..5da3131409 100644 --- a/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/JobOutputs.kt +++ b/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/JobOutputs.kt @@ -1,6 +1,5 @@ package io.github.typesafegithub.workflows.domain -import io.github.typesafegithub.workflows.dsl.expressions.expr import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -12,32 +11,32 @@ public open class JobOutputs { public val outputMapping: Map get() = _outputMapping.toMap() - public fun output(): Ref = Ref() + public fun output(): Ref = Ref() - public inner class Ref : ReadWriteProperty { + public inner class Ref : ReadWriteProperty> { private var initialized: Boolean = false override fun getValue( thisRef: JobOutputs, property: KProperty<*>, - ): String { + ): Expression { val key = property.name check(initialized) { "output '$key' must be initialized" } - return "needs.${job.id}.outputs.$key" + return Expression("needs.${job.id}.outputs.$key") } override fun setValue( thisRef: JobOutputs, property: KProperty<*>, - value: String, + value: Expression, ) { val key = property.name check(!initialized) { "Value for output '$key' can be assigned only once!" } - _outputMapping[key] = expr(value) + _outputMapping[key] = value.expressionString initialized = true } } diff --git a/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/actions/Action.kt b/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/actions/Action.kt index ff0d9d66c7..d8d6ad0f2c 100644 --- a/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/actions/Action.kt +++ b/github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/domain/actions/Action.kt @@ -1,5 +1,6 @@ package io.github.typesafegithub.workflows.domain.actions +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.Action.Outputs import io.github.typesafegithub.workflows.yaml.toYaml @@ -15,7 +16,7 @@ public abstract class Action { public open class Outputs( private val stepId: String, ) { - public operator fun get(outputName: String): String = "steps.$stepId.outputs.$outputName" + public operator fun get(outputName: String): Expression = Expression("steps.$stepId.outputs.$outputName") } } diff --git a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/IntegrationTest.kt b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/IntegrationTest.kt index 28316995b6..a2dc98695b 100644 --- a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/IntegrationTest.kt +++ b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/IntegrationTest.kt @@ -262,7 +262,7 @@ class IntegrationTest : Checkout( repository = expr(addAndCommit.id), ref = expr(addAndCommit.outputs.commitSha), - token = expr(addAndCommit.outputs["my-unsafe-output"]), + token = addAndCommit.outputs["my-unsafe-output"].expressionString, ), ) } @@ -402,7 +402,7 @@ class IntegrationTest : Checkout( repository = expr(addAndCommit.id), ref = expr(addAndCommit.outputs.commitSha), - token = expr(addAndCommit.outputs["my-unsafe-output"]), + token = addAndCommit.outputs["my-unsafe-output"].expressionString, ), ) } diff --git a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/actions/CustomActionTest.kt b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/actions/CustomActionTest.kt index a8b72b1192..2efeff4ba0 100644 --- a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/actions/CustomActionTest.kt +++ b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/actions/CustomActionTest.kt @@ -1,5 +1,6 @@ package io.github.typesafegithub.workflows.actions +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.actions.CustomAction import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe @@ -25,6 +26,6 @@ class CustomActionTest : val outputs = customAction.buildOutputObject("someStepId") // when & then - outputs["custom-output"] shouldBe "steps.someStepId.outputs.custom-output" + outputs["custom-output"] shouldBe Expression("steps.someStepId.outputs.custom-output") } }) diff --git a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/JobOutputsSnippets.kt b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/JobOutputsSnippets.kt index 1b49bbbf10..65e0994e1f 100644 --- a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/JobOutputsSnippets.kt +++ b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/JobOutputsSnippets.kt @@ -1,5 +1,6 @@ package io.github.typesafegithub.workflows.docsnippets +import io.github.typesafegithub.workflows.domain.Expression import io.github.typesafegithub.workflows.domain.JobOutputs import io.github.typesafegithub.workflows.domain.RunnerType import io.github.typesafegithub.workflows.domain.actions.Action @@ -34,8 +35,8 @@ class JobOutputsSnippets : runsOn = RunnerType.UbuntuLatest, outputs = object : JobOutputs() { - var myOutput by output() - var anotherOutput by output() + var myOutput by output() + var anotherOutput by output() }, // --8<-- [end:define-job-outputs-1] /* @@ -52,7 +53,7 @@ class JobOutputsSnippets : inner class Outputs( stepId: String, ) : Action.Outputs(stepId) { - val someStepOutput: String = "" + val someStepOutput: Expression = Expression("") } } val someStep = uses(action = DocTest()) @@ -73,8 +74,8 @@ class JobOutputsSnippets : name = "Use outputs", command = """ - echo ${expr { myJob.outputs.myOutput }} - echo ${expr { myJob.outputs.anotherOutput }} + echo ${myJob.outputs.myOutput.expressionString} + echo ${expr(myJob.outputs.anotherOutput.expression)} """.trimIndent(), ) } diff --git a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/UsingActionsSnippets.kt b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/UsingActionsSnippets.kt index a3261aaf8d..45569d558c 100644 --- a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/UsingActionsSnippets.kt +++ b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/UsingActionsSnippets.kt @@ -9,7 +9,6 @@ import io.github.typesafegithub.workflows.domain.actions.DockerAction import io.github.typesafegithub.workflows.domain.actions.LocalAction import io.github.typesafegithub.workflows.domain.actions.RegularAction import io.github.typesafegithub.workflows.domain.triggers.Push -import io.github.typesafegithub.workflows.dsl.expressions.expr import io.github.typesafegithub.workflows.dsl.workflow import io.kotest.core.spec.style.FunSpec import io.kotest.engine.spec.tempdir @@ -136,7 +135,7 @@ class UsingActionsSnippets : ) // use your outputs: - println(expr(customActionStep.outputs["custom-output"])) + println(customActionStep.outputs["custom-output"].expressionString) } // --8<-- [end:custom-action-outputs] } diff --git a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/domain/JobTest.kt b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/domain/JobTest.kt index 4e828c3047..e1a8cb1822 100644 --- a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/domain/JobTest.kt +++ b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/domain/JobTest.kt @@ -19,6 +19,7 @@ class JobTest : outputs = JobOutputs.EMPTY, ) job.result.toString() shouldBe "needs.some-id.result" + job.result.toExpression() shouldBe Expression("needs.some-id.result") job.result eq Status.Failure shouldBe "needs.some-id.result == 'failure'" job.result eq Status.Cancelled shouldBe "needs.some-id.result == 'cancelled'" job.result eq Status.Skipped shouldBe "needs.some-id.result == 'skipped'" @@ -34,17 +35,18 @@ class JobTest : steps = listOf(), outputs = object : JobOutputs() { - var output1 by output() - var output2 by output() + var output1 by output() + var output2 by output() }, ) - job.outputs.output1 = "foo" - job.outputs.output2 = "foo" + job.outputs.output1 = Expression("foo") + job.outputs.output2 = Expression("foo") - job.outputs.output1 shouldBe "needs.some-id.outputs.output1" - job.outputs.output2 shouldBe "needs.some-id.outputs.output2" + job.outputs.output1 shouldBe Expression("needs.some-id.outputs.output1") + job.outputs.output2 shouldBe Expression("needs.some-id.outputs.output2") job.result.toString() shouldBe "needs.some-id.result" + job.result.toExpression() shouldBe Expression("needs.some-id.result") job.result eq Status.Failure shouldBe "needs.some-id.result == 'failure'" job.result eq Status.Cancelled shouldBe "needs.some-id.result == 'cancelled'" job.result eq Status.Skipped shouldBe "needs.some-id.result == 'skipped'" @@ -60,7 +62,7 @@ class JobTest : steps = listOf(), outputs = object : JobOutputs() { - var output1 by output() + var output1 by output() }, ) @@ -78,13 +80,13 @@ class JobTest : steps = listOf(), outputs = object : JobOutputs() { - var output1 by output() + var output1 by output() }, ) - job.outputs.output1 = "foo" + job.outputs.output1 = Expression("foo") shouldThrow { - job.outputs.output1 = "bar" + job.outputs.output1 = Expression("bar") Unit } } diff --git a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/domain/StepTest.kt b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/domain/StepTest.kt index c9d7086715..a40e0a1b4c 100644 --- a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/domain/StepTest.kt +++ b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/domain/StepTest.kt @@ -11,6 +11,7 @@ class StepTest : test("step.outcome") { val step0: Step<*> = CommandStep(id = "step-0", command = "ls") step0.outcome.toString() shouldBe "steps.step-0.outcome" + step0.outcome.toExpression() shouldBe Expression("steps.step-0.outcome") step0.outcome eq Status.Failure shouldBe "steps.step-0.outcome == 'failure'" step0.outcome eq Status.Cancelled shouldBe "steps.step-0.outcome == 'cancelled'" step0.outcome eq Status.Skipped shouldBe "steps.step-0.outcome == 'skipped'" @@ -24,6 +25,7 @@ class StepTest : outputs = Action.Outputs("whatever"), ) someStep.conclusion.toString() shouldBe "steps.whatever.conclusion" + someStep.conclusion.toExpression() shouldBe Expression("steps.whatever.conclusion") someStep.conclusion eq Status.Failure shouldBe "steps.whatever.conclusion == 'failure'" someStep.conclusion eq Status.Cancelled shouldBe "steps.whatever.conclusion == 'cancelled'" someStep.conclusion eq Status.Skipped shouldBe "steps.whatever.conclusion == 'skipped'" @@ -31,6 +33,6 @@ class StepTest : } test("step.outputs") { val step0: Step<*> = CommandStep(id = "step-0", command = "ls") - step0.outputs["foo"] shouldBe "steps.step-0.outputs.foo" + step0.outputs["foo"] shouldBe Expression("steps.step-0.outputs.foo") } })