Skip to content

Commit

Permalink
feat(abg): use typings for older version if available (#1204)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
krzema12 committed Dec 23, 2023
1 parent c4fa425 commit 73322ad
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.typesafegithub.workflows.actionbindinggenerator
import com.charleskorn.kaml.Yaml
import io.github.typesafegithub.workflows.actionbindinggenerator.TypingActualSource.ACTION
import io.github.typesafegithub.workflows.actionbindinggenerator.TypingActualSource.TYPING_CATALOG
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import java.io.IOException
import java.net.URI
Expand All @@ -26,6 +27,10 @@ private fun ActionCoords.actionTypesFromCatalog() =
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
"main/typings/$owner/$repoName/$version/$subName/action-types.yml"

private fun ActionCoords.catalogMetadata() =
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
"main/typings/$owner/$repoName/metadata.yml"

private fun ActionCoords.actionTypesYamlUrl(gitRef: String) =
"https://raw.githubusercontent.com/$owner/$repoName/$gitRef/$subName/action-types.yaml"

Expand Down Expand Up @@ -54,7 +59,34 @@ private fun ActionCoords.fetchTypingMetadata(
}

private fun ActionCoords.fetchFromTypingsFromCatalog(fetchUri: (URI) -> String = ::fetchUri): Pair<ActionTypes, TypingActualSource>? =
fetchTypingsFromUrl(url = actionTypesFromCatalog(), fetchUri = fetchUri)?.let { Pair(it, TYPING_CATALOG) }
(
fetchTypingsFromUrl(url = actionTypesFromCatalog(), fetchUri = fetchUri)
?: fetchTypingsForOlderVersionFromCatalog(fetchUri = fetchUri)
)
?.let { Pair(it, TYPING_CATALOG) }

private fun ActionCoords.fetchTypingsForOlderVersionFromCatalog(fetchUri: (URI) -> String): ActionTypes? {
val metadataUrl = this.catalogMetadata()
val metadataYml =
try {
println(" ... metadata from $metadataUrl")
fetchUri(URI(metadataUrl))
} catch (e: IOException) {
return null
}
val metadata = myYaml.decodeFromString<CatalogMetadata>(metadataYml)
val fallbackVersion =
metadata.versionsWithTypings
.filter { it.versionToInt() < this.version.versionToInt() }
.maxByOrNull { it.versionToInt() }
?: run {
println(" ... no fallback version found!")
return null
}
println(" ... using fallback version: $fallbackVersion")
val adjustedCoords = this.copy(version = fallbackVersion)
return fetchTypingsFromUrl(url = adjustedCoords.actionTypesFromCatalog(), fetchUri = fetchUri)
}

private fun fetchTypingsFromUrl(
url: String,
Expand Down Expand Up @@ -118,3 +150,10 @@ private inline fun <reified T> Yaml.decodeFromStringOrDefaultIfEmpty(
} else {
default
}

private fun String.versionToInt() = lowercase().removePrefix("v").toInt()

@Serializable
private data class CatalogMetadata(
val versionsWithTypings: List<String>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,37 @@ class TypesProvidingTest : FunSpec({
}

context("order of using typings from various sources") {
val hostedByActionYml = "inputs:\n hosted-by-action-yml:\n type: string"
val hostedByActionYaml = "inputs:\n hosted-by-action-yaml:\n type: string"
val storedInTypingCatalog = "inputs:\n stored-in-typing-catalog:\n type: string"
val hostedByActionYml =
"""
inputs:
hosted-by-action-yml:
type: string
""".trimIndent()
val hostedByActionYaml =
"""
inputs:
hosted-by-action-yaml:
type: string
""".trimIndent()
val storedInTypingCatalog =
"""
inputs:
stored-in-typing-catalog:
type: string
""".trimIndent()
val metadata =
"""
"versionsWithTypings":
- "v2"
- "v3"
- "v4"
""".trimIndent()
val storedInTypingCatalogForOlderVersion =
"""
inputs:
stored-in-typing-catalog-for-older-version:
type: string
""".trimIndent()

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

test("only stored in typing catalog for older version") {
// Given
val fetchUri: (URI) -> String = {
when (it) {
URI(
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
"main/typings/some-owner/some-name/metadata.yml",
),
-> metadata
URI(
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
"main/typings/some-owner/some-name/v4//action-types.yml",
),
-> storedInTypingCatalogForOlderVersion
else -> throw IOException()
}
}
val actionCoord = ActionCoords("some-owner", "some-name", "v6")

// When
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri)

// Then
types shouldBe Pair(mapOf("stored-in-typing-catalog-for-older-version" to StringTyping), TypingActualSource.TYPING_CATALOG)
}

test("metadata available but no version available") {
// Given
val fetchUri: (URI) -> String = {
when (it) {
URI(
"https://raw.githubusercontent.com/typesafegithub/github-actions-typing-catalog/" +
"main/typings/some-owner/some-name/metadata.yml",
),
-> metadata
else -> throw IOException()
}
}
val actionCoord = ActionCoords("some-owner", "some-name", "v1")

// When
val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri)

// Then
types shouldBe Pair(emptyMap(), null)
}

test("no typings at all") {
// Given
val fetchUri: (URI) -> String = { throw IOException() }
Expand Down

0 comments on commit 73322ad

Please sign in to comment.