From 752bbb282503b3e529c43f150bbfa272a81de57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Mon, 19 Aug 2024 16:01:37 +0200 Subject: [PATCH 1/4] feat(abg): allow usage of version ranges by generating only major or minor version even with more detailed version --- .../api/action-binding-generator.api | 22 ++++-- .../domain/ActionCoords.kt | 7 +- .../domain/SignificantVersion.kt | 10 +++ .../generation/Generation.kt | 15 +++- .../ActionWithNoInputsWithMajorVersion.kt | 51 +++++++++++++ ...ionWithNoInputsWithMajorVersion_Untyped.kt | 76 +++++++++++++++++++ .../ActionWithNoInputsWithMinorVersion.kt | 51 +++++++++++++ ...ionWithNoInputsWithMinorVersion_Untyped.kt | 76 +++++++++++++++++++ .../generation/ClassNamingTest.kt | 7 +- .../generation/GenerationTest.kt | 59 +++++++++++++- .../typing/TypesProvidingTest.kt | 13 ++-- docs/user-guide/using-actions.md | 18 +++++ .../jitbindingserver/ActionCoords.kt | 26 ++++++- .../mavenbinding/ActionCoordsUtils.kt | 5 +- .../mavenbinding/MavenMetadataBuilding.kt | 9 ++- 15 files changed, 420 insertions(+), 25 deletions(-) create mode 100644 action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/domain/SignificantVersion.kt create mode 100644 action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion.kt create mode 100644 action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion_Untyped.kt create mode 100644 action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion.kt create mode 100644 action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion_Untyped.kt 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 69205b6339..24da3585e9 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/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..816ada8366 --- /dev/null +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion.kt @@ -0,0 +1,51 @@ +// 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..be8da8adfa --- /dev/null +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMajorVersion_Untyped.kt @@ -0,0 +1,76 @@ +// 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..4d993f162b --- /dev/null +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion.kt @@ -0,0 +1,51 @@ +// 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..c25b6ab78f --- /dev/null +++ b/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/bindingsfromunittests/ActionWithNoInputsWithMinorVersion_Untyped.kt @@ -0,0 +1,76 @@ +// 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 9b06cd36be..4d56781e1a 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 @@ -3,6 +3,7 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.typing import com.charleskorn.kaml.ForbiddenAnchorOrAliasException 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 @@ -193,7 +194,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) @@ -230,7 +231,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) @@ -272,7 +273,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) @@ -314,7 +315,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) @@ -366,7 +367,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) @@ -418,7 +419,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 dc0079c1d1..fd38336138 100644 --- a/docs/user-guide/using-actions.md +++ b/docs/user-guide/using-actions.md @@ -20,6 +20,24 @@ To add a dependency on an action: `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/ActionCoords.kt b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt index 37c7dbc0f4..ba59d24a50 100644 --- a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt +++ b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt @@ -1,14 +1,32 @@ package io.github.typesafegithub.workflows.jitbindingserver 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.ktor.http.Parameters fun Parameters.extractActionCoords(extractVersion: Boolean): ActionCoords { val owner = this["owner"]!! - val nameAndPath = this["name"]!!.split("__") - val name = nameAndPath.first() - val path = nameAndPath.drop(1).joinToString("/").takeUnless { it.isBlank() } + val nameAndPathAndSignificantVersionParts = this["name"]!!.split("___", limit = 2) + val nameAndPath = nameAndPathAndSignificantVersionParts.first() + val significantVersion = + nameAndPathAndSignificantVersionParts + .drop(1) + .takeIf { it.isNotEmpty() } + ?.single() + ?.let { significantVersionString -> + SignificantVersion + .entries + .find { it.name.lowercase() == significantVersionString } + } ?: FULL + val nameAndPathParts = nameAndPath.split("__") + val name = nameAndPathParts.first() + val path = + nameAndPathParts + .drop(1) + .joinToString("/") + .takeUnless { it.isBlank() } val version = if (extractVersion) this["version"]!! else "irrelevant" - return ActionCoords(owner, name, version, path) + return ActionCoords(owner, name, version, significantVersion, path) } diff --git a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt index 5739a0aa1a..ad362478c3 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt @@ -1,6 +1,9 @@ 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.subName -internal val ActionCoords.mavenName: String get() = "$name${subName.replace("/", "__")}" +internal val ActionCoords.mavenName: String get() = "$name${subName.replace("/", "__")}${ + significantVersion.takeUnless { it == FULL }?.let { "___${it.name.lowercase()}" } ?: "" +}" 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 7c8306b85e..d94d3d4d2e 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,6 +1,7 @@ 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.shared.internal.fetchAvailableVersions import io.github.typesafegithub.workflows.shared.internal.model.Version import java.time.format.DateTimeFormatter @@ -9,10 +10,10 @@ internal suspend fun ActionCoords.buildMavenMetadataFile( githubToken: String, fetchAvailableVersions: suspend (owner: String, name: String, githubToken: String?) -> List = ::fetchAvailableVersions, ): String? { - val availableMajorVersions = + val availableVersions = fetchAvailableVersions(owner, name, githubToken) - .filter { it.isMajorVersion() } - val newest = availableMajorVersions.maxOrNull() ?: return null + .filter { it.isMajorVersion() || (significantVersion < FULL) } + val newest = availableVersions.maxOrNull() ?: return null val lastUpdated = DateTimeFormatter .ofPattern("yyyyMMddHHmmss") @@ -26,7 +27,7 @@ internal suspend fun ActionCoords.buildMavenMetadataFile( $newest $newest -${availableMajorVersions.joinToString(separator = "\n") { +${availableVersions.joinToString(separator = "\n") { " $it" }} From 2cbfbfc374b83cbb025f06c5eb23a0c4fc1cca12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Mon, 24 Feb 2025 10:41:28 +0100 Subject: [PATCH 2/4] Review Feedback Vol. 1 --- .../mavenbinding/MavenMetadataBuildingTest.kt | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/maven-binding-builder/src/test/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuildingTest.kt b/maven-binding-builder/src/test/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuildingTest.kt index 3b4eea4692..713df7e800 100644 --- a/maven-binding-builder/src/test/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuildingTest.kt +++ b/maven-binding-builder/src/test/kotlin/io/github/typesafegithub/workflows/mavenbinding/MavenMetadataBuildingTest.kt @@ -1,6 +1,8 @@ package io.github.typesafegithub.workflows.mavenbinding 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.shared.internal.model.Version import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.nulls.shouldBeNull @@ -91,4 +93,52 @@ class MavenMetadataBuildingTest : xml.shouldBeNull() } + + (SignificantVersion.entries - FULL).forEach { significantVersion -> + test("significant version $significantVersion requested") { + // Given + val fetchAvailableVersions: suspend (String, String, String?) -> List = { owner, name, _ -> + listOf( + Version(version = "v3-beta", dateProvider = { ZonedDateTime.parse("2024-07-01T00:00:00Z") }), + Version(version = "v2", dateProvider = { ZonedDateTime.parse("2024-05-01T00:00:00Z") }), + Version(version = "v1", dateProvider = { ZonedDateTime.parse("2024-03-07T00:00:00Z") }), + Version(version = "v1.1", dateProvider = { ZonedDateTime.parse("2024-03-07T00:00:00Z") }), + Version(version = "v1.1.0", dateProvider = { ZonedDateTime.parse("2024-03-07T00:00:00Z") }), + Version(version = "v1.0.1", dateProvider = { ZonedDateTime.parse("2024-03-05T00:00:00Z") }), + Version(version = "v1.0", dateProvider = { ZonedDateTime.parse("2024-03-01T00:00:00Z") }), + Version(version = "v1.0.0", dateProvider = { ZonedDateTime.parse("2024-03-01T00:00:00Z") }), + ) + } + + val xml = + actionCoords.copy(significantVersion = significantVersion).buildMavenMetadataFile( + githubToken = "SOME_TOKEN", + fetchAvailableVersions = fetchAvailableVersions, + ) + + xml shouldBe + """ + + + owner + name___$significantVersion + + v2 + v2 + + v3-beta + v2 + v1 + v1.1 + v1.1.0 + v1.0.1 + v1.0 + v1.0.0 + + 20240501000000 + + + """.trimIndent() + } + } }) From 9a0da25111973429dee0e8d4600390197987d801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Tue, 25 Feb 2025 03:42:48 +0100 Subject: [PATCH 3/4] Review Feedback Vol. 2 --- ...uming-jit-bindings.main.do-not-compile.kts | 11 +++++ .../domain/ActionCoords.kt | 7 +++ .../domain/SignificantVersion.kt | 16 +++++++ docs/user-guide/using-actions.md | 46 ++++++++++++------- .../jitbindingserver/ActionCoords.kt | 2 +- .../mavenbinding/ActionCoordsUtils.kt | 2 +- mkdocs.yml | 1 + 7 files changed, 66 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test-script-consuming-jit-bindings.main.do-not-compile.kts b/.github/workflows/test-script-consuming-jit-bindings.main.do-not-compile.kts index 92fe969476..29962a9d80 100755 --- a/.github/workflows/test-script-consuming-jit-bindings.main.do-not-compile.kts +++ b/.github/workflows/test-script-consuming-jit-bindings.main.do-not-compile.kts @@ -1,6 +1,7 @@ #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") @file:DependsOn("io.github.typesafegithub:github-workflows-kt:1.13.0") +@file:DependsOn("io.kotest:kotest-assertions-core:5.9.1") @file:Repository("http://localhost:8080") @@ -13,6 +14,10 @@ // Using specific version. @file:DependsOn("actions:cache:v3.3.3") +// Using version ranges. +@file:DependsOn("gradle:actions__dependency-submission___major:[v3.3.1,v4-alpha)") +@file:DependsOn("gradle:actions__wrapper-validation___minor:[v4.2.1,v4.3-alpha)") + // Always untyped action. @file:DependsOn("typesafegithub:always-untyped-action-for-tests:v1") @@ -20,7 +25,10 @@ import io.github.typesafegithub.workflows.actions.actions.Cache import io.github.typesafegithub.workflows.actions.actions.Checkout import io.github.typesafegithub.workflows.actions.actions.Checkout_Untyped import io.github.typesafegithub.workflows.actions.gradle.ActionsSetupGradle +import io.github.typesafegithub.workflows.actions.gradle.ActionsDependencySubmission_Untyped +import io.github.typesafegithub.workflows.actions.gradle.ActionsWrapperValidation import io.github.typesafegithub.workflows.actions.typesafegithub.AlwaysUntypedActionForTests_Untyped +import io.kotest.matchers.shouldBe println(Checkout_Untyped(fetchTags_Untyped = "false")) println(Checkout(fetchTags = false)) @@ -29,5 +37,8 @@ println(AlwaysUntypedActionForTests_Untyped(foobar_Untyped = "baz")) println(ActionsSetupGradle()) println(Cache(path = listOf("some-path"), key = "some-key")) +ActionsDependencySubmission_Untyped().actionVersion shouldBe "v3" +ActionsWrapperValidation().actionVersion shouldBe "v4.2" + // Ensure that 'copy(...)' method is exposed. Checkout(fetchTags = false).copy(fetchTags = true) 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 24da3585e9..ad776d2c58 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 @@ -6,6 +6,13 @@ public data class ActionCoords( val owner: String, val name: String, val version: String, + /** + * The version part that is significant when generating the YAML output, + * i.e. whether to write the full version, only the major version or major and minor version. + * This is used to enable usage of Maven ranges without needing to specify a custom version + * each time instantiating an action. + * The value of this property is part of the Maven coordinates as a suffix for the [name] property. + */ val significantVersion: SignificantVersion = FULL, val path: String? = null, ) 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 index 3dd609a5a6..60662dd7d4 100644 --- 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 @@ -1,8 +1,24 @@ package io.github.typesafegithub.workflows.actionbindinggenerator.domain +/** + * The version part that is significant when generating the YAML output. + * This is used to enable usage of Maven ranges without needing to specify a custom version + * each time instantiating an action. + */ public enum class SignificantVersion { + /** + * Only write the major version to the generated YAML. + */ MAJOR, + + /** + * Only write the major and minor version to the generated YAML. + */ MINOR, + + /** + * Write the full version to the generated YAML. + */ FULL, ; diff --git a/docs/user-guide/using-actions.md b/docs/user-guide/using-actions.md index fd38336138..4b7f38d6e1 100644 --- a/docs/user-guide/using-actions.md +++ b/docs/user-guide/using-actions.md @@ -20,23 +20,35 @@ To add a dependency on an action: `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. + ??? tip "Dealing with stale Maven cache, a.k.a. using version ranges" + Additionally, the name part can have the suffix `___major` or `___minor` (three leading underscores). + 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. In the Maven world, + however, 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. And even if you remove the + old version from the Maven cache and get a new version from the bindings server, other people might also have + this outdated version in their Maven cache and then fail compilation with your changes. + + 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. + + !!! info + If an action maintainer provides pre-releases that follow certain naming conventions as documented in the + [Maven Documentation](https://maven.apache.org/pom.html#Version_Order_Specification), you might need to + adjust the upper bound. For exmple a version `v4.0-beta` is less than `v4` and thus part of the range + `[v3,v4)`. In such a case - or always, to be on the safe side - you might want to change the range to + `[v3,v4-alpha)`, as the `alpha` version is the lowest possible version in Maven semantics. 3. Use the action by importing a class like `io.github.typesafegithub.workflows.actions.actions.Checkout`. diff --git a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt index ba59d24a50..125f567649 100644 --- a/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt +++ b/jit-binding-server/src/main/kotlin/io/github/typesafegithub/workflows/jitbindingserver/ActionCoords.kt @@ -17,7 +17,7 @@ fun Parameters.extractActionCoords(extractVersion: Boolean): ActionCoords { ?.let { significantVersionString -> SignificantVersion .entries - .find { it.name.lowercase() == significantVersionString } + .find { "$it" == significantVersionString } } ?: FULL val nameAndPathParts = nameAndPath.split("__") val name = nameAndPathParts.first() diff --git a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt index ad362478c3..33dcb41f46 100644 --- a/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt +++ b/maven-binding-builder/src/main/kotlin/io/github/typesafegithub/workflows/mavenbinding/ActionCoordsUtils.kt @@ -5,5 +5,5 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.Signific import io.github.typesafegithub.workflows.actionbindinggenerator.domain.subName internal val ActionCoords.mavenName: String get() = "$name${subName.replace("/", "__")}${ - significantVersion.takeUnless { it == FULL }?.let { "___${it.name.lowercase()}" } ?: "" + significantVersion.takeUnless { it == FULL }?.let { "___$it" } ?: "" }" diff --git a/mkdocs.yml b/mkdocs.yml index 1492f9b592..c604c09a42 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,6 +24,7 @@ theme: name: Switch to light mode markdown_extensions: + - pymdownx.details - pymdownx.magiclink - pymdownx.inlinehilite - pymdownx.superfences From fdfb7d35d8522a684114826725f1e806fc15eadb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Tue, 25 Feb 2025 11:15:55 +0100 Subject: [PATCH 4/4] Review Feedback Vol. 3 --- docs/user-guide/using-actions.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/user-guide/using-actions.md b/docs/user-guide/using-actions.md index 4b7f38d6e1..f88ea7208b 100644 --- a/docs/user-guide/using-actions.md +++ b/docs/user-guide/using-actions.md @@ -28,18 +28,22 @@ To add a dependency on an action: 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. In the Maven world, - however, a version that does not end in `-SNAPSHOT` is considered immutable and is not expected to change. + however, a version that does not end with `-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. And even if you remove the old version from the Maven cache and get a new version from the bindings server, other people might also have this outdated version in their Maven cache and then fail compilation with your changes. + It's worth emphasizing that this problem is currently present only when iterating + on your workflows locally. When running on GitHub Actions, this problem doesn't + exist because the state of Maven Local repo isn't cached between the runs. 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 + `gradle:actions__setup-gradle___major:[v3,v4)` (from `v3` inclusive to `v4` exclusive). + 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.