Repository: typesafegithub/github-workflows-kt
Pull request #1020 (2023-10-13 12:32:39)

[#941] refactor: move typing resolving to abg
It's a step towards having the bindings generation process encapsulated within a single module: action-bindings-generator. Code-generator shouldn't be aware of this implementation detail.


Patch:
diff --git a/automation/action-binding-generator/api/action-binding-generator.api b/automation/action-binding-generator/api/action-binding-generator.api
index 8b4b081e5..6a7ab614d 100644
--- a/automation/action-binding-generator/api/action-binding-generator.api
+++ b/automation/action-binding-generator/api/action-binding-generator.api
@@ -16,7 +16,11 @@ public final class io/github/typesafegithub/workflows/actionbindinggenerator/Act
 }
 
 public final class io/github/typesafegithub/workflows/actionbindinggenerator/GenerationKt {
-	public static final fun generateBinding (Lio/github/typesafegithub/workflows/actionsmetadata/model/ActionCoords;Ljava/util/Map;Lkotlin/jvm/functions/Function1;)Lio/github/typesafegithub/workflows/actionbindinggenerator/ActionBinding;
-	public static synthetic fun generateBinding$default (Lio/github/typesafegithub/workflows/actionsmetadata/model/ActionCoords;Ljava/util/Map;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/ActionBinding;
+	public static final fun generateBinding (Lio/github/typesafegithub/workflows/actionsmetadata/model/ActionCoords;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Ljava/util/Map;)Lio/github/typesafegithub/workflows/actionbindinggenerator/ActionBinding;
+	public static synthetic fun generateBinding$default (Lio/github/typesafegithub/workflows/actionsmetadata/model/ActionCoords;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lio/github/typesafegithub/workflows/actionbindinggenerator/ActionBinding;
+}
+
+public final class io/github/typesafegithub/workflows/actionbindinggenerator/TypesProvidingKt {
+	public static final fun deleteActionTypesYamlCacheIfObsolete ()V
 }
 
diff --git a/automation/action-binding-generator/build.gradle.kts b/automation/action-binding-generator/build.gradle.kts
index 964b1822a..01d0e2a2d 100644
--- a/automation/action-binding-generator/build.gradle.kts
+++ b/automation/action-binding-generator/build.gradle.kts
@@ -11,6 +11,7 @@ dependencies {
     implementation(projects.automation.metadataReading)
     implementation(projects.automation.textUtils)
     implementation("com.squareup:kotlinpoet:1.14.2")
+    implementation("com.charleskorn.kaml:kaml:0.55.0")
 
     testImplementation(projects.library)
 }
diff --git a/automation/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/Generation.kt b/automation/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/Generation.kt
index ece4169d0..6a8978327 100644
--- a/automation/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/Generation.kt
+++ b/automation/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/Generation.kt
@@ -24,6 +24,7 @@ import io.github.typesafegithub.workflows.metadatareading.Metadata
 import io.github.typesafegithub.workflows.metadatareading.fetchMetadata
 import io.github.typesafegithub.workflows.metadatareading.prettyPrint
 import io.github.typesafegithub.workflows.textutils.toCamelCase
+import java.nio.file.Path
 
 public data class ActionBinding(
     val kotlinCode: String,
@@ -45,8 +46,9 @@ private object Properties {
 }
 
 public fun ActionCoords.generateBinding(
-    inputTypings: Map<String, Typing> = emptyMap(),
     fetchMetadataImpl: ActionCoords.() -> Metadata = { fetchMetadata() },
+    commitHash: String? = getCommitHashFromFileSystem(),
+    inputTypings: Map<String, Typing> = provideTypes(getCommitHash = { commitHash }),
 ): ActionBinding {
     require(this.version.removePrefix("v").toIntOrNull() != null) {
         "Only major versions are supported, and '${this.version}' was given!"
@@ -421,3 +423,7 @@ private val String.nestedCommentsSanitized
             .replace("`[^`]++`".toRegex()) {
                 it.value.replace("&#42;", "`&#42;`")
             }
+
+private fun ActionCoords.getCommitHashFromFileSystem(): String? =
+    Path.of("actions", owner, name.substringBefore('/'), version, "commit-hash.txt").toFile()
+        .let { if (it.exists()) it.readText().trim() else null }
diff --git a/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/types/TypesProviding.kt b/automation/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/TypesProviding.kt
similarity index 76%
rename from automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/types/TypesProviding.kt
rename to automation/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/TypesProviding.kt
index b3fddf04e..6e6358e01 100644
--- a/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/types/TypesProviding.kt
+++ b/automation/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/TypesProviding.kt
@@ -1,7 +1,6 @@
-package io.github.typesafegithub.workflows.codegenerator.types
+package io.github.typesafegithub.workflows.actionbindinggenerator
 
 import com.charleskorn.kaml.Yaml
-import io.github.typesafegithub.workflows.actionsmetadata.model.ActionBindingRequest
 import io.github.typesafegithub.workflows.actionsmetadata.model.ActionCoords
 import io.github.typesafegithub.workflows.actionsmetadata.model.ActionType
 import io.github.typesafegithub.workflows.actionsmetadata.model.ActionTypeEnum
@@ -14,10 +13,7 @@ import io.github.typesafegithub.workflows.actionsmetadata.model.IntegerWithSpeci
 import io.github.typesafegithub.workflows.actionsmetadata.model.ListOfTypings
 import io.github.typesafegithub.workflows.actionsmetadata.model.StringTyping
 import io.github.typesafegithub.workflows.actionsmetadata.model.Typing
-import io.github.typesafegithub.workflows.actionsmetadata.model.TypingsSource
 import io.github.typesafegithub.workflows.metadatareading.fetchUri
-import io.github.typesafegithub.workflows.metadatareading.prettyPrint
-import io.github.typesafegithub.workflows.metadatareading.releasesUrl
 import io.github.typesafegithub.workflows.textutils.toPascalCase
 import kotlinx.serialization.decodeFromString
 import java.io.File
@@ -26,20 +22,17 @@ import java.net.URI
 import java.nio.file.Path
 import java.time.LocalDate
 
-fun ActionBindingRequest.provideTypes(
+internal fun ActionCoords.provideTypes(
     fetchUri: (URI) -> String = ::fetchUri,
-    getCommitHash: (ActionCoords) -> String = ::getCommitHash,
+    getCommitHash: (ActionCoords) -> String? = ::getCommitHash,
 ): Map<String, Typing> =
-    when (typingsSource) {
-        is TypingsSource.CodeGenerator -> (typingsSource as TypingsSource.CodeGenerator).inputTypings
-        TypingsSource.ActionTypes ->
-            this@provideTypes.actionCoords
-                .fetchTypingMetadata(fetchUri, getCommitHash).toTypesMap()
-    }
+    getLocalTypings(this)?.let { myYaml.decodeFromString<ActionTypes>(it).toTypesMap() }
+        ?: this.fetchTypingMetadata(fetchUri, getCommitHash)?.toTypesMap()
+        ?: emptyMap()
 
-val actionTypesYamlDir = File("build/action-types-yaml")
+private val actionTypesYamlDir: File = File("build/action-types-yaml")
 
-fun deleteActionTypesYamlCacheIfObsolete() {
+public fun deleteActionTypesYamlCacheIfObsolete() {
     val today = LocalDate.now().toString()
     val dateTxt = actionTypesYamlDir.resolve("date.txt")
     val cacheUpToDate = dateTxt.canRead() && dateTxt.readText() == today
@@ -57,15 +50,15 @@ private fun ActionCoords.actionTypesYamlUrl(gitRef: String) = "https://raw.githu
 
 private fun ActionCoords.fetchTypingMetadata(
     fetchUri: (URI) -> String = ::fetchUri,
-    getCommitHash: (ActionCoords) -> String = ::getCommitHash,
-): ActionTypes {
+    getCommitHash: (ActionCoords) -> String? = ::getCommitHash,
+): ActionTypes? {
     val cacheFile = actionTypesYamlDir.resolve("$owner-${name.replace('/', '_')}-$version.yml")
     if (cacheFile.canRead()) {
         println("  ... types from cache: $cacheFile")
         return myYaml.decodeFromStringOrDefaultIfEmpty(cacheFile.readText(), ActionTypes())
     }
 
-    val commitHash = getCommitHash(this)
+    val commitHash = getCommitHash(this) ?: return null
     val list = listOf(actionTypesYmlUrl(commitHash), actionTypesYamlUrl(commitHash))
     val typesMetadataYaml =
         list.firstNotNullOfOrNull { url ->
@@ -75,19 +68,32 @@ private fun ActionCoords.fetchTypingMetadata(
             } catch (e: IOException) {
                 null
             }
-        } ?: error(
-            "$prettyPrint\n†Can't fetch any of those URLs:\n- ${list.joinToString(separator = "\n- ")}\n" +
-                "Check release page $releasesUrl",
-        )
+        } ?: return null
 
     cacheFile.parentFile.mkdirs()
     cacheFile.writeText(typesMetadataYaml)
     return myYaml.decodeFromStringOrDefaultIfEmpty(typesMetadataYaml, ActionTypes())
 }
 
-internal fun getCommitHash(actionCoords: ActionCoords) =
+internal fun getCommitHash(actionCoords: ActionCoords): String? =
     Path.of("actions", actionCoords.owner, actionCoords.name, actionCoords.version, "commit-hash.txt")
-        .toFile().readText().trim()
+        .toFile().let {
+            if (it.exists()) it.readText().trim() else null
+        }
+
+internal fun getLocalTypings(actionCoords: ActionCoords): String? {
+    val pathBeforeVersion = Path.of("actions", actionCoords.owner, actionCoords.name.split("/").first(), actionCoords.version)
+    val subnames = actionCoords.name.split("/").drop(1).joinToString("/")
+    val fullPath =
+        if (subnames.isNotEmpty()) {
+            pathBeforeVersion.resolve(subnames).resolve("action-types.yml")
+        } else {
+            pathBeforeVersion.resolve("action-types.yml")
+        }
+    return fullPath.toFile().let {
+        if (it.exists()) it.readText() else null
+    }
+}
 
 internal fun ActionTypes.toTypesMap(): Map<String, Typing> {
     return inputs.mapValues { (key, value) ->
diff --git a/automation/code-generator/build.gradle.kts b/automation/code-generator/build.gradle.kts
index 34c19f763..9e300cb04 100644
--- a/automation/code-generator/build.gradle.kts
+++ b/automation/code-generator/build.gradle.kts
@@ -7,7 +7,6 @@ plugins {
 }
 
 dependencies {
-    implementation("com.charleskorn.kaml:kaml:0.55.0")
     implementation("com.squareup:kotlinpoet:1.14.2")
     implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
 
diff --git a/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/GenerationEntryPoint.kt b/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/GenerationEntryPoint.kt
index 07d93a925..12272240f 100644
--- a/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/GenerationEntryPoint.kt
+++ b/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/GenerationEntryPoint.kt
@@ -1,14 +1,13 @@
 package io.github.typesafegithub.workflows.codegenerator
 
 import io.github.typesafegithub.workflows.actionbindinggenerator.ActionBinding
+import io.github.typesafegithub.workflows.actionbindinggenerator.deleteActionTypesYamlCacheIfObsolete
 import io.github.typesafegithub.workflows.actionbindinggenerator.generateBinding
 import io.github.typesafegithub.workflows.actionsmetadata.bindingsToGenerate
 import io.github.typesafegithub.workflows.actionsmetadata.model.ActionBindingRequest
 import io.github.typesafegithub.workflows.actionsmetadata.model.ActionCoords
 import io.github.typesafegithub.workflows.actionsmetadata.model.TypingsSource
 import io.github.typesafegithub.workflows.actionsmetadata.model.Version
-import io.github.typesafegithub.workflows.codegenerator.types.deleteActionTypesYamlCacheIfObsolete
-import io.github.typesafegithub.workflows.codegenerator.types.provideTypes
 import io.github.typesafegithub.workflows.dsl.expressions.generateEventPayloads
 import io.github.typesafegithub.workflows.metadatareading.deleteActionYamlCacheIfObsolete
 import io.github.typesafegithub.workflows.metadatareading.prettyPrint
@@ -36,8 +35,7 @@ private fun generateBindings(): List<Pair<ActionBindingRequest, ActionBinding>>
     val requestsAndBindings =
         bindingsToGenerate.map { actionBindingRequest ->
             println("Generating ${actionBindingRequest.actionCoords.prettyPrint}")
-            val inputTypings = actionBindingRequest.provideTypes()
-            val binding = actionBindingRequest.actionCoords.generateBinding(inputTypings)
+            val binding = actionBindingRequest.actionCoords.generateBinding()
             Pair(actionBindingRequest, binding)
         }
     requestsAndBindings.forEach { (_, binding) ->
diff --git a/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/updating/CreateActionUpdatePRs.kt b/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/updating/CreateActionUpdatePRs.kt
index 7ebd65a63..6d520cce6 100644
--- a/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/updating/CreateActionUpdatePRs.kt
+++ b/automation/code-generator/src/main/kotlin/io/github/typesafegithub/workflows/codegenerator/updating/CreateActionUpdatePRs.kt
@@ -6,7 +6,6 @@ import io.github.typesafegithub.workflows.actionsmetadata.bindingsToGenerate
 import io.github.typesafegithub.workflows.actionsmetadata.model.ActionBindingRequest
 import io.github.typesafegithub.workflows.actionsmetadata.model.ActionCoords
 import io.github.typesafegithub.workflows.actionsmetadata.model.isTopLevel
-import io.github.typesafegithub.workflows.codegenerator.types.provideTypes
 import io.github.typesafegithub.workflows.codegenerator.versions.GithubRef
 import io.github.typesafegithub.workflows.codegenerator.versions.GithubTag
 import io.github.typesafegithub.workflows.codegenerator.versions.getGithubToken
@@ -81,7 +80,7 @@ private suspend fun createPullRequest(
 
 private fun ActionBindingRequest.generateBindingForCommit(commitHash: String): ActionBinding =
     actionCoords.generateBinding(
-        inputTypings = provideTypes(getCommitHash = { commitHash }),
+        commitHash = commitHash,
         fetchMetadataImpl = { fetchMetadata(commitHash = commitHash, useCache = false) },
     )
 


Test patch:
diff --git a/automation/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/GenerationTest.kt b/automation/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/GenerationTest.kt
index 1fa5cb1a5..c20f69a36 100644
--- a/automation/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/GenerationTest.kt
+++ b/automation/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/GenerationTest.kt
@@ -50,7 +50,7 @@ class GenerationTest : FunSpec({
         val coords = ActionCoords("john-smith", "simple-action-with-required-string-inputs", "v3")
 
         // when
-        val binding = coords.generateBinding { actionManifest }
+        val binding = coords.generateBinding(fetchMetadataImpl = { actionManifest })
 
         // then
         binding.shouldMatchFile("SimpleActionWithRequiredStringInputsV3.kt")
@@ -212,7 +212,7 @@ class GenerationTest : FunSpec({
         val coords = ActionCoords("john-smith", "action-with-outputs", "v3")
 
         // when
-        val binding = coords.generateBinding { actionManifest }
+        val binding = coords.generateBinding(fetchMetadataImpl = { actionManifest })
 
         // then
         binding.shouldMatchFile("ActionWithOutputsV3.kt")
@@ -241,7 +241,7 @@ class GenerationTest : FunSpec({
 
         shouldThrowAny {
             // when
-            coords.generateBinding(inputTypings) { actionManifest }
+            coords.generateBinding(fetchMetadataImpl = { actionManifest }, inputTypings = inputTypings)
         }.shouldHaveMessage(
             // then
             """
@@ -265,7 +265,7 @@ class GenerationTest : FunSpec({
         val coords = ActionCoords("john-smith", "action-with-no-inputs", "v3")
 
         // when
-        val binding = coords.generateBinding { actionManifest }
+        val binding = coords.generateBinding(fetchMetadataImpl = { actionManifest })
 
         // then
         binding.shouldMatchFile("ActionWithNoInputsV3.kt")
@@ -284,7 +284,7 @@ class GenerationTest : FunSpec({
         val coords = ActionCoords("john-smith", "deprecated-action", "v2", deprecatedByVersion = "v3")
 
         // when
-        val binding = coords.generateBinding { actionManifest }
+        val binding = coords.generateBinding(fetchMetadataImpl = { actionManifest })
 
         // then
         binding.shouldMatchFile("DeprecatedActionV2.kt")
@@ -317,7 +317,7 @@ class GenerationTest : FunSpec({
         val coords = ActionCoords("john-smith", "action-with-deprecated-input-and-name-clash", "v2")
 
         // when
-        val binding = coords.generateBinding { actionManifest }
+        val binding = coords.generateBinding(fetchMetadataImpl = { actionManifest })
 
         // then
         binding.shouldMatchFile("ActionWithDeprecatedInputAndNameClashV2.kt")
@@ -355,7 +355,7 @@ class GenerationTest : FunSpec({
         val coords = ActionCoords("john-smith", "simple-action-with-lists", "v3")
 
         // when
-        val binding = coords.generateBinding(inputTypings) { actionManifest }
+        val binding = coords.generateBinding(fetchMetadataImpl = { actionManifest }, inputTypings = inputTypings)
 
         // then
         binding.shouldMatchFile("SimpleActionWithListsV3.kt")
diff --git a/automation/code-generator/src/test/kotlin/io/github/typesafegithub/workflows/codegenerator/types/TypesProvidingTest.kt b/automation/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/TypesProvidingTest.kt
similarity index 81%
rename from automation/code-generator/src/test/kotlin/io/github/typesafegithub/workflows/codegenerator/types/TypesProvidingTest.kt
rename to automation/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/TypesProvidingTest.kt
index 5284f1435..3f0de0a16 100644
--- a/automation/code-generator/src/test/kotlin/io/github/typesafegithub/workflows/codegenerator/types/TypesProvidingTest.kt
+++ b/automation/action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/TypesProvidingTest.kt
@@ -1,6 +1,5 @@
-package io.github.typesafegithub.workflows.codegenerator.types
+package io.github.typesafegithub.workflows.actionbindinggenerator
 
-import io.github.typesafegithub.workflows.actionsmetadata.model.ActionBindingRequest
 import io.github.typesafegithub.workflows.actionsmetadata.model.ActionCoords
 import io.github.typesafegithub.workflows.actionsmetadata.model.BooleanTyping
 import io.github.typesafegithub.workflows.actionsmetadata.model.EnumTyping
@@ -9,7 +8,6 @@ import io.github.typesafegithub.workflows.actionsmetadata.model.IntegerTyping
 import io.github.typesafegithub.workflows.actionsmetadata.model.IntegerWithSpecialValueTyping
 import io.github.typesafegithub.workflows.actionsmetadata.model.ListOfTypings
 import io.github.typesafegithub.workflows.actionsmetadata.model.StringTyping
-import io.github.typesafegithub.workflows.actionsmetadata.model.TypingsSource
 import io.kotest.core.spec.style.FunSpec
 import io.kotest.matchers.shouldBe
 
@@ -51,14 +49,10 @@ class TypesProvidingTest : FunSpec({
                   - admin
                   - guest
             """.trimIndent()
-        val actionBindingRequest =
-            ActionBindingRequest(
-                actionCoords = ActionCoords("some-owner", "some-name", "v1"),
-                typingsSource = TypingsSource.ActionTypes,
-            )
+        val actionCoord = ActionCoords("some-owner", "some-name", "v1")
 
         // When
-        val types = actionBindingRequest.provideTypes({ actionTypesYml }, { "some-hash" })
+        val types = actionCoord.provideTypes({ actionTypesYml }, { "some-hash" })
 
         // Then
         types shouldBe