Skip to content

Commit 73322ad

Browse files
authored
feat(abg): use typings for older version if available (#1204)
Fixes #1073. If there's no typings for a given version in the catalog, typings for an older version is tried to be fetched. Thanks to this, we cover a scenario where usually the next version of some action doesn't need to have typings defined immediately because the older version is fine. It doesn't account for breaking changes where this approach will produce an incorrect binding, however in practice it should be better than what we have now. It doesn't hold for action-hosted typings.
1 parent c4fa425 commit 73322ad

File tree

2 files changed

+118
-4
lines changed
  • action-binding-generator/src

2 files changed

+118
-4
lines changed

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.github.typesafegithub.workflows.actionbindinggenerator
33
import com.charleskorn.kaml.Yaml
44
import io.github.typesafegithub.workflows.actionbindinggenerator.TypingActualSource.ACTION
55
import io.github.typesafegithub.workflows.actionbindinggenerator.TypingActualSource.TYPING_CATALOG
6+
import kotlinx.serialization.Serializable
67
import kotlinx.serialization.decodeFromString
78
import java.io.IOException
89
import java.net.URI
@@ -26,6 +27,10 @@ private fun ActionCoords.actionTypesFromCatalog() =
2627
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
2728
"main/typings/$owner/$repoName/$version/$subName/action-types.yml"
2829

30+
private fun ActionCoords.catalogMetadata() =
31+
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
32+
"main/typings/$owner/$repoName/metadata.yml"
33+
2934
private fun ActionCoords.actionTypesYamlUrl(gitRef: String) =
3035
"https://raw.githubusercontent.com/$owner/$repoName/$gitRef/$subName/action-types.yaml"
3136

@@ -54,7 +59,34 @@ private fun ActionCoords.fetchTypingMetadata(
5459
}
5560

5661
private fun ActionCoords.fetchFromTypingsFromCatalog(fetchUri: (URI) -> String = ::fetchUri): Pair<ActionTypes, TypingActualSource>? =
57-
fetchTypingsFromUrl(url = actionTypesFromCatalog(), fetchUri = fetchUri)?.let { Pair(it, TYPING_CATALOG) }
62+
(
63+
fetchTypingsFromUrl(url = actionTypesFromCatalog(), fetchUri = fetchUri)
64+
?: fetchTypingsForOlderVersionFromCatalog(fetchUri = fetchUri)
65+
)
66+
?.let { Pair(it, TYPING_CATALOG) }
67+
68+
private fun ActionCoords.fetchTypingsForOlderVersionFromCatalog(fetchUri: (URI) -> String): ActionTypes? {
69+
val metadataUrl = this.catalogMetadata()
70+
val metadataYml =
71+
try {
72+
println(" ... metadata from $metadataUrl")
73+
fetchUri(URI(metadataUrl))
74+
} catch (e: IOException) {
75+
return null
76+
}
77+
val metadata = myYaml.decodeFromString<CatalogMetadata>(metadataYml)
78+
val fallbackVersion =
79+
metadata.versionsWithTypings
80+
.filter { it.versionToInt() < this.version.versionToInt() }
81+
.maxByOrNull { it.versionToInt() }
82+
?: run {
83+
println(" ... no fallback version found!")
84+
return null
85+
}
86+
println(" ... using fallback version: $fallbackVersion")
87+
val adjustedCoords = this.copy(version = fallbackVersion)
88+
return fetchTypingsFromUrl(url = adjustedCoords.actionTypesFromCatalog(), fetchUri = fetchUri)
89+
}
5890

5991
private fun fetchTypingsFromUrl(
6092
url: String,
@@ -118,3 +150,10 @@ private inline fun <reified T> Yaml.decodeFromStringOrDefaultIfEmpty(
118150
} else {
119151
default
120152
}
153+
154+
private fun String.versionToInt() = lowercase().removePrefix("v").toInt()
155+
156+
@Serializable
157+
private data class CatalogMetadata(
158+
val versionsWithTypings: List<String>,
159+
)

action-binding-generator/src/test/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/TypesProvidingTest.kt

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,37 @@ class TypesProvidingTest : FunSpec({
6363
}
6464

6565
context("order of using typings from various sources") {
66-
val hostedByActionYml = "inputs:\n hosted-by-action-yml:\n type: string"
67-
val hostedByActionYaml = "inputs:\n hosted-by-action-yaml:\n type: string"
68-
val storedInTypingCatalog = "inputs:\n stored-in-typing-catalog:\n type: string"
66+
val hostedByActionYml =
67+
"""
68+
inputs:
69+
hosted-by-action-yml:
70+
type: string
71+
""".trimIndent()
72+
val hostedByActionYaml =
73+
"""
74+
inputs:
75+
hosted-by-action-yaml:
76+
type: string
77+
""".trimIndent()
78+
val storedInTypingCatalog =
79+
"""
80+
inputs:
81+
stored-in-typing-catalog:
82+
type: string
83+
""".trimIndent()
84+
val metadata =
85+
"""
86+
"versionsWithTypings":
87+
- "v2"
88+
- "v3"
89+
- "v4"
90+
""".trimIndent()
91+
val storedInTypingCatalogForOlderVersion =
92+
"""
93+
inputs:
94+
stored-in-typing-catalog-for-older-version:
95+
type: string
96+
""".trimIndent()
6997

7098
test("only hosted by the action (.yml)") {
7199
// Given
@@ -166,6 +194,53 @@ class TypesProvidingTest : FunSpec({
166194
types shouldBe Pair(mapOf("hosted-by-action-yml" to StringTyping), TypingActualSource.ACTION)
167195
}
168196

197+
test("only stored in typing catalog for older version") {
198+
// Given
199+
val fetchUri: (URI) -> String = {
200+
when (it) {
201+
URI(
202+
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
203+
"main/typings/some-owner/some-name/metadata.yml",
204+
),
205+
-> metadata
206+
URI(
207+
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
208+
"main/typings/some-owner/some-name/v4//action-types.yml",
209+
),
210+
-> storedInTypingCatalogForOlderVersion
211+
else -> throw IOException()
212+
}
213+
}
214+
val actionCoord = ActionCoords("some-owner", "some-name", "v6")
215+
216+
// When
217+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri)
218+
219+
// Then
220+
types shouldBe Pair(mapOf("stored-in-typing-catalog-for-older-version" to StringTyping), TypingActualSource.TYPING_CATALOG)
221+
}
222+
223+
test("metadata available but no version available") {
224+
// Given
225+
val fetchUri: (URI) -> String = {
226+
when (it) {
227+
URI(
228+
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
229+
"main/typings/some-owner/some-name/metadata.yml",
230+
),
231+
-> metadata
232+
else -> throw IOException()
233+
}
234+
}
235+
val actionCoord = ActionCoords("some-owner", "some-name", "v1")
236+
237+
// When
238+
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri)
239+
240+
// Then
241+
types shouldBe Pair(emptyMap(), null)
242+
}
243+
169244
test("no typings at all") {
170245
// Given
171246
val fetchUri: (URI) -> String = { throw IOException() }

0 commit comments

Comments
 (0)