diff --git a/.editorconfig b/.editorconfig index af6e9e0d27..9ed5bb081a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,10 @@ indent_size = 2 # The file contains important whitespace at the end of the line in a multi-line string. # and editorconfig doesn't seem to respect multi-line strings. trim_trailing_whitespace = false + +[*.{kt,kts}] +indent_size = 4 +ij_kotlin_allow_trailing_comma = false +ij_kotlin_allow_trailing_comma_on_call_site = false +ktlint_code_style = intellij_idea +ktlint_standard_function-signature = disabled diff --git a/.github/actions/setup-jdks/action.yml b/.github/actions/setup-jdks/action.yml index 5afb6795da..eaf6567301 100644 --- a/.github/actions/setup-jdks/action.yml +++ b/.github/actions/setup-jdks/action.yml @@ -21,7 +21,7 @@ runs: uses: actions/setup-java@v4 with: # Temurin JDK 8 for macos on ARM is not available: https://github.com/adoptium/adoptium/issues/96 - distribution: ${{ runner.os == 'macOS' && 'zulu' || 'temurin' }} + distribution: ${{ ((runner.os == 'macOS') && (runner.arch == 'ARM64')) && 'zulu' || 'temurin' }} java-version: 8 - name: Prepare JDK8 env var shell: bash diff --git a/.github/workflows/README.adoc b/.github/workflows/README.adoc new file mode 100644 index 0000000000..379f4b1171 --- /dev/null +++ b/.github/workflows/README.adoc @@ -0,0 +1,98 @@ +== The YAML workflow files vs. the `*.main.kts` files + +The YAML workflow files are generated from the `*.main.kts` files. + +These use the https://github.com/typesafegithub/github-workflows-kt[github-workflows-kt] +Kotlin DSL library to conveniently and type-safely write GitHub Action workflow files. + +As there is no official built-in support in GitHub Actions yet until +https://github.com/orgs/community/discussions/15904 is considered, the YAML files +need to be generated manually. + +There is a safeguard check in all the generated files that this is not forgotten. +Running a workflow where the according `*.main.kts` produces a different output will +fail the execution. Additionally, the workflow that runs for pull requests checks +the consistency of all the YAML files as not all are run for pull requests. + + + +== Ways to generate the YAML workflow files + +There are multiple ways to generate the YAML files and all of them are fine, +but be aware of the last one of the caveats below if you are not using the Gradle method: + +* If you are in a `sh` derivate like e.g. `bash` and Kotlin is installed and + available in the `PATH`, you can just call the `*.main.kts` script like any + other shell script: ++ +[source,bash] +---- +$ ./release.main.kts +---- + +* If Kotlin is installed somewhere you can call it with the `*.main.kts` script + as argument: ++ +[source,bash] +---- +$ path/to/kotlin release.main.kts +---- + +* From the IDE you can create a run configuration that executes the `*.main.kts` script. + +* There is a Gradle task `preprocessWorkflows` that generates all YAML files from the + according `*.main.kts` files. Additionally, there is also one task per workflow to + only generate that one: ++ +[source,bash] +---- +$ ./gradlew preprocessReleaseWorkflow +$ ./gradlew preprocessWorkflows +---- + + + +== Caveats + +There are currently three known caveats with the approach we follow. + +* https://youtrack.jetbrains.com/issue/KTIJ-16532 ++ +If you navigate to a file in the dependencies, only a decompiled file is opened, +even though the source JAR would be available. Also the quick documentation is missing. ++ +This can easily by mitigated by attaching the library to the normal project +dependencies while having the need to navigate the source files or while editing them, +which makes them properly viewable and documentation displayable in the editor. + +* https://youtrack.jetbrains.com/issue/KTIJ-14580 ++ +We use `@file:Import` to reduce code duplication by having common code in a common file. +Unfortunately, this triggers a Kotlin IntelliJ plugin bug where the imported file cannot +be loaded properly and so the things supplied by it like dependencies or common functions +are not available. This makes most of the workflow `*.main.kts` files red as hell in the +IDE currently. ++ +To reduce risk for eye-cancer while reading the `*.main.kts` scripts or to be able to +sanely edit them, temporarily add the `@file:DependsOn` from the imported file to the +importing file and wait a second, then remove the line again once you are done. + +* https://youtrack.jetbrains.com/issue/KT-42101 ++ +We use `@file:Import` to reduce code duplication by having common code in a common file. +Unfortunately, this triggers a Kotlin bug where the compilation cache becomes confused +if the imported file is changed without the importing file being changed too. ++ +If only the imported file is changed, it could happen that an old version is used, +or it could also happen that classes added by a `@file:DependsOn` in the imported file +are not available to the importing file. So if there was a change in the imported file, +you either need to also change the importing file, or to properly execute the script, +you need to delete the stale entry from the compilation cache which can be found at for example +`~/.cache/main.kts.compiled.cache/` on Linux and `%LOCALAPPDATA%\main.kts.compiled.cache\` +on Windows. Alternatively, you can also delete the whole cache directory. ++ +Another option is to disable the compilation cache for the execution by setting the +environment variable `KOTLIN_MAIN_KTS_COMPILED_SCRIPTS_CACHE_DIR` or the system property +`kotlin.main.kts.compiled.scripts.cache.dir` to an empty value, depending on the run +method you chose. The Gradle tasks already do that, so when using the Gradle tasks you +do not have this problem and it just works. diff --git a/.github/workflows/branches-and-prs.main.kts b/.github/workflows/branches-and-prs.main.kts new file mode 100755 index 0000000000..9025113acd --- /dev/null +++ b/.github/workflows/branches-and-prs.main.kts @@ -0,0 +1,117 @@ +#!/usr/bin/env kotlin + +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Import("common.main.kts") +@file:Repository("https://bindings.krzeminski.it/") +@file:DependsOn("actions:checkout:v4") +@file:DependsOn("codecov:codecov-action:v5") + +import io.github.typesafegithub.workflows.actions.actions.Checkout +import io.github.typesafegithub.workflows.actions.actions.Checkout.FetchDepth +import io.github.typesafegithub.workflows.actions.codecov.CodecovAction +import io.github.typesafegithub.workflows.domain.Concurrency +import io.github.typesafegithub.workflows.domain.RunnerType +import io.github.typesafegithub.workflows.domain.RunnerType.UbuntuLatest +import io.github.typesafegithub.workflows.domain.triggers.MergeGroup +import io.github.typesafegithub.workflows.domain.triggers.PullRequest +import io.github.typesafegithub.workflows.domain.triggers.Push +import io.github.typesafegithub.workflows.dsl.expressions.Contexts.github +import io.github.typesafegithub.workflows.dsl.expressions.expr +import io.github.typesafegithub.workflows.dsl.workflow + +workflow( + name = "Verify Branches and PRs", + on = listOf( + Push( + branchesIgnore = listOf( + "master", + "gh-pages" + ) + ), + PullRequest(), + MergeGroup() + ), + sourceFile = __FILE__, + targetFileName = "${__FILE__.name.substringBeforeLast(".main.kts")}.yml", + // https://stackoverflow.com/a/72408109/16358266 + concurrency = Concurrency( + group = "${expr { github.workflow }}-${expr("${github.eventPullRequest.pull_request.number} || ${github.ref}")}", + cancelInProgress = true + ) +) { + job( + id = "check_all_workflow_yaml_consistency", + name = "Check all Workflow YAML Consistency", + runsOn = UbuntuLatest + ) { + uses( + name = "Checkout Repository", + action = Checkout() + ) + run( + name = "Regenerate all Workflow YAMLs", + command = """find .github/workflows -mindepth 1 -maxdepth 1 -name '*.main.kts' -exec {} \;""" + ) + run( + name = "Check for Modifications", + command = """ + git add --intent-to-add . + git diff --exit-code + """.trimIndent() + ) + } + + job( + id = "build-and-verify", + name = "Build and Verify", + runsOn = RunnerType.Custom(expr(Matrix.operatingSystem)), + strategy = Strategy( + matrix = Matrix.full + ) + ) { + uses( + name = "Checkout Repository", + action = Checkout( + // Codecov needs fetch-depth > 1 + fetchDepth = FetchDepth.Value(2) + ) + ) + uses( + name = "Set up JDKs", + action = SetupBuildEnv( + additionalJavaVersion = expr(Matrix.javaVersion) + ) + ) + run( + name = "Build Spock", + command = listOf( + "./gradlew", + "--stacktrace", + "ghActionsBuild", + """"-Dvariant=${expr(Matrix.variant)}"""", + """"-DjavaVersion=${expr(Matrix.javaVersion)}"""" + ).joinToString(" "), + // secrets are not injected for pull requests + env = commonCredentials + ) + uses( + name = "Upload to Codecov.io", + action = CodecovAction() + ) + } +} diff --git a/.github/workflows/branches-and-prs.yml b/.github/workflows/branches-and-prs.yml index ef7bfd8d53..67a09b6c3c 100644 --- a/.github/workflows/branches-and-prs.yml +++ b/.github/workflows/branches-and-prs.yml @@ -1,69 +1,114 @@ -name: 'Verify Branches and PRs' +# This file was generated using Kotlin DSL (.github/workflows/branches-and-prs.main.kts). +# If you want to modify the workflow, please change the Kotlin file and regenerate this YAML file. +# Generated with https://github.com/typesafegithub/github-workflows-kt +name: 'Verify Branches and PRs' on: push: branches-ignore: - - master - - gh-pages - pull_request: - merge_group: - -# https://stackoverflow.com/a/72408109/16358266 + - 'master' + - 'gh-pages' + pull_request: {} + merge_group: {} concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}' cancel-in-progress: true - jobs: + check_yaml_consistency: + name: 'Check YAML consistency' + runs-on: 'ubuntu-latest' + steps: + - id: 'step-0' + name: 'Check out' + uses: 'actions/checkout@v4' + - id: 'step-1' + name: 'Execute script' + run: 'rm ''.github/workflows/branches-and-prs.yml'' && ''.github/workflows/branches-and-prs.main.kts''' + - id: 'step-2' + name: 'Consistency check' + run: 'git diff --exit-code ''.github/workflows/branches-and-prs.yml''' + check_all_workflow_yaml_consistency: + name: 'Check all Workflow YAML Consistency' + runs-on: 'ubuntu-latest' + needs: + - 'check_yaml_consistency' + steps: + - id: 'step-0' + name: 'Checkout Repository' + uses: 'actions/checkout@v4' + - id: 'step-1' + name: 'Regenerate all Workflow YAMLs' + run: 'find .github/workflows -mindepth 1 -maxdepth 1 -name ''*.main.kts'' -exec {} \;' + - id: 'step-2' + name: 'Check for Modifications' + run: |- + git add --intent-to-add . + git diff --exit-code build-and-verify: - runs-on: ${{ matrix.os }} + name: 'Build and Verify' + runs-on: '${{ matrix.os }}' + needs: + - 'check_yaml_consistency' strategy: fail-fast: false matrix: - variant: ['2.5', '3.0', '4.0'] - java: ['8', '11', '17', '21', '23'] - os: ['ubuntu-latest'] + variant: + - '2.5' + - '3.0' + - '4.0' + java: + - '8' + - '11' + - '17' + - '21' + - '23' + os: + - 'ubuntu-latest' exclude: - - variant: '2.5' - java: '17' - os: 'ubuntu-latest' - - variant: '2.5' - java: '21' - os: 'ubuntu-latest' - - variant: '2.5' - java: '23' - os: 'ubuntu-latest' + - variant: '2.5' + java: '17' + os: 'ubuntu-latest' + - variant: '2.5' + java: '21' + os: 'ubuntu-latest' + - variant: '2.5' + java: '23' + os: 'ubuntu-latest' include: - - variant: '2.5' - java: '8' - os: 'windows-latest' - - variant: '3.0' - java: '8' - os: 'windows-latest' - - variant: '4.0' - java: '8' - os: 'windows-latest' - - variant: '2.5' - java: '8' - os: 'macos-latest' - - variant: '3.0' - java: '8' - os: 'macos-latest' - - variant: '4.0' - java: '8' - os: 'macos-latest' + - variant: '2.5' + java: '8' + os: 'windows-latest' + - variant: '3.0' + java: '8' + os: 'windows-latest' + - variant: '4.0' + java: '8' + os: 'windows-latest' + - variant: '2.5' + java: '8' + os: 'macos-latest' + - variant: '3.0' + java: '8' + os: 'macos-latest' + - variant: '4.0' + java: '8' + os: 'macos-latest' steps: - - uses: actions/checkout@v4 - with: - # Codecov needs fetch-depth > 1 - fetch-depth: 2 - - name: 'Set up JDKs' - uses: ./.github/actions/setup-build-env - with: - additional-java-version: ${{ matrix.java }} - - name: 'Build Spock' - # secrets are not injected for pull requests - env: - DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - run: ./gradlew --stacktrace ghActionsBuild "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.java }}" - - name: 'Upload to Codecov.io' - uses: codecov/codecov-action@v5 + - id: 'step-0' + name: 'Checkout Repository' + uses: 'actions/checkout@v4' + with: + fetch-depth: '2' + - id: 'step-1' + name: 'Set up JDKs' + uses: './.github/actions/setup-build-env' + with: + additional-java-version: '${{ matrix.java }}' + - id: 'step-2' + name: 'Build Spock' + env: + DEVELOCITY_ACCESS_KEY: '${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}' + run: './gradlew --stacktrace ghActionsBuild "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.java }}"' + - id: 'step-3' + name: 'Upload to Codecov.io' + uses: 'codecov/codecov-action@v5' diff --git a/.github/workflows/codeql-analysis.main.kts b/.github/workflows/codeql-analysis.main.kts new file mode 100755 index 0000000000..1c9e1e5562 --- /dev/null +++ b/.github/workflows/codeql-analysis.main.kts @@ -0,0 +1,132 @@ +#!/usr/bin/env kotlin + +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Import("common.main.kts") +@file:Repository("https://bindings.krzeminski.it/") +@file:DependsOn("actions:checkout:v4") +@file:DependsOn("github:codeql-action__analyze:v3") +@file:DependsOn("github:codeql-action__init:v3") + +import io.github.typesafegithub.workflows.actions.actions.Checkout +import io.github.typesafegithub.workflows.actions.github.CodeqlActionAnalyze +import io.github.typesafegithub.workflows.actions.github.CodeqlActionInit +import io.github.typesafegithub.workflows.domain.Concurrency +import io.github.typesafegithub.workflows.domain.RunnerType.UbuntuLatest +import io.github.typesafegithub.workflows.domain.triggers.Cron +import io.github.typesafegithub.workflows.domain.triggers.MergeGroup +import io.github.typesafegithub.workflows.domain.triggers.PullRequest +import io.github.typesafegithub.workflows.domain.triggers.Push +import io.github.typesafegithub.workflows.domain.triggers.Schedule +import io.github.typesafegithub.workflows.dsl.expressions.Contexts.github +import io.github.typesafegithub.workflows.dsl.expressions.expr +import io.github.typesafegithub.workflows.dsl.workflow + +workflow( + name = "Code scanning - action", + on = listOf( + Push( + branches = listOf("!dependabot/**") + ), + PullRequest(), + MergeGroup(), + Schedule( + listOf( + Cron( + minute = "0", + hour = "15", + dayWeek = "TUE" + ) + ) + ) + ), + sourceFile = __FILE__, + targetFileName = "${__FILE__.name.substringBeforeLast(".main.kts")}.yml", + // https://stackoverflow.com/a/72408109/16358266 + concurrency = Concurrency( + group = "${expr { github.workflow }}-${expr("${github.eventPullRequest.pull_request.number} || ${github.ref}")}", + cancelInProgress = true + ) +) { + job( + id = "codeql-build", + name = "CodeQL-Build", + // CodeQL runs on UbuntuLatest, WindowsLatest, and MacOSLatest + runsOn = UbuntuLatest, + strategy = Strategy( + matrix = Matrix( + variants = Matrix.axes.variants + ) + ) + ) { + uses( + name = "Checkout Repository", + action = Checkout() + ) + // Manually added: Install and setup JDK + uses( + name = "Set up JDKs", + action = SetupBuildEnv() + ) + // Initializes the CodeQL tools for scanning + uses( + name = "Initialize CodeQL", + action = CodeqlActionInit( + // Override language selection by uncommenting this and choosing your languages + // languages = listOf("go", "javascript", "csharp", "python", "cpp", "java"), + ) + ) + // Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + // If this step fails, then you should remove it and run the build manually (see below). + // uses( + // name = "Autobuild", + // action = CodeqlActionAutobuild() + // ) + // + // ℹī¸ Command-line programs to run using the OS shell. + // 📚 https://git.io/JvXDl + // + // ✏ī¸ If the Autobuild fails above, remove it and uncomment the following + // three lines and modify them (or add more) to build your code if your + // project uses a compiled language + // + // run( + // command = """ + // make bootstrap + // make release + // """.trimIndent() + // ) + + // Manually added: build + // we have to disable build cache for now as it seems to be necessary for the compiler to run during the build + // https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/troubleshooting-the-codeql-workflow#no-code-found-during-the-build + run( + name = "Build Spock Classes", + command = listOf( + "./gradlew", + "--stacktrace", + "--no-build-cache", + "testClasses", + """"-Dvariant=${expr(Matrix.variant)}"""" + ).joinToString(" ") + ) + uses( + name = "Perform CodeQL Analysis", + action = CodeqlActionAnalyze() + ) + } +} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a9d632bb86..755b3faf1b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,62 +1,58 @@ -name: "Code scanning - action" +# This file was generated using Kotlin DSL (.github/workflows/codeql-analysis.main.kts). +# If you want to modify the workflow, please change the Kotlin file and regenerate this YAML file. +# Generated with https://github.com/typesafegithub/github-workflows-kt +name: 'Code scanning - action' on: push: branches: - - '!dependabot/**' - pull_request: - merge_group: + - '!dependabot/**' + pull_request: {} + merge_group: {} schedule: - - cron: '0 15 * * 2' - -# https://stackoverflow.com/a/72408109/16358266 + - cron: '0 15 * * TUE' concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}' cancel-in-progress: true - jobs: - CodeQL-Build: - # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest - runs-on: ubuntu-latest + check_yaml_consistency: + name: 'Check YAML consistency' + runs-on: 'ubuntu-latest' + steps: + - id: 'step-0' + name: 'Check out' + uses: 'actions/checkout@v4' + - id: 'step-1' + name: 'Execute script' + run: 'rm ''.github/workflows/codeql-analysis.yml'' && ''.github/workflows/codeql-analysis.main.kts''' + - id: 'step-2' + name: 'Consistency check' + run: 'git diff --exit-code ''.github/workflows/codeql-analysis.yml''' + codeql-build: + name: 'CodeQL-Build' + runs-on: 'ubuntu-latest' + needs: + - 'check_yaml_consistency' strategy: fail-fast: false matrix: - variant: [ '2.5', '3.0', '4.0' ] + variant: + - '2.5' + - '3.0' + - '4.0' steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Manually added: Install and setup JDK - - name: 'Set up JDKs' - uses: ./.github/actions/setup-build-env - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - # Override language selection by uncommenting this and choosing your languages - # with: - # languages: go, javascript, csharp, python, cpp, java - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below). - #- name: Autobuild - # uses: github/codeql-action/autobuild@v1 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following - # three lines and modify them (or add more) to build your code if your - # project uses a compiled language - - #- run: | - # make bootstrap - # make release - - # Manually added: build - # we have to disable build cache for now as it seems to be necessary for the compiler to run during the build - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/troubleshooting-the-codeql-workflow#no-code-found-during-the-build - - name: 'Build Spock Classes' - run: ./gradlew --stacktrace --no-build-cache testClasses "-Dvariant=${{ matrix.variant }}" - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + - id: 'step-0' + name: 'Checkout Repository' + uses: 'actions/checkout@v4' + - id: 'step-1' + name: 'Set up JDKs' + uses: './.github/actions/setup-build-env' + - id: 'step-2' + name: 'Initialize CodeQL' + uses: 'github/codeql-action/init@v3' + - id: 'step-3' + name: 'Build Spock Classes' + run: './gradlew --stacktrace --no-build-cache testClasses "-Dvariant=${{ matrix.variant }}"' + - id: 'step-4' + name: 'Perform CodeQL Analysis' + uses: 'github/codeql-action/analyze@v3' diff --git a/.github/workflows/common.main.kts b/.github/workflows/common.main.kts new file mode 100755 index 0000000000..894d471bbb --- /dev/null +++ b/.github/workflows/common.main.kts @@ -0,0 +1,172 @@ +#!/usr/bin/env kotlin + +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Repository("https://repo.maven.apache.org/maven2/") +@file:DependsOn("io.github.typesafegithub:github-workflows-kt:3.0.1") + +import io.github.typesafegithub.workflows.domain.Job +import io.github.typesafegithub.workflows.domain.JobOutputs.EMPTY +import io.github.typesafegithub.workflows.domain.RunnerType +import io.github.typesafegithub.workflows.domain.actions.Action.Outputs +import io.github.typesafegithub.workflows.domain.actions.LocalAction +import io.github.typesafegithub.workflows.dsl.JobBuilder +import io.github.typesafegithub.workflows.dsl.WorkflowBuilder +import io.github.typesafegithub.workflows.dsl.expressions.Contexts.secrets +import io.github.typesafegithub.workflows.dsl.expressions.expr +import java.util.Properties + +val GRADLE_ENTERPRISE_ACCESS_KEY by secrets + +val commonCredentials = mapOf( + "DEVELOCITY_ACCESS_KEY" to expr(GRADLE_ENTERPRISE_ACCESS_KEY) +) + +data class Strategy( + val failFast: Boolean? = false, + val matrix: Matrix? = null +) { + fun toCustomArguments() = mapOf( + *listOfNotNull( + failFast?.let { "fail-fast" to failFast }, + matrix?.let { "matrix" to matrix.toCustomArguments() } + ).toTypedArray() + ) +} + +data class Matrix( + val operatingSystems: List? = null, + val variants: List? = null, + val javaVersions: List? = null, + val exclude: (Element.() -> Boolean)? = null, + val includes: List? = null +) { + private val originalElements by lazy { + (operatingSystems ?: listOf(null)) + .map { Element(operatingSystem = it) } + .flatMap { element -> (variants ?: listOf(null)).map { element.copy(variant = it) } } + .flatMap { element -> (javaVersions ?: listOf(null)).map { element.copy(javaVersion = it) } } + } + + fun toCustomArguments() = mapOf( + *listOfNotNull( + variants?.let { "variant" to variants }, + javaVersions?.let { "java" to javaVersions }, + operatingSystems?.let { "os" to operatingSystems }, + exclude?.let { + "exclude" to originalElements + .filter(exclude) + .map { it.toCustomArguments() } + }, + includes?.let { "include" to includes.map { it.toCustomArguments() } } + ).toTypedArray() + ) + + data class Axes( + val javaVersions: List, + val variants: List + ) + + data class Element( + val operatingSystem: String? = null, + val variant: String? = null, + val javaVersion: String? = null + ) { + fun toCustomArguments() = mapOf( + *listOfNotNull( + variant?.let { "variant" to variant }, + javaVersion?.let { "java" to javaVersion }, + operatingSystem?.let { "os" to operatingSystem } + ).toTypedArray() + ) + } + + companion object { + val operatingSystem = "matrix.os" + val variant = "matrix.variant" + val javaVersion = "matrix.java" + } +} + +fun WorkflowBuilder.job( + id: String, + name: String? = null, + runsOn: RunnerType, + needs: List> = emptyList(), + condition: String? = null, + strategy: Strategy? = null, + simpleStrategy: Map>? = null, + block: JobBuilder.() -> Unit +): Job = job( + id = id, + name = name, + runsOn = runsOn, + needs = needs, + condition = condition, + strategyMatrix = simpleStrategy, + _customArguments = mapOf( + *listOfNotNull( + strategy?.let { "strategy" to strategy.toCustomArguments() } + ).toTypedArray() + ), + block = block +) + +val Matrix.Companion.full + get() = Matrix( + operatingSystems = listOf("ubuntu-latest"), + variants = axes.variants, + javaVersions = axes.javaVersions + "23", + exclude = { (variant == "2.5") && (javaVersion!!.toInt() >= 17) }, + includes = listOf("windows-latest", "macos-latest") + .map { + Matrix.Element( + operatingSystem = it, + javaVersion = axes.javaVersions.first() + ) + } + .flatMap { element -> axes.variants.map { element.copy(variant = it) } } + ) + +val Matrix.Companion.axes by lazy { + Properties().let { properties -> + __FILE__ + .parentFile + .resolve("../../gradle.properties") + .inputStream() + .use { properties.load(it) } + + Matrix.Axes( + properties.getList("javaVersionsList"), + properties.getList("variantsList") + ) + } +} + +fun Properties.getList(key: String) = + getProperty(key).trim().split("""\s*+,\s*+""".toRegex()) + +data class SetupBuildEnv( + val additionalJavaVersion: String? = null +) : LocalAction("./.github/actions/setup-build-env") { + override fun toYamlArguments() = + additionalJavaVersion + ?.let { linkedMapOf("additional-java-version" to it) } + ?: linkedMapOf() + + override fun buildOutputObject(stepId: String): Outputs = Outputs(stepId) +} diff --git a/.github/workflows/docs-pr.main.kts b/.github/workflows/docs-pr.main.kts new file mode 100755 index 0000000000..1334f7566f --- /dev/null +++ b/.github/workflows/docs-pr.main.kts @@ -0,0 +1,99 @@ +#!/usr/bin/env kotlin + +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Import("common.main.kts") +@file:Repository("https://bindings.krzeminski.it/") +@file:DependsOn("actions:checkout:v4") +@file:DependsOn("actions:upload-artifact:v4") + +import io.github.typesafegithub.workflows.actions.actions.Checkout +import io.github.typesafegithub.workflows.actions.actions.Checkout.FetchDepth +import io.github.typesafegithub.workflows.actions.actions.UploadArtifact +import io.github.typesafegithub.workflows.domain.Concurrency +import io.github.typesafegithub.workflows.domain.RunnerType.UbuntuLatest +import io.github.typesafegithub.workflows.domain.triggers.MergeGroup +import io.github.typesafegithub.workflows.domain.triggers.PullRequest +import io.github.typesafegithub.workflows.domain.triggers.Push +import io.github.typesafegithub.workflows.dsl.expressions.Contexts.github +import io.github.typesafegithub.workflows.dsl.expressions.expr +import io.github.typesafegithub.workflows.dsl.workflow + +workflow( + name = "Verify Docs", + on = listOf( + Push( + branchesIgnore = listOf( + "master", + "gh-pages" + ) + ), + PullRequest(), + MergeGroup() + ), + sourceFile = __FILE__, + targetFileName = "${__FILE__.name.substringBeforeLast(".main.kts")}.yml", + // https://stackoverflow.com/a/72408109/16358266 + concurrency = Concurrency( + group = "${expr { github.workflow }}-${expr("${github.eventPullRequest.pull_request.number} || ${github.ref}")}", + cancelInProgress = true + ) +) { + job( + id = "docs-and-javadoc", + name = "Docs and JavaDoc", + runsOn = UbuntuLatest, + ) { + uses( + name = "Checkout Repository", + action = Checkout( + fetchDepth = FetchDepth.Value(1) + ) + ) + uses( + name = "Set up JDKs", + action = SetupBuildEnv( + additionalJavaVersion = Matrix.axes.javaVersions.last() + ) + ) + run( + name = "Install GraphViz", + command = "sudo apt update && sudo apt install --yes graphviz" + ) + run( + name = "Build Docs", + command = listOf( + "./gradlew", + "--stacktrace", + "asciidoctor", + "javadoc", + """"-Dvariant=${Matrix.axes.variants.last()}"""", + """"-DjavaVersion=${Matrix.axes.javaVersions.last()}"""" + ).joinToString(" ") + ) + uses( + name = "Archive and upload docs", + action = UploadArtifact( + name = "docs", + path = listOf( + "build/docs/**", + "build/javadoc/**" + ) + ) + ) + } +} diff --git a/.github/workflows/docs-pr.yml b/.github/workflows/docs-pr.yml index 518b745d9d..dc5e4cda86 100644 --- a/.github/workflows/docs-pr.yml +++ b/.github/workflows/docs-pr.yml @@ -1,38 +1,59 @@ -name: 'Verify Docs' +# This file was generated using Kotlin DSL (.github/workflows/docs-pr.main.kts). +# If you want to modify the workflow, please change the Kotlin file and regenerate this YAML file. +# Generated with https://github.com/typesafegithub/github-workflows-kt +name: 'Verify Docs' on: push: branches-ignore: - - master - - gh-pages - pull_request: - merge_group: - -# https://stackoverflow.com/a/72408109/16358266 + - 'master' + - 'gh-pages' + pull_request: {} + merge_group: {} concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}' cancel-in-progress: true - jobs: + check_yaml_consistency: + name: 'Check YAML consistency' + runs-on: 'ubuntu-latest' + steps: + - id: 'step-0' + name: 'Check out' + uses: 'actions/checkout@v4' + - id: 'step-1' + name: 'Execute script' + run: 'rm ''.github/workflows/docs-pr.yml'' && ''.github/workflows/docs-pr.main.kts''' + - id: 'step-2' + name: 'Consistency check' + run: 'git diff --exit-code ''.github/workflows/docs-pr.yml''' docs-and-javadoc: + name: 'Docs and JavaDoc' runs-on: 'ubuntu-latest' + needs: + - 'check_yaml_consistency' steps: - - uses: actions/checkout@v4 - with: - # Codecov needs fetch-depth > 1 - fetch-depth: 2 - - name: 'Set up JDKs' - uses: ./.github/actions/setup-build-env - with: - additional-java-version: 21 - - name: 'Install GraphViz' - run: sudo apt update && sudo apt install --yes graphviz - - name: 'Build Docs' - run: ./gradlew --stacktrace asciidoctor javadoc "-Dvariant=4.0" "-DjavaVersion=21" - - name: 'Archive and upload docs' - uses: actions/upload-artifact@v4 - with: - name: docs - path: | - build/docs/** - build/javadoc/** + - id: 'step-0' + name: 'Checkout Repository' + uses: 'actions/checkout@v4' + with: + fetch-depth: '1' + - id: 'step-1' + name: 'Set up JDKs' + uses: './.github/actions/setup-build-env' + with: + additional-java-version: '21' + - id: 'step-2' + name: 'Install GraphViz' + run: 'sudo apt update && sudo apt install --yes graphviz' + - id: 'step-3' + name: 'Build Docs' + run: './gradlew --stacktrace asciidoctor javadoc "-Dvariant=4.0" "-DjavaVersion=21"' + - id: 'step-4' + name: 'Archive and upload docs' + uses: 'actions/upload-artifact@v4' + with: + name: 'docs' + path: |- + build/docs/** + build/javadoc/** diff --git a/.github/workflows/release.main.kts b/.github/workflows/release.main.kts new file mode 100755 index 0000000000..d7119dd767 --- /dev/null +++ b/.github/workflows/release.main.kts @@ -0,0 +1,182 @@ +#!/usr/bin/env kotlin + +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Import("common.main.kts") +@file:Repository("https://bindings.krzeminski.it/") +@file:DependsOn("actions:checkout:v4") +@file:DependsOn("codecov:codecov-action:v5") + +import io.github.typesafegithub.workflows.actions.actions.Checkout +import io.github.typesafegithub.workflows.actions.actions.Checkout.FetchDepth +import io.github.typesafegithub.workflows.actions.codecov.CodecovAction +import io.github.typesafegithub.workflows.domain.RunnerType +import io.github.typesafegithub.workflows.domain.triggers.Push +import io.github.typesafegithub.workflows.dsl.expressions.Contexts.github +import io.github.typesafegithub.workflows.dsl.expressions.Contexts.secrets +import io.github.typesafegithub.workflows.dsl.expressions.expr +import io.github.typesafegithub.workflows.dsl.workflow + +workflow( + name = "Build and Release Spock", + on = listOf( + Push( + branches = listOf("master"), + tags = listOf("spock-*") + ) + ), + sourceFile = __FILE__, + targetFileName = "${__FILE__.name.substringBeforeLast(".main.kts")}.yml" +) { + val GITHUB_TOKEN by secrets + val SONATYPE_OSS_USER by secrets + val SONATYPE_OSS_PASSWORD by secrets + val SIGNING_GPG_PASSWORD by secrets + + val buildAndVerify = job( + id = "build-and-verify", + name = "Build and Verify", + runsOn = RunnerType.Custom(expr(Matrix.operatingSystem)), + condition = "${github.repository} == 'spockframework/spock'", + strategy = Strategy( + matrix = Matrix.full + ) + ) { + uses( + name = "Checkout Repository", + action = Checkout( + // Codecov needs fetch-depth > 1 + fetchDepth = FetchDepth.Value(2) + ) + ) + uses( + name = "Set up JDKs", + action = SetupBuildEnv( + additionalJavaVersion = expr(Matrix.javaVersion) + ) + ) + run( + name = "Build Spock", + command = listOf( + "./gradlew", + "--stacktrace", + "ghActionsBuild", + """"-Dvariant=${expr(Matrix.variant)}"""", + """"-DjavaVersion=${expr(Matrix.javaVersion)}"""", + "-Dscan.tag.main-build" + ).joinToString(" "), + env = commonCredentials + ) + run( + name = "Stop Daemon", + command = "./gradlew --stop" + ) + uses( + name = "Upload to Codecov.io", + action = CodecovAction() + ) + } + val releaseSpock = job( + id = "release-spock", + name = "Release Spock", + runsOn = RunnerType.Custom(expr(Matrix.operatingSystem)), + needs = listOf(buildAndVerify), + strategyMatrix = mapOf( + // publish needs to be done for all versions + "variant" to Matrix.axes.variants, + // publish needs the min supported java version + "java" to Matrix.axes.javaVersions.take(1), + "os" to listOf("ubuntu-latest") + ) + ) { + uses( + name = "Checkout Repository", + action = Checkout() + ) + uses( + name = "Set up JDKs", + action = SetupBuildEnv( + additionalJavaVersion = expr(Matrix.javaVersion) + ) + ) + run( + name = "Publish Spock", + command = listOf( + "./gradlew", + "--no-parallel", + "--stacktrace", + "ghActionsPublish", + """"-Dvariant=${expr(Matrix.variant)}"""", + """"-DjavaVersion=${expr(Matrix.javaVersion)}"""", + "-Dscan.tag.main-publish" + ).joinToString(" "), + env = mutableMapOf( + "GITHUB_TOKEN" to expr(GITHUB_TOKEN), + "SONATYPE_OSS_USER" to expr(SONATYPE_OSS_USER), + "SONATYPE_OSS_PASSWORD" to expr(SONATYPE_OSS_PASSWORD), + "SIGNING_PASSWORD" to expr(SIGNING_GPG_PASSWORD) + ).apply { putAll(commonCredentials) } + ) + } + job( + id = "publish-release-docs", + name = "Publish Release Docs", + runsOn = RunnerType.Custom(expr(Matrix.operatingSystem)), + needs = listOf(releaseSpock), + strategyMatrix = mapOf( + // docs need the highest variant + "variant" to Matrix.axes.variants.takeLast(1), + // docs need the highest java version + "java" to Matrix.axes.javaVersions.takeLast(1), + "os" to listOf("ubuntu-latest") + ) + ) { + uses( + name = "Checkout Repository", + action = Checkout() + ) + uses( + name = "Set up JDKs", + action = SetupBuildEnv( + additionalJavaVersion = expr(Matrix.javaVersion) + ) + ) + run( + name = "Create Temporary Branch", + command = "git checkout -b \"docs-\$GITHUB_SHA\"" + ) + run( + name = "Install GraphViz", + command = "sudo apt update && sudo apt install --yes graphviz" + ) + run( + name = "Publish Docs", + command = listOf( + "./gradlew", + "--no-parallel", + "--stacktrace", + "ghActionsDocs", + """"-Dvariant=${expr(Matrix.variant)}"""", + """"-DjavaVersion=${expr(Matrix.javaVersion)}"""", + "-Dscan.tag.main-docs" + ).joinToString(" "), + env = mutableMapOf( + "GITHUB_TOKEN" to expr(GITHUB_TOKEN) + ).apply { putAll(commonCredentials) } + ) + } +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 07114e9e57..d1ad9b36c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,114 +1,166 @@ -name: 'Build and Release Spock' +# This file was generated using Kotlin DSL (.github/workflows/release.main.kts). +# If you want to modify the workflow, please change the Kotlin file and regenerate this YAML file. +# Generated with https://github.com/typesafegithub/github-workflows-kt +name: 'Build and Release Spock' on: push: branches: - - master + - 'master' tags: - - 'spock-*' - + - 'spock-*' jobs: + check_yaml_consistency: + name: 'Check YAML consistency' + runs-on: 'ubuntu-latest' + steps: + - id: 'step-0' + name: 'Check out' + uses: 'actions/checkout@v4' + - id: 'step-1' + name: 'Execute script' + run: 'rm ''.github/workflows/release.yml'' && ''.github/workflows/release.main.kts''' + - id: 'step-2' + name: 'Consistency check' + run: 'git diff --exit-code ''.github/workflows/release.yml''' build-and-verify: - runs-on: ${{ matrix.os }} - if: github.repository == 'spockframework/spock' + name: 'Build and Verify' + runs-on: '${{ matrix.os }}' + needs: + - 'check_yaml_consistency' + if: 'github.repository == ''spockframework/spock''' strategy: fail-fast: false matrix: - variant: ['2.5', '3.0', '4.0'] - java: [ '8', '11', '17', '21', '23' ] - os: [ 'ubuntu-latest' ] + variant: + - '2.5' + - '3.0' + - '4.0' + java: + - '8' + - '11' + - '17' + - '21' + - '23' + os: + - 'ubuntu-latest' exclude: - - variant: '2.5' - java: '17' - os: 'ubuntu-latest' - - variant: '2.5' - java: '21' - os: 'ubuntu-latest' - - variant: '2.5' - java: '23' - os: 'ubuntu-latest' + - variant: '2.5' + java: '17' + os: 'ubuntu-latest' + - variant: '2.5' + java: '21' + os: 'ubuntu-latest' + - variant: '2.5' + java: '23' + os: 'ubuntu-latest' include: - - variant: '2.5' - java: '8' - os: 'windows-latest' - - variant: '3.0' - java: '8' - os: 'windows-latest' - - variant: '4.0' - java: '8' - os: 'windows-latest' - - variant: '2.5' - java: '8' - os: 'macos-latest' - - variant: '3.0' - java: '8' - os: 'macos-latest' - - variant: '4.0' - java: '8' - os: 'macos-latest' + - variant: '2.5' + java: '8' + os: 'windows-latest' + - variant: '3.0' + java: '8' + os: 'windows-latest' + - variant: '4.0' + java: '8' + os: 'windows-latest' + - variant: '2.5' + java: '8' + os: 'macos-latest' + - variant: '3.0' + java: '8' + os: 'macos-latest' + - variant: '4.0' + java: '8' + os: 'macos-latest' steps: - - uses: actions/checkout@v4 - with: - # Codecov needs fetch-depth > 1 - fetch-depth: 2 - - name: 'Set up JDKs' - uses: ./.github/actions/setup-build-env - with: - additional-java-version: ${{ matrix.java }} - - name: 'Build Spock' - env: - DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - run: ./gradlew --stacktrace ghActionsBuild "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.java }}" "-Dscan.tag.main-build" - - name: 'Stop Daemon' - run: | - ./gradlew --stop - - name: 'Upload to Codecov.io' - uses: codecov/codecov-action@v5 - + - id: 'step-0' + name: 'Checkout Repository' + uses: 'actions/checkout@v4' + with: + fetch-depth: '2' + - id: 'step-1' + name: 'Set up JDKs' + uses: './.github/actions/setup-build-env' + with: + additional-java-version: '${{ matrix.java }}' + - id: 'step-2' + name: 'Build Spock' + env: + DEVELOCITY_ACCESS_KEY: '${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}' + run: './gradlew --stacktrace ghActionsBuild "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.java }}" -Dscan.tag.main-build' + - id: 'step-3' + name: 'Stop Daemon' + run: './gradlew --stop' + - id: 'step-4' + name: 'Upload to Codecov.io' + uses: 'codecov/codecov-action@v5' release-spock: - runs-on: ${{ matrix.os }} - needs: [ 'build-and-verify' ] + name: 'Release Spock' + runs-on: '${{ matrix.os }}' + needs: + - 'build-and-verify' + - 'check_yaml_consistency' strategy: matrix: - os: [ 'ubuntu-latest' ] - variant: [ '2.5', '3.0', '4.0' ] # publish needs to be done for all versions - java: [ '8' ] # publish needs the min supported java version + variant: + - '2.5' + - '3.0' + - '4.0' + java: + - '8' + os: + - 'ubuntu-latest' steps: - - uses: actions/checkout@v4 - - name: 'Set up JDKs' - uses: ./.github/actions/setup-build-env - with: - additional-java-version: ${{ matrix.java }} - - name: 'Publish Spock' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONATYPE_OSS_USER: ${{ secrets.SONATYPE_OSS_USER }} - SONATYPE_OSS_PASSWORD: ${{ secrets.SONATYPE_OSS_PASSWORD }} - SIGNING_PASSWORD: ${{ secrets.SIGNING_GPG_PASSWORD }} - DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - run: ./gradlew --no-parallel --stacktrace ghActionsPublish "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.java }}" "-Dscan.tag.main-publish" - + - id: 'step-0' + name: 'Checkout Repository' + uses: 'actions/checkout@v4' + - id: 'step-1' + name: 'Set up JDKs' + uses: './.github/actions/setup-build-env' + with: + additional-java-version: '${{ matrix.java }}' + - id: 'step-2' + name: 'Publish Spock' + env: + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + SONATYPE_OSS_USER: '${{ secrets.SONATYPE_OSS_USER }}' + SONATYPE_OSS_PASSWORD: '${{ secrets.SONATYPE_OSS_PASSWORD }}' + SIGNING_PASSWORD: '${{ secrets.SIGNING_GPG_PASSWORD }}' + DEVELOCITY_ACCESS_KEY: '${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}' + run: './gradlew --no-parallel --stacktrace ghActionsPublish "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.java }}" -Dscan.tag.main-publish' publish-release-docs: - runs-on: ${{ matrix.os }} - needs: ['release-spock'] + name: 'Publish Release Docs' + runs-on: '${{ matrix.os }}' + needs: + - 'release-spock' + - 'check_yaml_consistency' strategy: matrix: - os: ['ubuntu-latest'] - variant: ['4.0'] # docs need the highest variant - java: ['21'] # docs need the highest java version + variant: + - '4.0' + java: + - '21' + os: + - 'ubuntu-latest' steps: - - uses: actions/checkout@v4 - - name: 'Set up JDKs' - uses: ./.github/actions/setup-build-env - with: - additional-java-version: ${{ matrix.java }} - - name: 'Create Temporary Branch' - run: | - git checkout -b "docs-$GITHUB_SHA" - - name: Install GraphViz - run: sudo apt update && sudo apt install --yes graphviz - - name: 'Publish Docs' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - run: ./gradlew --no-parallel --stacktrace ghActionsDocs "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.java }}" "-Dscan.tag.main-docs" + - id: 'step-0' + name: 'Checkout Repository' + uses: 'actions/checkout@v4' + - id: 'step-1' + name: 'Set up JDKs' + uses: './.github/actions/setup-build-env' + with: + additional-java-version: '${{ matrix.java }}' + - id: 'step-2' + name: 'Create Temporary Branch' + run: 'git checkout -b "docs-$GITHUB_SHA"' + - id: 'step-3' + name: 'Install GraphViz' + run: 'sudo apt update && sudo apt install --yes graphviz' + - id: 'step-4' + name: 'Publish Docs' + env: + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + DEVELOCITY_ACCESS_KEY: '${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}' + run: './gradlew --no-parallel --stacktrace ghActionsDocs "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.java }}" -Dscan.tag.main-docs' diff --git a/build-logic/preprocess-workflows/preprocess-workflows.gradle b/build-logic/preprocess-workflows/preprocess-workflows.gradle new file mode 100644 index 0000000000..23aff703d6 --- /dev/null +++ b/build-logic/preprocess-workflows/preprocess-workflows.gradle @@ -0,0 +1,17 @@ +plugins { + id 'groovy-gradle-plugin' + id 'idea' +} + +dependencies { + implementation('org.jetbrains.kotlin:kotlin-compiler-embeddable:1.8.20') +} + +gradlePlugin { + plugins { + preprocessWorkflowsPlugin { + id = 'org.spockframework.preprocess-workflows' + implementationClass = 'org.spockframework.gradle.PreprocessWorkflowsPlugin' + } + } +} diff --git a/build-logic/preprocess-workflows/src/main/groovy/org/spockframework/gradle/PreprocessWorkflowsPlugin.groovy b/build-logic/preprocess-workflows/src/main/groovy/org/spockframework/gradle/PreprocessWorkflowsPlugin.groovy new file mode 100644 index 0000000000..d04c8161fc --- /dev/null +++ b/build-logic/preprocess-workflows/src/main/groovy/org/spockframework/gradle/PreprocessWorkflowsPlugin.groovy @@ -0,0 +1,132 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.spockframework.gradle + +import groovy.transform.CompileStatic +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.ModuleDependency +import org.gradle.api.tasks.JavaExec +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.jvm.toolchain.JavaToolchainService +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer +import org.jetbrains.kotlin.com.intellij.openapi.vfs.local.CoreLocalFileSystem +import org.jetbrains.kotlin.com.intellij.openapi.vfs.local.CoreLocalVirtualFile +import org.jetbrains.kotlin.com.intellij.psi.PsiManager +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry +import org.jetbrains.kotlin.psi.KtStringTemplateExpression + +import static org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY +import static org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles.JVM_CONFIG_FILES + +@CompileStatic +class PreprocessWorkflowsPlugin implements Plugin { + void apply(Project project) { + def kotlinCompilerClasspath = project.configurations.detachedConfiguration( + project.dependencies.create('org.jetbrains.kotlin:kotlin-compiler:1.8.20'), + project.dependencies.create('org.jetbrains.kotlin:kotlin-scripting-compiler:1.8.20') + ) + def kotlinScriptClasspath = project.configurations.detachedConfiguration( + project.dependencies.create('org.jetbrains.kotlin:kotlin-main-kts:1.8.20') { ModuleDependency it -> + it.transitive = false + } + ) + + def preprocessWorkflows = project.tasks.register('preprocessWorkflows') { + it.group = 'github actions' + } + project.file('.github/workflows').eachFileMatch(~/.*\.main\.kts$/) { workflowScript -> + def workflowName = workflowScript.name - ~/\.main\.kts$/ + def pascalCasedWorkflowName = workflowName + .replaceAll(/-\w/) { String it -> it[1].toUpperCase() } + .replaceFirst(/^\w/) { String it -> it[0].toUpperCase() } + def preprocessWorkflow = project.tasks.register("preprocess${pascalCasedWorkflowName}Workflow", JavaExec) { + it.group = 'github actions' + + it.inputs + .file(workflowScript) + .withPropertyName('workflowScript') + it.inputs + .files(getImportedFiles(project.file(workflowScript))) + .withPropertyName("importedFiles") + it.outputs + .file(new File(workflowScript.parent, "${workflowName}.yml")) + .withPropertyName('workflowFile') + + it.javaLauncher.set project.extensions.getByType(JavaToolchainService).launcherFor { + it.languageVersion.set(JavaLanguageVersion.of(17)) + } + it.classpath(kotlinCompilerClasspath) + it.mainClass.set 'org.jetbrains.kotlin.cli.jvm.K2JVMCompiler' + it.args('-no-stdlib', '-no-reflect') + it.args('-classpath', kotlinScriptClasspath.asPath) + it.args('-script', workflowScript.absolutePath) + + // work-around for https://youtrack.jetbrains.com/issue/KT-42101 + it.systemProperty('kotlin.main.kts.compiled.scripts.cache.dir', '') + } + preprocessWorkflows.configure { + it.dependsOn(preprocessWorkflow) + } + } + } + + private List getImportedFiles(File workflowScript) { + if (!workflowScript.file) { + return [] + } + + return PsiManager + .getInstance( + KotlinCoreEnvironment + .createForProduction( + Disposer.newDisposable(), + new CompilerConfiguration().tap { + it.put(MESSAGE_COLLECTOR_KEY, MessageCollector.@Companion.NONE) + }, + JVM_CONFIG_FILES + ) + .project + ) + .findFile( + new CoreLocalVirtualFile( + new CoreLocalFileSystem(), + workflowScript + ) + ) + .with { it as KtFile } + .fileAnnotationList + ?.annotationEntries + ?.findAll { it.shortName?.asString() == "Import" } + *.valueArgumentList + ?.collectMany { it?.arguments ?: [] } + *.argumentExpression + ?.findAll { it instanceof KtStringTemplateExpression } + ?.collect { it as KtStringTemplateExpression } + *.entries + *.first() + ?.findAll { it instanceof KtLiteralStringTemplateEntry } + ?.collect { it as KtLiteralStringTemplateEntry } + ?.collect { new File(workflowScript.parentFile, it.text) } + ?.collectMany { getImportedFiles(it) + it } + ?: [] + } +} diff --git a/build-logic/settings.gradle b/build-logic/settings.gradle index 8303208fd4..ae8ad333c1 100644 --- a/build-logic/settings.gradle +++ b/build-logic/settings.gradle @@ -20,6 +20,7 @@ dependencyResolutionManagement { } include("base") +include("preprocess-workflows") include("asciidoc-extensions") nameBuildScriptsAfterProjectNames(rootProject.children) diff --git a/build.gradle b/build.gradle index dcb4219406..8dffe596d4 100644 --- a/build.gradle +++ b/build.gradle @@ -5,13 +5,14 @@ import static org.spockframework.gradle.AsciiDocLinkVerifier.verifyLinksAndAncho plugins { id "org.spockframework.base" apply false - id "base" + id "java-base" id "org.asciidoctor.jvm.convert" id "jacoco" id "net.nemerosa.versioning" id "io.github.gradle-nexus.publish-plugin" id "com.github.ben-manes.versions" id "io.spring.nohttp" + id "org.spockframework.preprocess-workflows" } description = "Spock Framework" @@ -20,9 +21,9 @@ ext { baseVersion = "2.4" snapshotVersion = true milestone = 0 - javaVersions = [8, 11, 17, 21] // ensure that latest version is actually build on GH actions and added to gradle.properties, otherwise no docs get published + javaVersions = javaVersionsList.trim().split(/\s*+,\s*+/).collect { it as int } javaVersion = (System.getProperty("javaVersion") ?: 8) as int - variants = [2.5, 3.0, 4.0] + variants = variantsList.trim().split(/\s*+,\s*+/).collect { it as BigDecimal } variant = System.getProperty("variant") as BigDecimal ?: variants.first() develocity.buildScan.tag "groovy-$variant" if (variant == 2.5) { diff --git a/gradle.properties b/gradle.properties index 21f1e47c99..d84a801050 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,3 +19,7 @@ org.gradle.java.installations.fromEnv=JDK8,JDK11,JDK17,JDK21,JDK23 org.gradle.parallel=true org.gradle.caching=true + +javaVersionsList=8, 11, 17, 21 +variantsList=2.5, 3.0, 4.0 +kotlin.code.style=official