feat(build): Java 25와 Spring 호환성 기준을 고정 - #53
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughJava 25, Spring Boot 4.1.0, Spring AI 2.0.0 호환성 기준을 빌드·게시 검증과 문서에 반영하고, ChatClient 빌더 커스터마이저 및 native usage 변환을 Spring AI 2.0 API에 맞게 변경했다. Changes호환성 기준 및 통합
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
build.gradle (4)
650-652: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
check에 네트워크 의존적인 중첩 빌드가 붙습니다.생성된 소비자 프로젝트가
mavenCentral()과 Java 25 툴체인 프로비저닝을 필요로 하므로, 오프라인/제한 환경에서./gradlew check가 실패합니다. 호환성 검증 전용 태스크 그룹으로 분리하고 CI에서만 호출하는 방안을 검토해 주세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@build.gradle` around lines 650 - 652, Remove the network-dependent verifyCompatibilityMatrix and verifyCoreConsumer dependencies from the check task. Keep both verification tasks available under a dedicated compatibility-verification grouping, and invoke that group explicitly from CI rather than as part of the default local check lifecycle.
573-581: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
outputs.dir(...)를 선언하고doLast에서 그 디렉터리를 삭제하면 up-to-date 판정과 충돌합니다.또한
delete가 출력 디렉터리 전체를 지우므로 증분 빌드에서 매번 재생성됩니다. 출력 선언을 유지하려면 삭제 대신 파일 덮어쓰기만 하거나,outputs.upToDateWhen { false }로 의도를 명확히 하는 편이 낫습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@build.gradle` around lines 573 - 581, Resolve the output/up-to-date conflict in the prepareCoreConsumer task by either preserving coreConsumerDirectory and overwriting its generated files without deleting the directory, or explicitly disabling up-to-date checks with outputs.upToDateWhen { false } if cleanup is required. Keep the outputs.dir(coreConsumerDirectory) declaration consistent with the chosen behavior.
388-397: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff
options.release검증이sample-app을 포함한 모든 서브프로젝트의 모든 JavaCompile 태스크를 실행 시점에 realize합니다.
subprojects.each { ... tasks.withType(JavaCompile).each ... }는 doLast 안에서 다른 프로젝트의 태스크 그래프에 접근하므로 configuration cache와 격리 규칙에 어긋납니다. 검증 값 자체는compatibilityMatrix하나에서 나오므로, 각 서브프로젝트 설정 단계에서release값을 수집해 태스크 입력(Provider)으로 넘기는 편이 안전합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@build.gradle` around lines 388 - 397, Refactor the validation around the subprojects.each and JavaCompile task iteration so it does not access or realize other projects’ tasks at execution time. During each subproject’s configuration, collect its JavaCompile release values and expose them as a Provider task input, deriving the expected value solely from compatibilityMatrix. Validate that provider input in the verification task while preserving the existing mismatch error and compile-task path details.
489-500: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftsample-app의 runtimeClasspath 해석을 작업 액션 밖으로 빼세요.
project(':token-pilot-sample-app').configurations.runtimeClasspath를doLast에서 직접 읽는 두 구간은 configuration cache 호환이 아닙니다. 설정 단계에서Configuration/ArtifactCollection을 미리 만들고, 태스크에는 입력으로 주입하세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@build.gradle` around lines 489 - 500, `project(':token-pilot-sample-app').configurations.runtimeClasspath`를 `doLast`에서 직접 해석하지 않도록 두 구간을 수정하세요. 설정 단계에서 해당 Configuration/ArtifactCollection과 필요한 해석 결과를 미리 생성하고 태스크 입력으로 주입한 뒤, `resolvedVersions`를 구성하는 `allComponents` 순회는 주입된 입력만 사용하게 변경해 configuration cache 호환성을 유지하세요.token-pilot-micrometer/build.gradle (1)
3-4: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win
compatibilityMatrix에micrometer항목을 추가해 이 모듈도 단일 기준으로 관리해 주세요.token-pilot-micrometer는 BOM 밖이라 명시 버전이 필요하지만, 루트 기준과 분리되면 버전 일치 여부를 놓치기 쉽습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@token-pilot-micrometer/build.gradle` around lines 3 - 4, Add a micrometer entry to compatibilityMatrix and update the explicit versions in the token-pilot-micrometer dependencies to reference that centralized value, keeping micrometer-observation and micrometer-core aligned while preserving their required explicit versioning outside the BOM.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@AGENTS.md`:
- Line 201: TokenPilotAutoConfiguration의 ChatClientBuilderCustomizer 기본 빈에
`@ConditionalOnMissingBean`(ChatClientBuilderCustomizer.class)을 추가해 사용자 정의 빈이 있으면
자동구성 빈을 등록하지 않도록 하세요. ApplicationContextRunner 테스트에 사용자 커스터마이저가 기본 빈보다 우선되고 중복
등록되지 않는 검증을 추가하세요.
In `@build.gradle`:
- Around line 642-648: Update the verifyCoreConsumer Exec task to select the
platform-appropriate Gradle wrapper, using gradlew.bat on Windows and gradlew on
Unix-like systems, while preserving the existing prepareCoreConsumer dependency
and run invocation.
In
`@token-pilot-autoconfigure/src/main/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfiguration.java`:
- Around line 133-138: Update
TokenPilotAutoConfiguration.ledgerChatClientBuilderCustomizer to return the
public ChatClientBuilderCustomizer interface and add `@ConditionalOnMissingBean`
so user-provided customizers take precedence. In
token-pilot-autoconfigure/src/test/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfigurationTest.java:76,
assert hasSingleBean(ChatClientBuilderCustomizer.class); verify the
implementation type separately via getBean(...).isInstanceOf(...) only if
needed.
In
`@token-pilot-autoconfigure/src/test/java/io/tokenpilot/autoconfigure/ChatClientBuilderCustomizerTest.java`:
- Line 20: Update the `@DisplayName` annotation in ChatClientBuilderCustomizerTest
to replace the misspelled “ChatClementCustomizer” text with
“ChatClientBuilderCustomizer”, leaving the test behavior unchanged.
---
Nitpick comments:
In `@build.gradle`:
- Around line 650-652: Remove the network-dependent verifyCompatibilityMatrix
and verifyCoreConsumer dependencies from the check task. Keep both verification
tasks available under a dedicated compatibility-verification grouping, and
invoke that group explicitly from CI rather than as part of the default local
check lifecycle.
- Around line 573-581: Resolve the output/up-to-date conflict in the
prepareCoreConsumer task by either preserving coreConsumerDirectory and
overwriting its generated files without deleting the directory, or explicitly
disabling up-to-date checks with outputs.upToDateWhen { false } if cleanup is
required. Keep the outputs.dir(coreConsumerDirectory) declaration consistent
with the chosen behavior.
- Around line 388-397: Refactor the validation around the subprojects.each and
JavaCompile task iteration so it does not access or realize other projects’
tasks at execution time. During each subproject’s configuration, collect its
JavaCompile release values and expose them as a Provider task input, deriving
the expected value solely from compatibilityMatrix. Validate that provider input
in the verification task while preserving the existing mismatch error and
compile-task path details.
- Around line 489-500:
`project(':token-pilot-sample-app').configurations.runtimeClasspath`를 `doLast`에서
직접 해석하지 않도록 두 구간을 수정하세요. 설정 단계에서 해당 Configuration/ArtifactCollection과 필요한 해석 결과를
미리 생성하고 태스크 입력으로 주입한 뒤, `resolvedVersions`를 구성하는 `allComponents` 순회는 주입된 입력만
사용하게 변경해 configuration cache 호환성을 유지하세요.
In `@token-pilot-micrometer/build.gradle`:
- Around line 3-4: Add a micrometer entry to compatibilityMatrix and update the
explicit versions in the token-pilot-micrometer dependencies to reference that
centralized value, keeping micrometer-observation and micrometer-core aligned
while preserving their required explicit versioning outside the BOM.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 00535b12-7301-47c9-b1c2-be11e30935db
📒 Files selected for processing (12)
.github/workflows/ci.ymlAGENTS.mdREADME.mdbuild.gradletoken-pilot-autoconfigure/src/main/java/io/tokenpilot/autoconfigure/LedgerChatClientBuilderCustomizer.javatoken-pilot-autoconfigure/src/main/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfiguration.javatoken-pilot-autoconfigure/src/test/java/io/tokenpilot/autoconfigure/ChatClientBuilderCustomizerTest.javatoken-pilot-autoconfigure/src/test/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfigurationTest.javatoken-pilot-micrometer/build.gradletoken-pilot-sample-app/src/test/java/io/tokenpilot/sample/SampleApplicationChatClientE2ETest.javatoken-pilot-spring-ai/build.gradletoken-pilot-spring-ai/src/main/java/io/tokenpilot/springai/internal/DefaultUsageExtractor.java
💤 Files with no reviewable changes (1)
- token-pilot-spring-ai/build.gradle
| | `BudgetStateStore` | `token-pilot.budget.enabled` + missing bean | Default in-memory budget state | | ||
| | `BudgetEvaluator` | `token-pilot.budget.enabled` + missing bean | Default budget evaluator | | ||
| | `ChatClientCustomizer` | Spring AI classpath + `LedgerAdvisor` bean | Adds advisor to ChatClient builders | | ||
| | `ChatClientBuilderCustomizer` | Spring AI classpath + `LedgerAdvisor` bean | Adds advisor to ChatClient builders | |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
사용자 정의 커스터마이저가 기본 빈보다 우선하도록 조건을 맞춰 주세요.
AGENTS.md는 ChatClientBuilderCustomizer 기본 빈을 문서화하지만, 제공된 TokenPilotAutoConfiguration.java:130-138 구현에는 @ConditionalOnMissingBean(ChatClientBuilderCustomizer.class)가 없습니다. 따라서 사용자가 정의한 커스터마이저가 있어도 기본 커스터마이저가 함께 등록되어 사용자 빈 우선 규칙을 위반할 수 있습니다. 자동구성에 missing-bean 조건을 추가하고 ApplicationContextRunner로 사용자 빈 우선 테스트를 보강해야 합니다.
As per coding guidelines, @ConditionalOnMissingBean을 사용해 사용자 빈이 기본 빈보다 우선하도록 해야 합니다.
Also applies to: 216-216
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@AGENTS.md` at line 201, TokenPilotAutoConfiguration의
ChatClientBuilderCustomizer 기본 빈에
`@ConditionalOnMissingBean`(ChatClientBuilderCustomizer.class)을 추가해 사용자 정의 빈이 있으면
자동구성 빈을 등록하지 않도록 하세요. ApplicationContextRunner 테스트에 사용자 커스터마이저가 기본 빈보다 우선되고 중복
등록되지 않는 검증을 추가하세요.
Source: Coding guidelines
| tasks.register('verifyCoreConsumer', Exec) { | ||
| group = 'verification' | ||
| description = 'Compiles and runs the generated Java 25 core-only consumer.' | ||
| dependsOn tasks.named('prepareCoreConsumer') | ||
| workingDir coreConsumerDirectory | ||
| commandLine rootProject.file('gradlew').absolutePath, '--no-daemon', 'run' | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
gradlew를 직접 실행하면 Windows에서 깨지고, 상위 빌드 안에서 중첩 Gradle 실행이 됩니다.
rootProject.file('gradlew')는 유닉스 셸 스크립트라 Windows 개발자 환경에서는 verifyCoreConsumer(그리고 check)가 실패합니다. OS에 따라 gradlew.bat을 선택하거나, Gradle TestKit GradleRunner로 소비자 빌드를 실행하는 방식을 고려해 주세요.
🐛 OS 분기 예시
tasks.register('verifyCoreConsumer', Exec) {
group = 'verification'
description = 'Compiles and runs the generated Java 25 core-only consumer.'
dependsOn tasks.named('prepareCoreConsumer')
workingDir coreConsumerDirectory
- commandLine rootProject.file('gradlew').absolutePath, '--no-daemon', 'run'
+ def wrapper = org.gradle.internal.os.OperatingSystem.current().windows ? 'gradlew.bat' : 'gradlew'
+ commandLine rootProject.file(wrapper).absolutePath, '--no-daemon', 'run'
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| tasks.register('verifyCoreConsumer', Exec) { | |
| group = 'verification' | |
| description = 'Compiles and runs the generated Java 25 core-only consumer.' | |
| dependsOn tasks.named('prepareCoreConsumer') | |
| workingDir coreConsumerDirectory | |
| commandLine rootProject.file('gradlew').absolutePath, '--no-daemon', 'run' | |
| } | |
| tasks.register('verifyCoreConsumer', Exec) { | |
| group = 'verification' | |
| description = 'Compiles and runs the generated Java 25 core-only consumer.' | |
| dependsOn tasks.named('prepareCoreConsumer') | |
| workingDir coreConsumerDirectory | |
| def wrapper = org.gradle.internal.os.OperatingSystem.current().windows ? 'gradlew.bat' : 'gradlew' | |
| commandLine rootProject.file(wrapper).absolutePath, '--no-daemon', 'run' | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@build.gradle` around lines 642 - 648, Update the verifyCoreConsumer Exec task
to select the platform-appropriate Gradle wrapper, using gradlew.bat on Windows
and gradlew on Unix-like systems, while preserving the existing
prepareCoreConsumer dependency and run invocation.
| @Bean | ||
| @ConditionalOnBean(LedgerAdvisor.class) | ||
| @ConditionalOnClass(ChatClient.class) | ||
| public LedgerChatClientCustomizer ledgerChatClientCustomizer(LedgerAdvisor ledgerAdvisor) { | ||
| return new LedgerChatClientCustomizer(ledgerAdvisor); | ||
| public LedgerChatClientBuilderCustomizer ledgerChatClientBuilderCustomizer(LedgerAdvisor ledgerAdvisor) { | ||
| return new LedgerChatClientBuilderCustomizer(ledgerAdvisor); | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
커스터마이저 빈이 인터페이스가 아닌 구현 클래스 타입으로 노출되어 있고, 테스트도 그 타입에 묶여 있습니다. 근본 원인은 자동구성 팩토리 메서드의 반환 타입이 LedgerChatClientBuilderCustomizer라는 점입니다.
token-pilot-autoconfigure/src/main/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfiguration.java#L133-L138: 반환 타입을ChatClientBuilderCustomizer로 바꾸고@ConditionalOnMissingBean을 추가하세요.token-pilot-autoconfigure/src/test/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfigurationTest.java#L76-L76: 단언을hasSingleBean(ChatClientBuilderCustomizer.class)로 변경하고, 필요하면 구현 타입 검증은getBean(...)후isInstanceOf로 수행하세요.
As per path instructions: "Register beans by public interface type whenever possible" 및 "Use @ConditionalOnMissingBean so user beans take precedence over defaults".
📍 Affects 2 files
token-pilot-autoconfigure/src/main/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfiguration.java#L133-L138(this comment)token-pilot-autoconfigure/src/test/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfigurationTest.java#L76-L76
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@token-pilot-autoconfigure/src/main/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfiguration.java`
around lines 133 - 138, Update
TokenPilotAutoConfiguration.ledgerChatClientBuilderCustomizer to return the
public ChatClientBuilderCustomizer interface and add `@ConditionalOnMissingBean`
so user-provided customizers take precedence. In
token-pilot-autoconfigure/src/test/java/io/tokenpilot/autoconfigure/TokenPilotAutoConfigurationTest.java:76,
assert hasSingleBean(ChatClientBuilderCustomizer.class); verify the
implementation type separately via getBean(...).isInstanceOf(...) only if
needed.
Source: Path instructions
| .withConfiguration(AutoConfigurations.of(TokenPilotAutoConfiguration.class)); | ||
|
|
||
| @Test | ||
| @DisplayName("Advisor가 존재할 때 ChatClementCustomizer를 등록해야 합니다.") |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
@DisplayName 오타: "ChatClementCustomizer" → "ChatClientBuilderCustomizer".
🐛 수정안
- `@DisplayName`("Advisor가 존재할 때 ChatClementCustomizer를 등록해야 합니다.")
+ `@DisplayName`("Advisor가 존재할 때 ChatClientBuilderCustomizer를 등록해야 합니다.")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @DisplayName("Advisor가 존재할 때 ChatClementCustomizer를 등록해야 합니다.") | |
| `@DisplayName`("Advisor가 존재할 때 ChatClientBuilderCustomizer를 등록해야 합니다.") |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@token-pilot-autoconfigure/src/test/java/io/tokenpilot/autoconfigure/ChatClientBuilderCustomizerTest.java`
at line 20, Update the `@DisplayName` annotation in
ChatClientBuilderCustomizerTest to replace the misspelled
“ChatClementCustomizer” text with “ChatClientBuilderCustomizer”, leaving the
test behavior unchanged.
변경 사항
ChatClientBuilderCustomizer와 Jackson 3 기반 usage 변환으로 마이그레이션했습니다.영향
0.1.0 사용자는 Java 25 + Spring Boot 4.1.0 + Spring AI 2.0.0 조합을 사용해야 합니다.
token-pilot-core는 계속 Spring 계열 런타임 의존성 없이 사용할 수 있습니다.검증
./gradlew clean build./gradlew publishToMavenLocal jreleaserConfigCloses #47