Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
3309313
docs(migration): split guide into Java/Kotlin, add status, issues and…
dsudomoin Aug 5, 2026
d5941c8
fix(examples,guides): migrate HTTP client/server code to Kora 2.0 API
dsudomoin Aug 5, 2026
b69919d
fix(examples,guides): migrate HTTP server config keys and global inte…
dsudomoin Aug 5, 2026
5554778
fix(examples,guides): migrate gRPC client package and HTTP client con…
dsudomoin Aug 5, 2026
3b2bbd9
fix(examples,guides): migrate JSON API renames, JSpecify placement an…
dsudomoin Aug 5, 2026
3348337
fix(examples): migrate kafka listeners/publishers to Kora 2.0
dsudomoin Aug 5, 2026
43b8e43
docs(migration): record actual per-module progress in status
dsudomoin Aug 5, 2026
d3554d1
fix(examples,guides): rename JDBC config section db -> jdbc
dsudomoin Aug 5, 2026
35d5841
fix(examples): make OpenAPI server delegate synchronous
dsudomoin Aug 5, 2026
7afd32a
fix(examples): migrate OpenAPI client module to Kora 2.0 contracts
dsudomoin Aug 5, 2026
8b9eee2
fix(examples,guides): migrate OpenAPI client auth config and document…
dsudomoin Aug 5, 2026
cf49073
docs(migration): update module statuses and record open question on O…
dsudomoin Aug 5, 2026
3ce467b
fix(examples): migrate Java S3 modules to Kora 2.0 artifacts
dsudomoin Aug 5, 2026
04bec1a
fix(examples): migrate Java camunda zeebe worker to Kora 2.0
dsudomoin Aug 5, 2026
fdfc505
fix(examples): migrate resilient annotations to typed specifications
dsudomoin Aug 5, 2026
88762b8
fix(examples,guides): migrate configuration embedded in test sources
dsudomoin Aug 5, 2026
25f0b85
docs(migration): add NEURO pattern for configuration embedded in test…
dsudomoin Aug 5, 2026
94e79a5
docs(migration): record crud-submodule status and the open blackbox q…
dsudomoin Aug 5, 2026
0436099
fix(examples,guides): migrate the config extraction contract and rema…
dsudomoin Aug 5, 2026
bb58cef
fix(examples,guides): migrate circuit breaker predicate, fallback and…
dsudomoin Aug 5, 2026
7a6526e
docs(migration): record sweep progress and per-module status
dsudomoin Aug 5, 2026
6c0693c
fix(guides): migrate HTTP server and client guides to the synchronous…
dsudomoin Aug 5, 2026
e5b1916
docs(migration): refine when a mapper needs @Component
dsudomoin Aug 5, 2026
35064b0
fix(examples,guides): finish the remaining Java guide and graalvm mod…
dsudomoin Aug 5, 2026
a6352a0
docs(migration): update sweep numbers after Java guides
dsudomoin Aug 5, 2026
b9f79a4
fix(guides): migrate openapi advanced guide contracts; record the res…
dsudomoin Aug 5, 2026
8f2c2d7
fix(guides): migrate the S3 guide to the two Kora 2.0 S3 artifacts
dsudomoin Aug 5, 2026
9e2d70e
fix(guides): make the S3 bucket initializer a graph root
dsudomoin Aug 5, 2026
0535f1d
docs(migration): record the dual S3 artifact setup and final Java swe…
dsudomoin Aug 5, 2026
ef51251
fix(examples): migrate the Kotlin http-server example to Kora 2.0
dsudomoin Aug 5, 2026
7acce0a
fix(guides): migrate the Kotlin http-server advanced guide to Kora 2.0
dsudomoin Aug 5, 2026
4e357d7
fix(guides): migrate the Kotlin http-client advanced guide to Kora 2.0
dsudomoin Aug 5, 2026
859e502
fix(examples): resolve OpenAPI enums by wire value in both openapi ht…
dsudomoin Aug 5, 2026
271c019
fix(examples): migrate the Kotlin camunda zeebe worker example to Kor…
dsudomoin Aug 5, 2026
b4c19a9
docs(migration): record the two KSP crash defects and the Kotlin zeeb…
dsudomoin Aug 5, 2026
8b6aade
fix(examples): complete the resilient configuration and migrate the K…
dsudomoin Aug 5, 2026
4cc73c1
fix(guides): migrate the Kotlin resilient guide predicate to the 2.0 …
dsudomoin Aug 5, 2026
7ed46cc
fix(examples): migrate both Kotlin S3 examples to the two Kora 2.0 ar…
dsudomoin Aug 5, 2026
7fcc776
fix(guides): migrate the Kotlin S3 guide to the two Kora 2.0 artifacts
dsudomoin Aug 5, 2026
29ac56c
fix(guides): use LettuceRedisCacheModule in the Kotlin multi-level ca…
dsudomoin Aug 5, 2026
93decca
fix(guides): assert the cached value in the Kotlin cache guide test
dsudomoin Aug 5, 2026
4d98e3a
fix(guides): generate the validator and bind the interceptor in the K…
dsudomoin Aug 5, 2026
9d31b2a
fix(guides): generate the email config mapper in the Kotlin DI guide
dsudomoin Aug 5, 2026
8875a40
fix(guides): declare the Void response mapper in the Kotlin http-clie…
dsudomoin Aug 5, 2026
175d29e
fix(examples): migrate the Kotlin http-client example to Kora 2.0
dsudomoin Aug 5, 2026
f93d32d
fix(guides): migrate the Kotlin advanced JDBC guide to the 2.0 execut…
dsudomoin Aug 5, 2026
c9fcd99
fix(examples): migrate the Kotlin JDBC example entities and parameter…
dsudomoin Aug 5, 2026
a3f0dc8
fix(config): migrate openapi.management keys and finish the Kotlin cr…
dsudomoin Aug 5, 2026
458e61a
fix(examples): migrate the Kotlin kafka example to Kora 2.0
dsudomoin Aug 5, 2026
40f7fc0
fix(examples): migrate the Kotlin crud submodule example to Kora 2.0
dsudomoin Aug 5, 2026
d4b4087
docs(migration): record the parallel KSP race, the gRPC builder resol…
dsudomoin Aug 5, 2026
41395a2
docs(migration): record that a gRPC-only application exits right afte…
dsudomoin Aug 5, 2026
799887d
fix(examples,guides): migrate the Kotlin OpenAPI client and advanced …
dsudomoin Aug 5, 2026
97f33ee
fix(examples): send the API key from the generated OpenAPI client
dsudomoin Aug 5, 2026
a9acbf9
fix(examples): align third-party versions and enable metrics
dsudomoin Aug 5, 2026
7ca980f
docs(migration): rebuild the status document from the final verificat…
dsudomoin Aug 5, 2026
19b1c3a
docs(migration): close the issue entries resolved by today's framewor…
dsudomoin Aug 5, 2026
7222e66
docs(migration): record the twenty upstream pull requests
dsudomoin Aug 6, 2026
c63de41
feat(graalvm): build and verify the native images for all three Graal…
dsudomoin Aug 6, 2026
d50d96f
test(graalvm-kafka): wait on the readiness probe instead of a log line
dsudomoin Aug 6, 2026
cf6b145
docs(migration): close the gaps the original brief still had open
dsudomoin Aug 6, 2026
3eb2bbe
docs(migration): record the maintainer review outcome on #801 and #807
dsudomoin Aug 6, 2026
7cf0363
Updated CI
GoodforGod Aug 11, 2026
c2ee147
Updated CI
GoodforGod Aug 11, 2026
164b562
Updated CI
GoodforGod Aug 11, 2026
6a8dfe2
build: finish Kora 2.0 dependency migration
GoodforGod Aug 13, 2026
7eac115
feat(migration)!: finalize Kora 2 migration
GoodforGod Aug 13, 2026
a6245d1
Fixes
GoodforGod Aug 13, 2026
9955177
fix(tests): remove invalid environment entries
GoodforGod Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
31 changes: 19 additions & 12 deletions .github/workflows/test-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,44 @@ on:
branches:
- "master"

permissions:
contents: read

jobs:
build-tests:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ "21" ]
java: [ "25" ]
type: [ "Examples", "Guides" ]
lang: [ "Java", "Kotlin" ]
feature: [ "Database", "Client", "Server", "Graalvm", "Kafka", "Other" ]
name: Pull Request Java ${{ matrix.java }} for ${{ matrix.type }}-${{ matrix.lang }}-${{ matrix.feature }}
exclude:
- type: "Examples"
lang: "Kotlin"
feature: "Graalvm"
- type: "Guides"
feature: "Graalvm"
name: Master Java ${{ matrix.java }} for ${{ matrix.type }}-${{ matrix.lang }}-${{ matrix.feature }}
env:
GRADLE_TASK_SUFFIX: ${{ matrix.type }}${{ matrix.lang }}${{ matrix.feature }}
MAX_PARALLEL_WORKERS: 1
GRADLE_ACTIONS_SKIP_BUILD_RESULT_CAPTURE: true
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: JDK setup
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: "adopt"
distribution: "temurin"

- name: Gradle setup
uses: gradle/actions/setup-gradle@v4
with:
cache-read-only: ${{ github.ref != 'refs/heads/master' }}
gradle-home-cache-cleanup: ${{ github.ref == 'refs/heads/master' }}

- name: Gradle build classes
run: "./gradlew classes${{ env.GRADLE_TASK_SUFFIX }} --no-daemon --no-parallel --scan --stacktrace"

- name: Gradle build test classes
run: "./gradlew testClasses${{ env.GRADLE_TASK_SUFFIX }} --no-daemon --no-parallel --scan --stacktrace"
cache-read-only: false
gradle-home-cache-cleanup: true

- name: Gradle run tests
run: "./gradlew test${{ env.GRADLE_TASK_SUFFIX }} --no-daemon --no-parallel --scan --stacktrace"
run: "./gradlew test${{ env.GRADLE_TASK_SUFFIX }} --no-daemon --no-parallel --console=plain --warning-mode=none --no-problems-report"
32 changes: 17 additions & 15 deletions .github/workflows/test-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,49 @@ name: Test PR

on:
pull_request:
branches:
- "master"

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read
checks: write
pull-requests: write


jobs:
build-tests:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ "21" ]
java: [ "25" ]
type: [ "Examples", "Guides" ]
lang: [ "Java", "Kotlin" ]
feature: [ "Database", "Client", "Server", "Graalvm", "Kafka", "Other" ]
exclude:
- type: "Examples"
lang: "Kotlin"
feature: "Graalvm"
- type: "Guides"
feature: "Graalvm"
name: Pull Request Java ${{ matrix.java }} for ${{ matrix.type }}-${{ matrix.lang }}-${{ matrix.feature }}
env:
GRADLE_TASK_SUFFIX: ${{ matrix.type }}${{ matrix.lang }}${{ matrix.feature }}
MAX_PARALLEL_WORKERS: 1
GRADLE_ACTIONS_SKIP_BUILD_RESULT_CAPTURE: true
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: JDK setup
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: "adopt"
distribution: "temurin"

- name: Gradle setup
uses: gradle/actions/setup-gradle@v4
with:
cache-read-only: ${{ github.ref != 'refs/heads/master' }}
gradle-home-cache-cleanup: ${{ github.ref == 'refs/heads/master' }}

- name: Gradle build classes
run: "./gradlew classes${{ env.GRADLE_TASK_SUFFIX }} --no-daemon --no-parallel --scan --stacktrace"

- name: Gradle build test classes
run: "./gradlew testClasses${{ env.GRADLE_TASK_SUFFIX }} --no-daemon --no-parallel --scan --stacktrace"
cache-read-only: true

- name: Gradle run tests
run: "./gradlew test${{ env.GRADLE_TASK_SUFFIX }} --no-daemon --no-parallel --scan --stacktrace"
run: "./gradlew test${{ env.GRADLE_TASK_SUFFIX }} --no-daemon --no-parallel --console=plain --warning-mode=none --no-problems-report"
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ target/
.idea
.settings/
*.iml
out/
out/

### Local reference checkouts used during the Kora 2.0 migration
agents-md/

### Local working artifacts (never commit)
docs/superpowers/
34 changes: 30 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import org.gradle.api.services.BuildServiceParameters

plugins {
id "com.diffplug.spotless" version "6.19.0"
id "com.asarkar.gradle.build-time-tracker" version "4.3.0"
id "org.graalvm.buildtools.native" version "0.11.5" apply false // or dependsOn fails in graalvm examples
id "com.asarkar.gradle.build-time-tracker" version "4.3.0" apply false
id "org.graalvm.buildtools.native" version "1.1.7" apply false // or dependsOn fails in graalvm examples
id "org.jetbrains.kotlin.jvm" version "2.4.10" apply false
id "org.jetbrains.kotlin.kapt" version "2.4.10" apply false
id "com.google.devtools.ksp" version "2.3.11" apply false
}

def isCiBuild = providers.environmentVariable("CI").map { it.toBoolean() }.orElse(false).get()
if (isCiBuild) {
URLConnection.setDefaultUseCaches("jar", false)
}
if (!isCiBuild) {
apply plugin: "com.asarkar.gradle.build-time-tracker"
}

group = groupId
version = koraVersion

Expand Down Expand Up @@ -53,7 +60,9 @@ subprojects {
}

apply plugin: "java"
apply plugin: "com.asarkar.gradle.build-time-tracker"
if (!isCiBuild) {
apply plugin: "com.asarkar.gradle.build-time-tracker"
}

if (!project.name.contains("kotlin")) {
apply plugin: "com.diffplug.spotless"
Expand Down Expand Up @@ -93,4 +102,21 @@ subprojects {
tasks.register("allDeps", DependencyReportTask)
}

if (isCiBuild) {
subprojects {
afterEvaluate {
tasks.withType(Test).configureEach {
testLogging {
showStandardStreams = false
events = [org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED]
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}
}
}
}

apply from: "${project.rootDir}/grouping.gradle"
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ghcr.io/graalvm/native-image-community:21 as builder
FROM ghcr.io/graalvm/native-image-community:25 as builder

ARG TARGET_DIR=/opt/app
ARG SOURCE_DIR=build/libs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
- [Validation](https://kora-projects.github.io/kora-docs/ru/documentation/validation/)
- [Cache Redis](https://kora-projects.github.io/kora-docs/ru/documentation/cache/#redis)

Скомпилирован с помощью [GraalVM](https://www.graalvm.org/release-notes/JDK_21/)
Скомпилирован с помощью [GraalVM](https://www.graalvm.org/release-notes/JDK_25/)

## Build

Expand Down
19 changes: 7 additions & 12 deletions examples/graalvm/kora-java-graalvm-crud-cassandra/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ configurations {
}

dependencies {
koraBom platform("io.koraframework:kora-parent:$koraVersion")
koraBom platform("io.koraframework:kora-bom:$koraVersion")
annotationProcessor "org.mapstruct:mapstruct-processor:1.5.5.Final"
annotationProcessor "io.koraframework:annotation-processors"
annotationProcessor "io.goodforgod:graalvm-hint-processor:1.2.0"
Expand All @@ -51,7 +51,6 @@ dependencies {
implementation "io.koraframework:openapi-management"
implementation "io.koraframework:logging-logback"

implementation "io.projectreactor:reactor-core:3.6.18" // For reactive examples (optional)
implementation "org.mapstruct:mapstruct:1.5.5.Final"

testImplementation "org.json:json:20231013"
Expand All @@ -60,8 +59,8 @@ dependencies {

testImplementation "org.mockito:mockito-core:5.18.0"
testImplementation "io.koraframework:test-junit5"
testImplementation "io.goodforgod:testcontainers-extensions-scylla:0.13.1"
testImplementation "io.goodforgod:testcontainers-extensions-redis:0.13.1"
testImplementation "io.goodforgod:testcontainers-extensions-scylla:0.15.0"
testImplementation "io.goodforgod:testcontainers-extensions-redis:0.15.0"
testImplementation "org.testcontainers:junit-jupiter:1.21.4"
}

Expand Down Expand Up @@ -89,8 +88,8 @@ compileJava.dependsOn openApiGenerateHttpServer
graalvmNative {
binaries {
main {
imageName = "$project.name"
mainClass = "$application.mainClass"
imageName = project.name
mainClass = application.mainClass
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(25)
vendor = JvmVendorSpec.matching("GraalVM Community")
Expand Down Expand Up @@ -119,7 +118,7 @@ run {
])
}

jar.enabled = false
// nativeCompile takes the project's own artifact as its classpath entry, so the plain jar must stay enabled
shadowJar {
mergeServiceFiles()
manifest {
Expand All @@ -138,10 +137,6 @@ test {
"-XX:TieredStopAtLevel=1",
]

environment([
"": ""
])

useJUnitPlatform()
testLogging {
showStandardStreams = true
Expand Down Expand Up @@ -181,4 +176,4 @@ javadoc {
if (JavaVersion.current().isJava9Compatible()) {
options.addBooleanOption("html5", true)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,40 @@ services:
scylla:
image: scylladb/scylla:2025.3
restart: unless-stopped
command: --smp 1 --developer-mode 1 --authenticator PasswordAuthenticator
ports:
- '5432:5432'
- '9042:9042'
healthcheck:
test: [ "CMD-SHELL", "cqlsh -u cassandra -p cassandra -e 'DESCRIBE KEYSPACES' || exit 1" ]
interval: 10s
timeout: 10s
retries: 30
start_period: 20s


redis:
image: redis:8.0-alpine
restart: unless-stopped
command: [ "redis-server", "--requirepass", "redis" ]
ports:
- '6379:6379'


scylla-init:
image: scylladb/scylla:2025.3
restart: "no"
depends_on:
scylla:
condition: service_healthy
volumes:
- ./src/main/resources/migrations:/migrations:ro
entrypoint: [ "sh", "-c" ]
command:
- |
cqlsh scylla -u cassandra -p cassandra -e "CREATE KEYSPACE IF NOT EXISTS petshop WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}" &&
cqlsh scylla -u cassandra -p cassandra -k petshop -f /migrations/setup.cql


# Можно закомментировать секцию application и использовать docker-compose
# как окружения для запуска сервиса через gradle run
application:
Expand All @@ -14,14 +46,18 @@ services:
- '8080:8080'
- '8085:8085'
environment:
CASSANDRA_CONTACT_POINTS: jdbc:postgresql://postgres:5432/postgres
CASSANDRA_CONTACT_POINTS: scylla:9042
CASSANDRA_USER: cassandra
CASSANDRA_PASS: cassandra
CASSANDRA_DC: cassandra
CASSANDRA_KEYSPACE: cassandra
REDIS_URL: redis
REDIS_USER: redis
CASSANDRA_DC: datacenter1
CASSANDRA_KEYSPACE: petshop
REDIS_URL: redis://redis:6379/0
REDIS_USER: default
REDIS_PASS: redis
depends_on:
- scylla
- redis
scylla:
condition: service_healthy
scylla-init:
condition: service_completed_successfully
redis:
condition: service_started
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package io.koraframework.example.graalvm.crud.cassandra.controller;

import io.micrometer.core.instrument.config.validate.ValidationException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.koraframework.common.annotation.Component;
import io.koraframework.common.Context;
import io.koraframework.common.annotation.Tag;
import io.koraframework.example.graalvm.crud.openapi.server.model.MessageTO;
import io.koraframework.http.common.body.HttpBody;
import io.koraframework.http.server.common.*;
import io.koraframework.http.server.common.HttpServer;
import io.koraframework.http.server.common.interceptor.HttpServerInterceptor;
import io.koraframework.http.server.common.request.HttpServerRequest;
import io.koraframework.http.server.common.response.HttpServerResponse;
import io.koraframework.http.server.common.response.HttpServerResponseException;
import io.koraframework.json.common.JsonWriter;
import io.micrometer.core.instrument.config.validate.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeoutException;

@Tag(HttpServerModule.class)
@Tag(HttpServer.class)
@Component
public final class HttpExceptionHandler implements HttpServerInterceptor {

Expand All @@ -26,14 +29,15 @@ public HttpExceptionHandler(JsonWriter<MessageTO> errorJsonWriter) {
}

@Override
public CompletionStage<HttpServerResponse> intercept(Context context, HttpServerRequest request, InterceptChain chain)
throws Exception {
return chain.process(context, request).exceptionally(e -> {
public HttpServerResponse intercept(HttpServerRequest request, InterceptChain chain) {
try {
return chain.process(request);
} catch (Exception e) {
if (e instanceof HttpServerResponseException ex) {
return ex;
}

var body = HttpBody.json(errorJsonWriter.toByteArrayUnchecked(new MessageTO(e.getMessage())));
var body = HttpBody.json(errorJsonWriter.toByteArray(new MessageTO(e.getMessage())));
if (e instanceof IllegalArgumentException || e instanceof ValidationException) {
return HttpServerResponse.of(400, body);
} else if (e instanceof TimeoutException) {
Expand All @@ -42,6 +46,6 @@ public CompletionStage<HttpServerResponse> intercept(Context context, HttpServer
logger.error("Request '{} {}' failed", request.method(), request.path(), e);
return HttpServerResponse.of(500, body);
}
});
}
}
}
Loading
Loading