Skip to content

ci: test against KSP, Gradle and Kotlin version matrices #145

ci: test against KSP, Gradle and Kotlin version matrices

ci: test against KSP, Gradle and Kotlin version matrices #145

Workflow file for this run

name: Test
on:
pull_request:
# Also run on main so the Gradle cache is seeded on the default branch;
# pull_request caches are scoped to their own PR and can't be shared.
push:
branches: [ main ]
# Weekly probe against whatever KSP is newest at the time — see ksp-latest.
schedule:
- cron: '0 6 * * 1'
workflow_dispatch:
permissions:
checks: write
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
# Caches Gradle distribution, dependencies, and build caches.
# Writes only on main (default), so PR runs read without thrashing quota.
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Run detekt
run: ./gradlew detekt --continue
- name: Upload detekt reports
uses: actions/upload-artifact@v7
if: always()
with:
name: detekt-reports
path: '**/build/reports/detekt/'
- name: Run tests
run: ./gradlew :kraft-ksp:jvmTest --continue
# Gradle plugin functional tests: real TestKit builds covering the
# wiring, the kraft { } DSL and raw-KSP-args drift pair, error paths,
# and an end-to-end mapper compile against the in-dev kraft artifacts.
- name: Gradle plugin functional tests
run: ./gradlew :kraft-gradle-plugin:test
# Real two-module KMP build: the consumer's converter only exists in the
# producer's klib, so a green build proves cross-module @KraftConverter
# discovery works under kspCommonMainKotlinMetadata (klib classpath).
- name: KMP cross-module integration test
run: ./gradlew :integration-tests:kmp-consumer:jvmTest :integration-tests:kmp-consumer:compileKotlinJs
# Expected-failure counterpart: two producer klibs publish the same converter
# pair, so the consumer's KSP run MUST fail with the classpath ambiguity error.
# Asserts both the failure and the user-facing message.
- name: KMP cross-module ambiguity error test
run: |
set +e
out=$(./gradlew -PkraftAmbiguityFixture=true :integration-tests:kmp-consumer-ambiguous:kspCommonMainKotlinMetadata --console=plain 2>&1)
status=$?
set -e
if [ "$status" -eq 0 ]; then
echo "Expected the ambiguous-consumer build to fail, but it succeeded."
exit 1
fi
echo "$out" | grep -q "Ambiguous @KraftConverter on the classpath for (kotlin.Int → kotlin.String)" || {
echo "Build failed but without the expected ambiguity message. Output tail:"
echo "$out" | tail -40
exit 1
}
- name: Publish test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results
path: |
kraft-ksp/build/reports/tests/jvmTest/
kraft-gradle-plugin/build/reports/tests/test/
- name: Publish JUnit report
uses: mikepenz/action-junit-report@v6
if: always()
with:
report_paths: |
kraft-ksp/build/test-results/jvmTest/**/*.xml
kraft-gradle-plugin/build/test-results/test/**/*.xml
# CI-10: KSP-version compatibility. The Gradle plugin functional tests generate
# real consumer projects that apply the KSP plugin, so overriding
# kraft.test.kspVersion exercises Kraft against a KSP other than the catalog's.
#
# Pinned legs: the floor Kraft is built on, plus the newest KSP known-good at
# the time of writing. Deterministic, so a PR that breaks either one fails here
# rather than in a consumer's build.
ksp-compat:
name: KSP ${{ matrix.ksp }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ksp:
- '2.3.3' # floor - the version in gradle/libs.versions.toml
- '2.3.9' # newest known-good at 2026-07-21
steps:
- uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Gradle plugin functional tests (KSP ${{ matrix.ksp }})
run: ./gradlew :kraft-gradle-plugin:test -Pkraft.test.kspVersion=${{ matrix.ksp }}
- name: Publish test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-ksp-${{ matrix.ksp }}
path: kraft-gradle-plugin/build/reports/tests/test/
# CI-10: Gradle-version compatibility. TestKit runs the generated consumer
# builds on the Gradle running the tests unless kraft.test.gradleVersion says
# otherwise, so without this every run only ever proved Gradle 8.13 works.
#
# 9.6.x drops the Android leg: AGP 8.x (both 8.11.2 and the latest 8.13.2)
# calls org.gradle.api.problems.internal.InternalProblems, removed in Gradle
# 9.6.0, so the Android plugin cannot load. AGP 9.x fixes that, but it ships
# built-in Kotlin and Kraft does not detect that setup yet (F-17). 9.5.1 is
# the last Gradle before the removal, so it still covers Android on AGP 8.x.
gradle-compat:
name: Gradle ${{ matrix.gradle }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- gradle: '8.13' # floor - the wrapper version
excludeTags: ''
- gradle: '9.5.1' # last Gradle 9 that can still load AGP 8.x
excludeTags: ''
- gradle: '9.6.1' # newest; Android excluded (see above)
excludeTags: 'android'
steps:
- uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Gradle plugin functional tests (Gradle ${{ matrix.gradle }})
run: >-
./gradlew :kraft-gradle-plugin:test
-Pkraft.test.gradleVersion=${{ matrix.gradle }}
-PkraftExcludeTags=${{ matrix.excludeTags }}
- name: Publish test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-gradle-${{ matrix.gradle }}
path: kraft-gradle-plugin/build/reports/tests/test/
# CI-10: the newest stack Kraft is known to work on, two Kotlin minors above
# its own 2.2.21 floor. This is the job that backs the claim that a Kotlin or
# KSP upgrade does not strand consumers -- it is tested, not assumed.
#
# Android is excluded here for two separate reasons, neither of them a bug:
# 1. AGP 8.x cannot load on Gradle >= 9.6.0 (removed Problems internals).
# 2. Moving to AGP 9.x is not a drop-in for this fixture: AGP 9 ships
# built-in Kotlin, so applying org.jetbrains.kotlin.android alongside it
# is an error. The fixture (and Kraft's own plugin detection) still
# assume the kotlin-android plugin -- tracked as F-17.
# Android coverage lives on the gradle-compat 8.13 and 9.5.1 legs.
modern-stack:
name: Kotlin 2.4.10 / KSP 2.3.9 / Gradle 9.6.1
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Gradle plugin functional tests (newest stack)
run: >-
./gradlew :kraft-gradle-plugin:test
-Pkraft.test.kotlinVersion=2.4.10
-Pkraft.test.kspVersion=2.3.9
-Pkraft.test.gradleVersion=9.6.1
-PkraftExcludeTags=android
- name: Publish test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-modern-stack
path: kraft-gradle-plugin/build/reports/tests/test/
# The pinned matrix above goes stale the moment a new KSP ships. This job
# resolves whatever is newest on Maven Central at run time, so a KSP release
# that breaks Kraft surfaces on the next weekly run instead of in an issue.
# Schedule/manual only - never blocks a PR on an upstream release.
ksp-latest:
name: KSP latest (probe)
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Resolve latest KSP version
id: ksp
run: |
set -euo pipefail
version=$(curl -sSf --max-time 30 \
'https://search.maven.org/solrsearch/select?q=g:com.google.devtools.ksp+AND+a:symbol-processing-api&core=gav&rows=1&wt=json' \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["response"]["docs"][0]["v"])')
if [ -z "$version" ]; then
echo "Could not resolve the latest KSP version." >&2
exit 1
fi
echo "Latest KSP on Maven Central: $version"
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Gradle plugin functional tests (KSP ${{ steps.ksp.outputs.version }})
run: ./gradlew :kraft-gradle-plugin:test -Pkraft.test.kspVersion=${{ steps.ksp.outputs.version }}
- name: Publish test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-ksp-latest
path: kraft-gradle-plugin/build/reports/tests/test/