diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8732d20550..9c133e8444 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,12 @@ assignees: '' --- +# Component + +* [ ] github-workflows-kt (library with the DSL) +* [ ] bindings server (https://bindings.krzeminski.it) +* [ ] I don't know + # Action _Describe what you are trying to do._ @@ -23,6 +29,6 @@ _What happens instead?_ _Manual adjustment of output YAML is not a workaround. Adjusting Kotlin code in a certain way is._ -# Library version +# Version -v... +_If it's a bug in the library, specify it's version._ diff --git a/.github/workflows/_shared.main.kts b/.github/workflows/_shared.main.kts index 6635c89823..6c4bdc1c4f 100644 --- a/.github/workflows/_shared.main.kts +++ b/.github/workflows/_shared.main.kts @@ -1,5 +1,5 @@ #!/usr/bin/env kotlin -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") import io.github.typesafegithub.workflows.dsl.expressions.expr diff --git a/.github/workflows/bindings-server.main.kts b/.github/workflows/bindings-server.main.kts index ec8579a227..10e82ffcd5 100755 --- a/.github/workflows/bindings-server.main.kts +++ b/.github/workflows/bindings-server.main.kts @@ -1,6 +1,6 @@ #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:checkout:v4") diff --git a/.github/workflows/build.main.kts b/.github/workflows/build.main.kts index ba91a087ae..33bdba81b1 100755 --- a/.github/workflows/build.main.kts +++ b/.github/workflows/build.main.kts @@ -1,6 +1,6 @@ #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:checkout:v4") diff --git a/.github/workflows/check-if-generated-code-up-to-date.main.kts b/.github/workflows/check-if-generated-code-up-to-date.main.kts index 9dad995f9b..8819e12ba6 100755 --- a/.github/workflows/check-if-generated-code-up-to-date.main.kts +++ b/.github/workflows/check-if-generated-code-up-to-date.main.kts @@ -1,6 +1,6 @@ #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:checkout:v4") diff --git a/.github/workflows/end-to-end-tests.main.kts b/.github/workflows/end-to-end-tests.main.kts index 54f1cc0aec..ad100a36a9 100755 --- a/.github/workflows/end-to-end-tests.main.kts +++ b/.github/workflows/end-to-end-tests.main.kts @@ -1,7 +1,7 @@ #!/usr/bin/env kotlin @file:Repository("file://~/.m2/repository/") -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") -@file:DependsOn("io.github.typesafegithub:action-updates-checker:3.0.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.1") +@file:DependsOn("io.github.typesafegithub:action-updates-checker:3.0.1") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:checkout:v4") @file:DependsOn("actions:github-script:v7") diff --git a/.github/workflows/gradle-wrapper-validation.main.kts b/.github/workflows/gradle-wrapper-validation.main.kts index be18de0510..f2d4add54c 100755 --- a/.github/workflows/gradle-wrapper-validation.main.kts +++ b/.github/workflows/gradle-wrapper-validation.main.kts @@ -1,6 +1,6 @@ #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:checkout:v4") diff --git a/.github/workflows/release.main.kts b/.github/workflows/release.main.kts index 73aeecc983..f2b2e28834 100755 --- a/.github/workflows/release.main.kts +++ b/.github/workflows/release.main.kts @@ -1,6 +1,6 @@ #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:checkout:v4") diff --git a/.github/workflows/setup-java.main.kts b/.github/workflows/setup-java.main.kts index 67c7f8aaf8..61657be07f 100644 --- a/.github/workflows/setup-java.main.kts +++ b/.github/workflows/setup-java.main.kts @@ -1,6 +1,6 @@ #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:setup-java:v4") diff --git a/.github/workflows/setup-python.main.kts b/.github/workflows/setup-python.main.kts index 9a31bc5546..bc8c8f6ec1 100644 --- a/.github/workflows/setup-python.main.kts +++ b/.github/workflows/setup-python.main.kts @@ -1,6 +1,6 @@ #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.3.0") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:setup-python:v5") diff --git a/MAINTENANCE.md b/MAINTENANCE.md index f387be8477..b6b56271f0 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -2,9 +2,9 @@ This file describes various maintenance tasks, relevant for project maintainers # Release a new version -It currently happens monthly, around the beginning of each month. We don't need to stick to this cadence strictly, the goal is to publish small frequent updates. +It currently happens whenever necessary, there's no agreed cadence. Whenever we see there's an important bug fix or a feature to roll out, or an important dependency update, we release. -1. Remove `-SNAPSHOT` for version starting from [/github-workflows-kt/build.gradle.kts](https://github.com/typesafegithub/github-workflows-kt/blob/main/github-workflows-kt/build.gradle.kts). By building the whole project with `./gradlew build`, you will learn what other places need to be adjusted. There's one place that needs extra care: in PomBuilding.kt, there's `LATEST_RELASED_LIBRARY_VERSION` - set it to the version you're going to deploy in a minute. Once done, create a commit using this pattern for commit message: `chore: prepare for releasing version `. +1. Remove `-SNAPSHOT` for version starting from [build.gradle.kts](https://github.com/typesafegithub/github-workflows-kt/blob/main/build.gradle.kts). By building the whole project with `./gradlew build`, you will learn what other places need to be adjusted. There's one place that needs extra care: in PomBuilding.kt, there's `LATEST_RELASED_LIBRARY_VERSION` - set it to the version you're going to deploy in a minute. Once done, create a commit using this pattern for commit message: `chore: prepare for releasing version `. 1. Once CI is green for the newly merged commits, create and push an annotated tag: ``` COMMIT_TITLE=`git log -1 --pretty=%B` diff --git a/README.md b/README.md index df802e7fd0..f5ea0b50a7 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ [![License](https://img.shields.io/github/license/typesafegithub/github-workflows-kt)](https://github.com/typesafegithub/github-workflows-kt/blob/main/LICENSE) [![github-workflows-kt @ kotlinlang.slack.com](https://img.shields.io/static/v1?label=kotlinlang&message=github-workflows-kt&color=blue&logo=slack)](https://kotlinlang.slack.com/archives/C02UUATR7RC) -[github-workflows-kt](https://github.com/typesafegithub/github-workflows-kt/) is a tool for creating -[GitHub Actions workflows](https://docs.github.com/en/actions/using-workflows) in a **type-safe** script, helping you to +[github-workflows-kt](https://github.com/typesafegithub/github-workflows-kt/) is a tool for generating +[GitHub Actions workflow](https://docs.github.com/en/actions/using-workflows) YAML files in a **type-safe** script, helping you to build **robust** workflows for your GitHub projects without mistakes, with **pleasure**, in [Kotlin](https://kotlinlang.org/). @@ -22,7 +22,8 @@ hierarchical data, but it is sometimes used (abused?) to configure complicated s files that are difficult to write and maintain. Who among us hasn't accidentally used the wrong indentation, missed a possibility to extract a reusable piece of code, -or been confused by ambiguous types? The power of a generic-purpose would come in handy in these cases. +or been confused by ambiguous types? The power of a generic-purpose programming language would come in handy in these +cases. We're developing **github-workflows-kt** to solve these and other problems, so you can create GitHub Workflows with confidence. diff --git a/action-binding-generator/api/action-binding-generator.api b/action-binding-generator/api/action-binding-generator.api index 3140406f28..ab354073ab 100644 --- a/action-binding-generator/api/action-binding-generator.api +++ b/action-binding-generator/api/action-binding-generator.api @@ -1,6 +1,3 @@ -public abstract interface annotation class io/github/typesafegithub/workflows/actionbindinggenerator/annotations/ExperimentalClientSideBindings : java/lang/annotation/Annotation { -} - public final class io/github/typesafegithub/workflows/actionbindinggenerator/domain/ActionCoords { public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; diff --git a/action-binding-generator/build.gradle.kts b/action-binding-generator/build.gradle.kts index c0123973be..af7a27463a 100644 --- a/action-binding-generator/build.gradle.kts +++ b/action-binding-generator/build.gradle.kts @@ -13,6 +13,7 @@ version = rootProject.version dependencies { implementation("com.squareup:kotlinpoet:1.18.1") + implementation("it.krzeminski:snakeyaml-engine-kmp:3.0.2") implementation("com.charleskorn.kaml:kaml:0.61.0") implementation(projects.sharedInternal) diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/annotations/ExperimentalClientSideBindings.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/annotations/ExperimentalClientSideBindings.kt deleted file mode 100644 index 2ef13f59a6..0000000000 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/annotations/ExperimentalClientSideBindings.kt +++ /dev/null @@ -1,4 +0,0 @@ -package io.github.typesafegithub.workflows.actionbindinggenerator.annotations - -@RequiresOptIn -public annotation class ExperimentalClientSideBindings 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 c7c452cf0d..e5ccbb47cc 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 @@ -14,6 +14,7 @@ import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.asClassName 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.TypingActualSource @@ -338,39 +339,32 @@ private fun Metadata.buildToYamlArgumentsFunction( private fun Metadata.linkedMapOfInputs( inputTypings: Map, untypedClass: Boolean, -): CodeBlock { - if (inputs.isEmpty()) { - return CodeBlock - .Builder() - .add(CodeBlock.of("return %T($CUSTOM_INPUTS)", LinkedHashMap::class)) - .build() - } else { - return CodeBlock - .Builder() - .apply { - add("return linkedMapOf(\n") - indent() - add("*listOfNotNull(\n") - indent() - inputs.forEach { (key, value) -> - val propertyName = key.toCamelCase() - if (!untypedClass && inputTypings.containsKey(key)) { - val asStringCode = inputTypings.getInputTyping(key).asString() - add("%N?.let { %S·to·it$asStringCode },\n", propertyName, key) - } - val asStringCode = null.getInputTyping(key).asString() - if (value.shouldBeRequiredInBinding() && !value.shouldBeNullable(untypedClass, inputTypings.containsKey(key))) { - add("%S·to·%N$asStringCode,\n", key, "${propertyName}_Untyped") - } else { - add("%N?.let { %S·to·it$asStringCode },\n", "${propertyName}_Untyped", key) - } - } - add("*$CUSTOM_INPUTS.%M().%M(),\n", Types.mapToList, Types.listToArray) - unindent() - add(").toTypedArray()\n") - unindent() - add(")") - }.build() +) = if (inputs.isEmpty()) { + CodeBlock.of("return %T($CUSTOM_INPUTS)", LinkedHashMap::class) +} else { + buildCodeBlock { + add("return linkedMapOf(\n") + indent() + add("*listOfNotNull(\n") + indent() + inputs.forEach { (key, value) -> + val propertyName = key.toCamelCase() + if (!untypedClass && inputTypings.containsKey(key)) { + val asStringCode = inputTypings.getInputTyping(key).asString() + add("%N?.let { %S·to·it$asStringCode },\n", propertyName, key) + } + val asStringCode = null.getInputTyping(key).asString() + if (value.shouldBeRequiredInBinding() && !value.shouldBeNullable(untypedClass, inputTypings.containsKey(key))) { + add("%S·to·%N$asStringCode,\n", key, "${propertyName}_Untyped") + } else { + add("%N?.let { %S·to·it$asStringCode },\n", "${propertyName}_Untyped", key) + } + } + add("*$CUSTOM_INPUTS.%M().%M(),\n", Types.mapToList, Types.listToArray) + unindent() + add(").toTypedArray()\n") + unindent() + add(")") } } @@ -471,15 +465,16 @@ private fun Metadata.buildCommonConstructorParameters( .flatMap { (key, input) -> val typedInput = inputTypings.containsKey(key) val description = input.description.escapedForComments.removeTrailingWhitespacesForEachLine() - val kdocBuilder = CodeBlock.builder() - if (typedInput && !untypedClass && input.shouldBeRequiredInBinding()) { - kdocBuilder.add("%L", "".escapedForComments) - if (description.isNotEmpty()) { - kdocBuilder.add(" ") + val kdoc = + buildCodeBlock { + if (typedInput && !untypedClass && input.shouldBeRequiredInBinding()) { + add("%L", "".escapedForComments) + if (description.isNotEmpty()) { + add(" ") + } + } + add("%L", description) } - } - kdocBuilder.add("%L", description) - val kdoc = kdocBuilder.build() listOfNotNull( untypedClass.takeIf { !it && typedInput }?.let { @@ -545,19 +540,18 @@ private fun TypeSpec.Builder.addInitializerBlockIfNecessary( return this } -private fun Metadata.initializerBlock(inputTypings: Map): CodeBlock { - val codeBlockBuilder = CodeBlock.builder() - var first = true - inputs - .filter { inputTypings.containsKey(it.key) } - .forEach { (key, input) -> - if (!first) { - codeBlockBuilder.add("\n") - } - first = false - val propertyName = key.toCamelCase() - codeBlockBuilder - .add( +private fun Metadata.initializerBlock(inputTypings: Map) = + buildCodeBlock { + var first = true + inputs + .filter { inputTypings.containsKey(it.key) } + .forEach { (key, input) -> + if (!first) { + add("\n") + } + first = false + val propertyName = key.toCamelCase() + add( """ require(!((%1N != null) && (%1L_Untyped != null))) { %2S @@ -567,9 +561,8 @@ private fun Metadata.initializerBlock(inputTypings: Map): CodeBl propertyName, "Only $propertyName or ${propertyName}_Untyped must be set, but not both", ) - if (input.shouldBeRequiredInBinding()) { - codeBlockBuilder - .add( + if (input.shouldBeRequiredInBinding()) { + add( """ require((%1N != null) || (%1L_Untyped != null)) { %2S @@ -579,10 +572,9 @@ private fun Metadata.initializerBlock(inputTypings: Map): CodeBl propertyName, "Either $propertyName or ${propertyName}_Untyped must be set, one of them is required", ) + } } - } - return codeBlockBuilder.build() -} + } private fun actionKdoc( metadata: Metadata, diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt index 3ab2d2ed31..8cb029d86f 100644 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt +++ b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesProviding.kt @@ -12,6 +12,7 @@ import io.github.typesafegithub.workflows.actionbindinggenerator.domain.repoName import io.github.typesafegithub.workflows.actionbindinggenerator.domain.subName import io.github.typesafegithub.workflows.actionbindinggenerator.metadata.fetchUri import io.github.typesafegithub.workflows.actionbindinggenerator.utils.toPascalCase +import it.krzeminski.snakeyaml.engine.kmp.api.Load import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import java.io.IOException @@ -79,10 +80,11 @@ private fun ActionCoords.fetchTypingsForOlderVersionFromCatalog(fetchUri: (URI) } catch (e: IOException) { return null } - val metadata = yaml.decodeFromString(metadataYml) + val metadata = yaml.protectedDecodeFromString(metadataYml) + val requestedVersionAsInt = this.version.versionToIntOrNull() ?: return null val fallbackVersion = metadata.versionsWithTypings - .filter { it.versionToInt() < this.version.versionToInt() } + .filter { it.versionToInt() < requestedVersionAsInt } .maxByOrNull { it.versionToInt() } ?: run { println(" ... no fallback version found!") @@ -146,12 +148,21 @@ private inline fun Yaml.decodeFromStringOrDefaultIfEmpty( default: T, ): T = if (text.isNotBlank()) { - decodeFromString(text) + protectedDecodeFromString(text) } else { default } -private fun String.versionToInt() = lowercase().removePrefix("v").toInt() +private inline fun Yaml.protectedDecodeFromString(text: String): T { + // protect against billion laughs attack until + // https://github.com/charleskorn/kaml/pull/620 is available + Load().loadOne(text) + return decodeFromString(text) +} + +private fun String.versionToInt() = this.versionToIntOrNull() ?: error("Version '$this' cannot be treated as numeric!") + +private fun String.versionToIntOrNull() = lowercase().removePrefix("v").toIntOrNull() private val yaml = Yaml( diff --git a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesYamlReading.kt b/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesYamlReading.kt deleted file mode 100644 index 31d43f150c..0000000000 --- a/action-binding-generator/src/main/kotlin/io/github/typesafegithub/workflows/actionbindinggenerator/typing/TypesYamlReading.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.typesafegithub.workflows.actionbindinggenerator.typing - -private fun ActionType.toTyping(fieldName: String): Typing = - when (this.type) { - ActionTypeEnum.String -> StringTyping - ActionTypeEnum.Boolean -> BooleanTyping - ActionTypeEnum.Integer -> { - if (this.namedValues.isEmpty()) { - IntegerTyping - } else { - IntegerWithSpecialValueTyping(typeName = name, specialValues = namedValues) - } - } - ActionTypeEnum.Float -> FloatTyping - ActionTypeEnum.List -> - ListOfTypings( - delimiter = separator, - typing = listItem?.toTyping(fieldName) ?: error("Lists should have list-item set!"), - ) - ActionTypeEnum.Enum -> EnumTyping(typeName = name, items = allowedValues) - } 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 d60cc1a3d2..c0b352fbc4 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,8 +3,10 @@ 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.TypingActualSource +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe +import it.krzeminski.snakeyaml.engine.kmp.exceptions.YamlEngineException import java.io.IOException import java.net.URI @@ -508,5 +510,63 @@ class TypesProvidingTest : TypingActualSource.TYPING_CATALOG, ) } + + test("billion laughs attack is prevented") { + // Given + val billionLaughsAttack = + """ + a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"] + b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a] + c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b] + d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c] + e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d] + f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e] + g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f] + h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g] + i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h] + """.trimIndent() + val fetchUri: (URI) -> String = { + when (it) { + URI("https://raw.githubusercontent.com/some-owner/some-name/some-hash/action-types.yml") -> billionLaughsAttack + else -> throw IOException() + } + } + val actionCoord = ActionCoords("some-owner", "some-name", "v3") + + // Expect + val exception = + shouldThrow { + actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) + } + exception.message shouldBe "Number of aliases for non-scalar nodes exceeds the specified max=50" + } + } + + test("non-numeric version is provided and metadata in typing catalog exists") { + // Given + val metadata = + """ + "versionsWithTypings": + - "v2" + - "v3" + - "v4" + """.trimIndent() + 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", "v6-beta") + + // When + val types = actionCoord.provideTypes(metadataRevision = CommitHash("some-hash"), fetchUri = fetchUri) + + // Then + types shouldBe Pair(emptyMap(), null) } }) diff --git a/action-updates-checker/build.gradle.kts b/action-updates-checker/build.gradle.kts index ff46fadf25..99cbb71e81 100644 --- a/action-updates-checker/build.gradle.kts +++ b/action-updates-checker/build.gradle.kts @@ -9,7 +9,7 @@ group = rootProject.group version = rootProject.version dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") implementation(projects.githubWorkflowsKt) implementation(projects.sharedInternal) diff --git a/automation/code-generator/build.gradle.kts b/automation/code-generator/build.gradle.kts index 35e8be6cb7..88cea85a03 100644 --- a/automation/code-generator/build.gradle.kts +++ b/automation/code-generator/build.gradle.kts @@ -8,7 +8,7 @@ plugins { dependencies { implementation("com.squareup:kotlinpoet:1.18.1") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.2") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") } application { diff --git a/build.gradle.kts b/build.gradle.kts index 980bdf5013..e77227e2c7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "io.github.typesafegithub" -version = "3.0.0" +version = "3.0.1" nexusPublishing { repositories { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index bcff1f6318..d4bfbf901f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -6,15 +6,15 @@ plugins { } dependencies { - implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.0.20")) + implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.0.21")) - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.20") - implementation("org.jetbrains.kotlin:kotlin-serialization:2.0.20") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21") + implementation("org.jetbrains.kotlin:kotlin-serialization:2.0.21") - implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.6") + implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.7") implementation("org.jmailen.gradle:kotlinter-gradle:4.4.1") - implementation(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1")) + implementation(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0")) implementation(("org.jetbrains.kotlinx:kotlinx-coroutines-core")) } diff --git a/docs/faq.md b/docs/faq.md index de088c31f9..2f64c9cfea 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -63,11 +63,31 @@ scripting. It's lagging behind the support provided by Kotlin itself. Here's a list of tickets on the JetBrains side, along with proposed workarounds: -* [\[KTIJ-14580\] Imported script are not supported for scripts outside of a source root](https://youtrack.jetbrains.com/issue/KTIJ-14580) +* **[\[KTIJ-14580\] Imported script are not supported for scripts outside of a source root](https://youtrack.jetbrains.com/issue/KTIJ-14580)** It's possible to partially mitigate it by adding dependencies from the imported files directly in the top-level script. -* [\[KTIJ-16532\] Scripting: dependencies do not open source files](https://youtrack.jetbrains.com/issue/KTIJ-16532) +* **[\[KTIJ-16532\] Scripting: dependencies do not open source files](https://youtrack.jetbrains.com/issue/KTIJ-16532)** There are several workarounds: browse the code in GitHub, add a dependency on the library in your main project which will let you browse the source code in the IDE, or maybe it's enough to use [the rendered API docs](https://typesafegithub.github.io/github-workflows-kt/api-docs/). + +* **[\[KTIJ-31203\] main.kts script handler uses stale `@Repository` value to resolve dependencies](https://youtrack.jetbrains.com/issue/KTIJ-31203)** + When changing `@Repository` values, close the IntelliJ project and re-open it. + If IntelliJ already resolved the dependency from the old repository, you might need to delete it from the Maven Local + cache repository and after that restart the whole IDE as IntelliJ might not see the recreated file even after + executing the workflow script and the file was recreated in the Maven Local cache repository. (also see next points) + +* **A new release of an action added some input, but I don't see it in the IDE** + If you depend on a major version of an action like `v1` and the action adds backwards compatible changes in a new + release that adds inputs, the generated `v1` bindings are already present in your Maven Local cache repository and + you do not get a new binding generated that includes the new inputs. To work-around this, delete the cache binding + from your Maven Local cache repository, which typically is located at `~/.m2/respository/`. After that, executing or + syncing the workflow script will re-request a fresh binding from the binding server that will include the up-to-date + state of the action. + +* **[\[KTIJ-31214\] Deleting a dependency jar from Maven Local and recreating it leaves `*.main.kts` script with unresolved symbols until IDE is restarted](https://youtrack.jetbrains.com/issue/KTIJ-31214)** + After following the previous step and deleting a dependency from the Maven Local cache repository to get it updated, + IntelliJ will not re-resolve the dependency and even doing so in another way like actually executing the workflow + script, IntelliJ still treats the classes as unresolved symbols. To get to a usable state again, you need to restart + the whole IDE, just closing and re-opening the project is not sufficient. diff --git a/docs/index.md b/docs/index.md index a1b1e25b44..498ef2e4e0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,7 @@ # github-workflows-kt -github-workflows-kt is a tool for creating -[GitHub Actions workflows](https://docs.github.com/en/actions/using-workflows) in a **type-safe** script, helping you to +github-workflows-kt is a tool for generating +[GitHub Actions workflow](https://docs.github.com/en/actions/using-workflows) YAML files in a **type-safe** script, helping you to build **robust** workflows for your GitHub projects without mistakes, with **pleasure**, in [Kotlin](https://kotlinlang.org/). @@ -14,7 +14,8 @@ hierarchical data, but it is sometimes used (abused?) to configure complicated s files that are difficult to write and maintain. Who among us hasn't accidentally used the wrong indentation, missed a possibility to extract a reusable piece of code, -or been confused by ambiguous types? The power of a generic-purpose would come in handy in these cases. +or been confused by ambiguous types? The power of a generic-purpose programming language would come in handy in these +cases. We're developing **github-workflows-kt** to solve these and other problems, so you can create GitHub Workflows with confidence. diff --git a/docs/requirements.txt b/docs/requirements.txt index cf7401d7b9..8680781979 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ mkdocs==1.6.1 -mkdocs-material==9.5.34 +mkdocs-material==9.5.41 mkdocs-material-extensions==1.3.1 mkdocs-video==1.5.0 -pymdown-extensions==10.9 +pymdown-extensions==10.11.2 diff --git a/docs/user-guide/migrating-to-Maven-based-bindings.md b/docs/user-guide/migrating-to-Maven-based-bindings.md index 7c2de7f4ba..8b6826c8ac 100644 --- a/docs/user-guide/migrating-to-Maven-based-bindings.md +++ b/docs/user-guide/migrating-to-Maven-based-bindings.md @@ -6,7 +6,7 @@ Let's say you have the following workflow: ```kotlin #!/usr/bin/env kotlin -@file:DependsOn("io.github.typesafegithub:github-workflows-kt:") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:2.1.0") import io.github.typesafegithub.workflows.actions.actions.CheckoutV4 import io.github.typesafegithub.workflows.actions.actions.SetupJavaV3 diff --git a/github-workflows-kt/build.gradle.kts b/github-workflows-kt/build.gradle.kts index 23fa99ee71..4fceafd6cc 100644 --- a/github-workflows-kt/build.gradle.kts +++ b/github-workflows-kt/build.gradle.kts @@ -21,8 +21,8 @@ version = rootProject.version dependencies { implementation("it.krzeminski:snakeyaml-engine-kmp:3.0.2") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.2") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.2") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") implementation(projects.sharedInternal) testImplementation("dev.zacsweers.kctfork:core:0.5.1") diff --git a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/GettingStartedSnippets.kt b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/GettingStartedSnippets.kt index 70ac9354d2..5db32e22ba 100644 --- a/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/GettingStartedSnippets.kt +++ b/github-workflows-kt/src/test/kotlin/io/github/typesafegithub/workflows/docsnippets/GettingStartedSnippets.kt @@ -19,7 +19,7 @@ class GettingStartedSnippets : #!/usr/bin/env kotlin @file:Repository("https://repo.maven.apache.org/maven2/") - @file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.0") + @file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.1") @file:Repository("https://bindings.krzeminski.it") @file:DependsOn("actions:checkout:v4") diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2c3521197d..a4b76b9530 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2b189974c2..fb602ee2af 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/images/teaser-with-newest-version.svg b/images/teaser-with-newest-version.svg index 84bbf44bb5..02c75c8e27 100644 --- a/images/teaser-with-newest-version.svg +++ b/images/teaser-with-newest-version.svg @@ -1,4 +1,4 @@ -