diff --git a/action-binding-generator/api/action-binding-generator.api b/action-binding-generator/api/action-binding-generator.api index 76660945c9..d587f20c3e 100644 --- a/action-binding-generator/api/action-binding-generator.api +++ b/action-binding-generator/api/action-binding-generator.api @@ -1,16 +1,18 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/lang/String; - public final fun component4 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; - public static synthetic fun copy$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; + public final fun component4 ()Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; + public final fun component5 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; + public static synthetic fun copy$default (Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion;Ljava/lang/String;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords; public fun equals (Ljava/lang/Object;)Z public final fun getName ()Ljava/lang/String; public final fun getOwner ()Ljava/lang/String; public final fun getPath ()Ljava/lang/String; + public final fun getSignificantVersion ()Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; public final fun getVersion ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -44,6 +46,16 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/dom public fun toString ()Ljava/lang/String; } +public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion : java/lang/Enum { + public static final field FULL Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; + public static final field MAJOR Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; + public static final field MINOR Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public fun toString ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; + public static fun values ()[Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion; +} + public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource : java/lang/Enum { public static final field ACTION Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource; public static final field TYPING_CATALOG Lio/github/typesafegithub/workflows/actionbindinggenerator/domain/TypingActualSource; diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt index 617d283cb0..38c70160a7 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords.kt @@ -1,9 +1,12 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.domain +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL + public data class ActionCoords( val owner: String, val name: String, val version: String, + val significantVersion: SignificantVersion = FULL, val path: String? = null, ) @@ -13,7 +16,9 @@ public data class ActionCoords( */ public val ActionCoords.isTopLevel: Boolean get() = path == null -public val ActionCoords.prettyPrint: String get() = "$owner/$fullName@$version" +public val ActionCoords.prettyPrint: String get() = "$owner/$fullName${ + significantVersion.takeUnless { it == FULL }?.let { " with $it version" } ?: "" +}@$version" /** * For most actions, it's empty. diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion.kt new file mode 100644 index 0000000000..3dd609a5a6 --- /dev/null +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion.kt @@ -0,0 +1,10 @@ +package io.github.typesafegithub.workflows.actionbindinggenerator.domain + +public enum class SignificantVersion { + MAJOR, + MINOR, + FULL, + ; + + override fun toString(): String = super.toString().lowercase() +} 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 070ab2b477..6af3e5c84d 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 @@ -17,6 +17,9 @@ 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.MetadataRevision +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.MAJOR +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.MINOR import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource import io.github.typesafegithub.workflows.actionbindinggenerator.domain.fullName import io.github.typesafegithub.workflows.actionbindinggenerator.domain.isTopLevel @@ -415,9 +418,19 @@ private fun TypeSpec.Builder.inheritsFromRegularAction( .superclass(superclass) .addSuperclassConstructorParameter("%S", coords.owner) .addSuperclassConstructorParameter("%S", coords.fullName) - .addSuperclassConstructorParameter("_customVersion ?: %S", coords.version) + .addSuperclassConstructorParameter( + "_customVersion ?: %S", + when (coords.significantVersion) { + MAJOR -> coords.version.majorVersion + MINOR -> coords.version.minorVersion + FULL -> coords.version + }, + ) } +private val String.majorVersion get() = substringBefore('.') +private val String.minorVersion get() = split('.', limit = 3).take(2).joinToString(".") + private fun Metadata.primaryConstructor( inputTypings: Map, coords: ActionCoords, diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt index bacd9d3384..fd81c2980d 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/metadata/MetadataReading.kt @@ -40,8 +40,6 @@ private fun ActionCoords.actionYmlUrl(gitRef: String) = "https://raw.githubuserc private fun ActionCoords.actionYamlUrl(gitRef: String) = "https://raw.githubusercontent.com/$owner/$name/$gitRef$subName/action.yaml" -internal val ActionCoords.gitHubUrl: String get() = "https://github.com/$owner/$name" - public fun ActionCoords.fetchMetadata( metadataRevision: MetadataRevision, fetchUri: (URI) -> String = ::fetchUri, diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion.kt new file mode 100644 index 0000000000..1359c05b84 --- /dev/null +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion.kt @@ -0,0 +1,55 @@ +// This file was generated using action-binding-generator. Don't change it by hand, otherwise your +// changes will be overwritten with the next binding code regeneration. +// See https://github.com/typesafegithub/github-workflows-kt for more info. +@file:Suppress( + "DataClassPrivateConstructor", + "UNUSED_PARAMETER", +) + +package io.github.typesafegithub.workflows.actions.johnsmith + +import io.github.typesafegithub.workflows.domain.actions.Action +import io.github.typesafegithub.workflows.domain.actions.RegularAction +import java.util.LinkedHashMap +import kotlin.ExposedCopyVisibility +import kotlin.String +import kotlin.Suppress +import kotlin.Unit +import kotlin.collections.Map + +/** + * Action: Action With No Inputs + * + * Description + * + * [Action on GitHub](https://github.com/john-smith/action-with-no-inputs-with-major-version) + * + * @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 + */ +@ExposedCopyVisibility +public data class ActionWithNoInputsWithMajorVersion private constructor( + /** + * Type-unsafe map where you can put any inputs that are not yet supported by the binding + */ + public val _customInputs: Map = mapOf(), + /** + * 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 + */ + public val _customVersion: String? = null, +) : RegularAction("john-smith", "action-with-no-inputs-with-major-version", + _customVersion ?: "v3") { + public constructor( + vararg pleaseUseNamedArguments: Unit, + _customInputs: Map = mapOf(), + _customVersion: String? = null, + ) : this(_customInputs = _customInputs, _customVersion = _customVersion) + + @Suppress("SpreadOperator") + override fun toYamlArguments(): LinkedHashMap = LinkedHashMap(_customInputs) + + override fun buildOutputObject(stepId: String): Action.Outputs = Outputs(stepId) +} diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion_Untyped.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion_Untyped.kt new file mode 100644 index 0000000000..4298712831 --- /dev/null +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion_Untyped.kt @@ -0,0 +1,80 @@ +// This file was generated using action-binding-generator. Don't change it by hand, otherwise your +// changes will be overwritten with the next binding code regeneration. +// See https://github.com/typesafegithub/github-workflows-kt for more info. +@file:Suppress( + "DataClassPrivateConstructor", + "UNUSED_PARAMETER", +) + +package io.github.typesafegithub.workflows.actions.johnsmith + +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 +import kotlin.Unit +import kotlin.collections.Map + +/** + * ```text + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!! WARNING !!! + * !!! !!! + * !!! This action binding has no typings provided. All inputs will !!! + * !!! have a default type of String. !!! + * !!! To be able to use this action in a type-safe way, ask the !!! + * !!! action's owner to provide the typings using !!! + * !!! !!! + * !!! https://github.com/typesafegithub/github-actions-typing !!! + * !!! !!! + * !!! or if it's impossible, contribute typings to a community-driven !!! + * !!! !!! + * !!! https://github.com/typesafegithub/github-actions-typing-catalog !!! + * !!! !!! + * !!! This '_Untyped' binding will be available even once the typings !!! + * !!! are added. !!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ``` + * + * Action: Action With No Inputs + * + * Description + * + * [Action on GitHub](https://github.com/john-smith/action-with-no-inputs-with-major-version) + * + * @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 + */ +@Deprecated( + "Use the typed class instead", + ReplaceWith("ActionWithNoInputsWithMajorVersion"), +) +@ExposedCopyVisibility +public data class ActionWithNoInputsWithMajorVersion_Untyped private constructor( + /** + * Type-unsafe map where you can put any inputs that are not yet supported by the binding + */ + public val _customInputs: Map = mapOf(), + /** + * 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 + */ + public val _customVersion: String? = null, +) : RegularAction("john-smith", "action-with-no-inputs-with-major-version", + _customVersion ?: "v3") { + public constructor( + vararg pleaseUseNamedArguments: Unit, + _customInputs: Map = mapOf(), + _customVersion: String? = null, + ) : this(_customInputs = _customInputs, _customVersion = _customVersion) + + @Suppress("SpreadOperator") + override fun toYamlArguments(): LinkedHashMap = LinkedHashMap(_customInputs) + + override fun buildOutputObject(stepId: String): Action.Outputs = Outputs(stepId) +} diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion.kt new file mode 100644 index 0000000000..714c31456e --- /dev/null +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion.kt @@ -0,0 +1,55 @@ +// This file was generated using action-binding-generator. Don't change it by hand, otherwise your +// changes will be overwritten with the next binding code regeneration. +// See https://github.com/typesafegithub/github-workflows-kt for more info. +@file:Suppress( + "DataClassPrivateConstructor", + "UNUSED_PARAMETER", +) + +package io.github.typesafegithub.workflows.actions.johnsmith + +import io.github.typesafegithub.workflows.domain.actions.Action +import io.github.typesafegithub.workflows.domain.actions.RegularAction +import java.util.LinkedHashMap +import kotlin.ExposedCopyVisibility +import kotlin.String +import kotlin.Suppress +import kotlin.Unit +import kotlin.collections.Map + +/** + * Action: Action With No Inputs + * + * Description + * + * [Action on GitHub](https://github.com/john-smith/action-with-no-inputs-with-minor-version) + * + * @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 + */ +@ExposedCopyVisibility +public data class ActionWithNoInputsWithMinorVersion private constructor( + /** + * Type-unsafe map where you can put any inputs that are not yet supported by the binding + */ + public val _customInputs: Map = mapOf(), + /** + * 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 + */ + public val _customVersion: String? = null, +) : RegularAction("john-smith", "action-with-no-inputs-with-minor-version", + _customVersion ?: "v3.1") { + public constructor( + vararg pleaseUseNamedArguments: Unit, + _customInputs: Map = mapOf(), + _customVersion: String? = null, + ) : this(_customInputs = _customInputs, _customVersion = _customVersion) + + @Suppress("SpreadOperator") + override fun toYamlArguments(): LinkedHashMap = LinkedHashMap(_customInputs) + + override fun buildOutputObject(stepId: String): Action.Outputs = Outputs(stepId) +} diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion_Untyped.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion_Untyped.kt new file mode 100644 index 0000000000..a5d3ea68d1 --- /dev/null +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion_Untyped.kt @@ -0,0 +1,80 @@ +// This file was generated using action-binding-generator. Don't change it by hand, otherwise your +// changes will be overwritten with the next binding code regeneration. +// See https://github.com/typesafegithub/github-workflows-kt for more info. +@file:Suppress( + "DataClassPrivateConstructor", + "UNUSED_PARAMETER", +) + +package io.github.typesafegithub.workflows.actions.johnsmith + +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 +import kotlin.Unit +import kotlin.collections.Map + +/** + * ```text + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!! WARNING !!! + * !!! !!! + * !!! This action binding has no typings provided. All inputs will !!! + * !!! have a default type of String. !!! + * !!! To be able to use this action in a type-safe way, ask the !!! + * !!! action's owner to provide the typings using !!! + * !!! !!! + * !!! https://github.com/typesafegithub/github-actions-typing !!! + * !!! !!! + * !!! or if it's impossible, contribute typings to a community-driven !!! + * !!! !!! + * !!! https://github.com/typesafegithub/github-actions-typing-catalog !!! + * !!! !!! + * !!! This '_Untyped' binding will be available even once the typings !!! + * !!! are added. !!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ``` + * + * Action: Action With No Inputs + * + * Description + * + * [Action on GitHub](https://github.com/john-smith/action-with-no-inputs-with-minor-version) + * + * @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 + */ +@Deprecated( + "Use the typed class instead", + ReplaceWith("ActionWithNoInputsWithMinorVersion"), +) +@ExposedCopyVisibility +public data class ActionWithNoInputsWithMinorVersion_Untyped private constructor( + /** + * Type-unsafe map where you can put any inputs that are not yet supported by the binding + */ + public val _customInputs: Map = mapOf(), + /** + * 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 + */ + public val _customVersion: String? = null, +) : RegularAction("john-smith", "action-with-no-inputs-with-minor-version", + _customVersion ?: "v3.1") { + public constructor( + vararg pleaseUseNamedArguments: Unit, + _customInputs: Map = mapOf(), + _customVersion: String? = null, + ) : this(_customInputs = _customInputs, _customVersion = _customVersion) + + @Suppress("SpreadOperator") + override fun toYamlArguments(): LinkedHashMap = LinkedHashMap(_customInputs) + + override fun buildOutputObject(stepId: String): Action.Outputs = Outputs(stepId) +} diff --git a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/ClassNamingTest.kt b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/ClassNamingTest.kt index f32beae252..92c738e092 100644 --- a/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/ClassNamingTest.kt +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/generation/ClassNamingTest.kt @@ -1,6 +1,8 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.generation import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.MAJOR import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe @@ -9,8 +11,9 @@ class ClassNamingTest : context("buildActionClassName") { listOf( ActionCoords("irrelevant", "some-action-name", "v2") to "SomeActionName", - ActionCoords("irrelevant", "some-action-name", "v2", "subaction") to "SomeActionNameSubaction", - ActionCoords("irrelevant", "some-action-name", "v2", "foo/bar/baz") to "SomeActionNameFooBarBaz", + ActionCoords("irrelevant", "some-action-name", "v2", FULL, "subaction") to "SomeActionNameSubaction", + ActionCoords("irrelevant", "some-action-name", "v2", FULL, "foo/bar/baz") to "SomeActionNameFooBarBaz", + ActionCoords("irrelevant", "some-action-name", "v2", MAJOR, "foo/bar/baz") to "SomeActionNameFooBarBaz", ).forEach { (input, output) -> test("should get '$input' and produce '$output'") { input.buildActionClassName() shouldBe output 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 b0070d3b1f..60307692b5 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 @@ -2,6 +2,9 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.generation import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords import io.github.typesafegithub.workflows.actionbindinggenerator.domain.NewestForVersion +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.MAJOR +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.MINOR import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.ACTION import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource.TYPING_CATALOG import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.Input @@ -297,7 +300,7 @@ class GenerationTest : description = "Description", ) - val coords = ActionCoords("john-smith", "action-with", "v3", "sub/action") + val coords = ActionCoords("john-smith", "action-with", "v3", FULL, "sub/action") // when val binding = @@ -503,6 +506,60 @@ class GenerationTest : binding.shouldContainAndMatchFile("ActionWithPartlyTypings_Untyped.kt") } } + + test("action with no inputs with major version") { + // given + val actionManifestHasNoInputs = emptyMap() + val actionManifest = + Metadata( + inputs = actionManifestHasNoInputs, + name = "Action With No Inputs", + description = "Description", + ) + + val coords = ActionCoords("john-smith", "action-with-no-inputs-with-major-version", "v3.1.3", MAJOR) + + // when + val binding = + coords.generateBinding( + metadataRevision = NewestForVersion, + metadata = actionManifest, + inputTypings = Pair(emptyMap(), ACTION), + ) + + // then + assertSoftly { + binding.shouldContainAndMatchFile("ActionWithNoInputsWithMajorVersion.kt") + binding.shouldContainAndMatchFile("ActionWithNoInputsWithMajorVersion_Untyped.kt") + } + } + + test("action with no inputs with minor version") { + // given + val actionManifestHasNoInputs = emptyMap() + val actionManifest = + Metadata( + inputs = actionManifestHasNoInputs, + name = "Action With No Inputs", + description = "Description", + ) + + val coords = ActionCoords("john-smith", "action-with-no-inputs-with-minor-version", "v3.1.3", MINOR) + + // when + val binding = + coords.generateBinding( + metadataRevision = NewestForVersion, + metadata = actionManifest, + inputTypings = Pair(emptyMap(), ACTION), + ) + + // then + assertSoftly { + binding.shouldContainAndMatchFile("ActionWithNoInputsWithMinorVersion.kt") + binding.shouldContainAndMatchFile("ActionWithNoInputsWithMinorVersion_Untyped.kt") + } + } }) private fun Metadata.allInputsAsStrings(): Map = this.inputs.mapValues { StringTyping } 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..11135fa9b4 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 @@ -2,6 +2,7 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.typing import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords import io.github.typesafegithub.workflows.actionbindinggenerator.domain.CommitHash +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL import io.github.typesafegithub.workflows.actionbindinggenerator.domain.TypingActualSource import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec @@ -129,7 +130,7 @@ class TypesProvidingTest : else -> throw IOException() } } - val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub") + val actionCoord = ActionCoords("some-owner", "some-name", "v3", FULL, "some-sub") // When val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) @@ -166,7 +167,7 @@ class TypesProvidingTest : else -> throw IOException() } } - val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub") + val actionCoord = ActionCoords("some-owner", "some-name", "v3", FULL, "some-sub") // When val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) @@ -208,7 +209,7 @@ class TypesProvidingTest : else -> throw IOException() } } - val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub") + val actionCoord = ActionCoords("some-owner", "some-name", "v3", FULL, "some-sub") // When val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) @@ -250,7 +251,7 @@ class TypesProvidingTest : else -> throw IOException() } } - val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub") + val actionCoord = ActionCoords("some-owner", "some-name", "v3", FULL, "some-sub") // When val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) @@ -302,7 +303,7 @@ class TypesProvidingTest : else -> throw IOException() } } - val actionCoord = ActionCoords("some-owner", "some-name", "v3", "some-sub") + val actionCoord = ActionCoords("some-owner", "some-name", "v3", FULL, "some-sub") // When val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) @@ -354,7 +355,7 @@ class TypesProvidingTest : else -> throw IOException() } } - val actionCoord = ActionCoords("some-owner", "some-name", "v6", "some-sub") + val actionCoord = ActionCoords("some-owner", "some-name", "v6", FULL, "some-sub") // When val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) diff --git a/docs/user-guide/using-actions.md b/docs/user-guide/using-actions.md index c1828fb59f..9c5e0ec53f 100644 --- a/docs/user-guide/using-actions.md +++ b/docs/user-guide/using-actions.md @@ -17,6 +17,24 @@ To add a dependency on an action: corresponding to a released version). If an action's manifest is defined in a subdirectory, like the popular `gradle/actions/setup-gradle@v3`, replace the slashes in the action name with `__`, so in this case it would be `@file:DependsOn("gradle:actions__setup-gradle:v3")`. + + Additionally, the name part can have the suffix `___major` or `___minor`. Without these suffixes if you request + a version `v1.2.3`, the generated YAML will also use exactly `v1.2.3` unless you use a custom version override. + with the `___major` suffix, it would only write `v1` to the generated YAML, with the `___minor` suffix `v1.2`. + + This is especially useful when combined with a version range. The problem with using `v1` or `v1.2` is, that for + GitHub actions these are changing tags or changing branches and not static releases. But in the Maven world + a version that does not end in `-SNAPSHOT` is considered immutable and is not expected to change. This means that + if a new version of the action is released that adds a new input, you cannot use it easily as you still have the old + `v1` artifact in your Maven cache and it will not be updated usually, even though the binding server provides a new + binding including the added input. + + To mitigate this problem you can for example use a dependency like `gradle/actions/setup-gradle___major@[v3,v4)`. + This will resolve to the latest `v3.x.y` version and thus include any newly added inputs, but still only write `v3` + to the YAML. Without the `___major` suffix or a not semantically matching range like `[v3,v5)` or even `[v3,v4]` you + will get problems with the consistency check as then the YAML output changes as soon as a new version is released. + For a minor version you would accordingly use the `___minor` suffix together with a range like `[v4.0,v4.1)` to get + the latest `v4.0` release if the action in question provides such a tag or branch. 3. Use the action by importing a class like `io.github.typesafegithub.workflows.actions.actions.Checkout`. For every action, a binding will be generated. However, some less popular actions don't have typings configured for diff --git a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt index fb41166eba..bee18d17a5 100644 --- a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt +++ b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/Main.kt @@ -2,6 +2,8 @@ package io.github.typesafegithub.workflows.jitbindingserver import io.github.reactivecircus.cache4k.Cache import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL import io.github.typesafegithub.workflows.actionbindinggenerator.domain.prettyPrint import io.github.typesafegithub.workflows.mavenbinding.Artifact import io.github.typesafegithub.workflows.mavenbinding.JarArtifact @@ -71,17 +73,8 @@ private fun Route.metadata(refresh: Boolean = false) { return@get } - val owner = call.parameters["owner"]!! - val nameAndPath = call.parameters["name"]!!.split("__") - val name = nameAndPath.first() + val actionCoords = call.toActionCoords(version = "irrelevant") val file = call.parameters["file"]!! - val actionCoords = - ActionCoords( - owner = owner, - name = name, - version = "irrelevant", - path = nameAndPath.drop(1).joinToString("/").takeUnless { it.isBlank() }, - ) val bindingArtifacts = actionCoords.buildPackageArtifacts(githubToken = getGithubToken()) if (file in bindingArtifacts) { when (val artifact = bindingArtifacts[file]) { @@ -144,17 +137,7 @@ private suspend fun ApplicationCall.toBindingArtifacts( bindingsCache: Cache>>, refresh: Boolean, ): Map? { - val owner = parameters["owner"]!! - val nameAndPath = parameters["name"]!!.split("__") - val name = nameAndPath.first() - val version = parameters["version"]!! - val actionCoords = - ActionCoords( - owner = owner, - name = name, - version = version, - path = nameAndPath.drop(1).joinToString("/").takeUnless { it.isBlank() }, - ) + val actionCoords = toActionCoords() println("➡️ Requesting ${actionCoords.prettyPrint}") val bindingArtifacts = if (refresh) { @@ -174,3 +157,28 @@ private fun Result.Companion.failure(): Result = failure(object : Throw private fun Result.Companion.of(value: T?): Result = value?.let { success(it) } ?: failure() private val deliverOnRefreshRoute = System.getenv("GWKT_DELIVER_ON_REFRESH").toBoolean() + +private fun ApplicationCall.toActionCoords(version: String = parameters["version"]!!): ActionCoords { + val owner = parameters["owner"]!! + val nameAndPathAndSignificantVersionParts = parameters["name"]!!.split("___", limit = 2) + val nameAndPath = nameAndPathAndSignificantVersionParts.first() + val significantVersion = + nameAndPathAndSignificantVersionParts + .drop(1) + .takeIf { it.isNotEmpty() } + ?.single() + ?.let { significantVersionString -> + SignificantVersion + .entries + .find { it.name.equals(significantVersionString, ignoreCase = true) } + } ?: FULL + val nameAndPathParts = nameAndPath.split("__") + val name = nameAndPathParts.first() + val path = + nameAndPathParts + .drop(1) + .joinToString("/") + .takeUnless { it.isBlank() } + + return ActionCoords(owner, name, version, significantVersion, path) +} diff --git a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuilding.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuilding.kt index 8a8c854b6d..a9af497455 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuilding.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuilding.kt @@ -1,15 +1,16 @@ package io.github.typesafegithub.workflows.mavenbinding import io.github.typesafegithub.workflows.actionbindinggenerator.domain.ActionCoords +import io.github.typesafegithub.workflows.actionbindinggenerator.domain.SignificantVersion.FULL import io.github.typesafegithub.workflows.actionbindinggenerator.domain.fullName import io.github.typesafegithub.workflows.shared.internal.fetchAvailableVersions import java.time.format.DateTimeFormatter internal suspend fun ActionCoords.buildMavenMetadataFile(githubToken: String): String { - val availableMajorVersions = + val availableVersions = fetchAvailableVersions(owner = owner, name = name, githubToken = githubToken) - .filter { it.isMajorVersion() } - val newest = availableMajorVersions.max() + .filter { it.isMajorVersion() || (significantVersion < FULL) } + val newest = availableVersions.max() val lastUpdated = DateTimeFormatter .ofPattern("yyyyMMddHHmmss") @@ -23,7 +24,7 @@ internal suspend fun ActionCoords.buildMavenMetadataFile(githubToken: String): S $newest $newest -${availableMajorVersions.joinToString(separator = "\n") { +${availableVersions.joinToString(separator = "\n") { " $it" }}