Description Of Bug
During a full consumer migration from asset-based @GraphQuery usage to codegen-first usage of retrofit-graphql tag 7f05e86cd9, three upstream gaps were identified.
The consumer migration required local workarounds, but this issue should be treated as a self-contained upstream handoff. Implementers should not assume access to downstream applications. The expected fix should be validated inside this repository using host-side publication checks and minimal synthetic Gradle consumer fixtures.
1. gradle-plugin artifact is not resolvable from JitPack
The Gradle plugin does not resolve from JitPack using:
classpath("com.github.AniTrend.retrofit-graphql:gradle-plugin:7f05e86cd9")
or an equivalent plugin-resolution path.
The root build currently publishes repository modules, but the Gradle plugin is structurally handled as an included build. That makes it easy for the root publishMavenPublicationToMavenLocal path to miss the plugin implementation artifact and/or the Gradle plugin marker artifact.
This blocks external consumers from applying the codegen plugin without local includeBuild(...) substitution.
2. Generated GraphQL sources are not safely wired into all Kotlin processing paths
The current plugin generates sources, then wires generation into Kotlin compile and KSP tasks. However, consumer migration exposed task validation and ordering failures around generated source availability.
The observed failure surfaced through KAPT stub generation, where downstream builds needed a manual workaround similar to:
tasks.matching { it.name.startsWith("kaptGenerateStubs") }
.configureEach { dependsOn("generateGraphQLSources") }
This must not be fixed as a KAPT-only issue.
Future and existing consumers may use different Kotlin processing stacks:
- plain Kotlin compilation only
- KAPT, for annotation processors that need generated symbols during stub generation
- KSP, for symbol processors that need generated symbols during source analysis
- Android variants such as
Debug, Release, and custom build types/flavors
- multiple GraphQL targets such as
anilist and edge
The plugin contract should guarantee that generated GraphQL sources are available before any Kotlin compile, KAPT stub, or KSP task that can consume those sources.
3. GraphConverter still requires an AbstractGraphProcessor for codegen-only usage
GraphConverter accepts a GraphQLDocumentRegistry, but still requires an AbstractGraphProcessor path. This forces codegen-first consumers to keep compatibility shims such as a no-op processor even when all GraphQL documents are supplied by generated registry code.
That keeps asset-era runtime assumptions in codegen-only consumers and makes migration noisier than necessary.
Expected Behaviour
Plugin publishing
gradle-plugin should publish as a resolvable artifact for external consumers.
- The implementation artifact should exist in the generated Maven repository output.
- The Gradle plugin marker artifact should exist if the intended consumption path is the
plugins {} DSL.
codegen-core should resolve transitively from the plugin artifact.
- Consumers should not need local
includeBuild(".../retrofit-graphql/gradle-plugin") substitution.
Generated source task wiring
- Generated GraphQL sources should be registered as source inputs for the relevant Kotlin/Android source sets.
compile*Kotlin tasks should depend on the relevant GraphQL generation task.
ksp*Kotlin tasks should depend on the relevant GraphQL generation task.
kaptGenerateStubs*Kotlin tasks should depend on the relevant GraphQL generation task when KAPT is applied.
- Multi-target codegen should not accidentally omit outputs or rely on consumers manually wiring task dependencies.
- Consumers should not need manual
dependsOn("generateGraphQLSources") workarounds for KAPT, KSP, or Kotlin compilation.
Registry-only converter API
- Codegen-first consumers should be able to create/use a
GraphConverter with a GraphQLDocumentRegistry without requiring Android assets or an AbstractGraphProcessor shim.
- Existing asset-based APIs should remain source compatible.
- Legacy package/API compatibility should be preserved where applicable, especially for the existing
:library compatibility surface.
Implementation Guidance
This issue should ideally be implemented as three isolated PRs because each concern has a different blast radius.
PR 1: Publish the Gradle plugin correctly
Verify the publication graph rather than assuming root publication covers the plugin.
Implementation options may include:
- making
gradle-plugin publish from its own included build
- making the root JitPack install step publish both the root modules and the plugin build
- ensuring
java-gradle-plugin + maven-publish produce the plugin implementation artifact and marker artifact
- documenting the supported plugin consumption path
Do not stop at ./gradlew build passing. The output must be inspected.
Host verification should include:
./gradlew build --stacktrace --no-daemon -x lint -x lintVitalRelease
./gradlew publishMavenPublicationToMavenLocal --no-daemon
./gradlew -p gradle-plugin publishToMavenLocal --no-daemon
Then verify the expected local Maven outputs exist under ~/.m2/repository, including:
- plugin implementation artifact
- Gradle plugin marker artifact, if applicable
codegen-core
- generated
.pom files with valid dependency metadata
Also add or document a minimal consumer check that resolves the plugin from Maven local.
Example verification shape:
pluginManagement {
repositories {
mavenLocal()
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("co.anitrend.retrofit.graphql.codegen") version "<local-version>"
}
Run:
./gradlew help
./gradlew generateGraphQLSources
PR 2: Make generated source wiring processor-safe
Do not solve this by only adding a string match for kaptGenerateStubs.
The plugin should define a clear contract for generated source availability across Kotlin, KAPT, and KSP.
At minimum, the implementation should handle task names matching these families:
compile*Kotlin
ksp*Kotlin
kaptGenerateStubs*Kotlin
However, prefer Gradle/AGP-aware source registration where possible so task dependencies are inferred safely. Where AGP/Kotlin APIs do not infer the correct ordering, explicit dependsOn(...) wiring is acceptable.
The implementation must consider:
- Android application modules
- Android library modules
- Kotlin JVM modules, if still supported
- debug/release variants
- custom build types/flavors
- single-target GraphQL configuration
- multi-target GraphQL configuration
Synthetic consumer fixtures should be added inside this repository, or an equivalent test strategy should be documented, because implementers may not have access to real downstream consumers.
Suggested verification matrix:
Fixture A: Android module with no KAPT/KSP
- Apply the codegen plugin.
- Reference generated GraphQL code from Kotlin source.
- Run:
./gradlew generateGraphQLSources
./gradlew compileDebugKotlin
Fixture B: Android module with KAPT
- Apply
kotlin-kapt.
- Add a simple annotation processor path or use an existing lightweight processor fixture.
- Reference generated GraphQL code from source participating in KAPT stub generation.
- Run:
./gradlew generateGraphQLSources
./gradlew kaptGenerateStubsDebugKotlin
./gradlew compileDebugKotlin
Fixture C: Android module with KSP
- Apply
com.google.devtools.ksp.
- Add a simple KSP processor path or use an existing lightweight processor fixture.
- Reference generated GraphQL code from source participating in KSP analysis.
- Run:
./gradlew generateGraphQLSources
./gradlew kspDebugKotlin
./gradlew compileDebugKotlin
Fixture D: Multi-target GraphQL config
Configure at least two GraphQL targets, for example:
retrofitGraphQL {
common {
generateDocuments.set(true)
generateOperationConstants.set(true)
generateHashes.set(true)
generateVariables.set(false)
}
target("anilist") {
packageName.set("test.generated.anilist")
schema.set(file("src/main/graphql/anilist/schema.graphql"))
operations.from(fileTree("src/main/graphql/anilist") { include("**/*.graphql") })
}
target("edge") {
packageName.set("test.generated.edge")
schema.set(file("src/main/graphql/edge/schema.graphql"))
operations.from(fileTree("src/main/graphql/edge") { include("**/*.graphql") })
}
}
Verify:
- both generation tasks run
- both output directories exist
- generated registries/constants/documents compile
- KAPT/KSP tasks do not race generated source creation
PR 3: Add a registry-only converter path
Avoid making consumers provide a fake AbstractGraphProcessor when a generated GraphQLDocumentRegistry is already available.
Prefer a codegen-first API such as:
GraphConverter.create(
registry = GeneratedGraphQLDocumentRegistry,
)
or:
GraphConverter.create(
registry = GeneratedGraphQLDocumentRegistry,
gson = customGson,
level = ILogger.Level.INFO,
)
This path should not require:
Context
- Android assets
AssetManagerDiscoveryPlugin
- consumer-defined no-op processors
Existing asset-based APIs should remain available.
If deeper refactoring is needed, prefer introducing a small document resolver abstraction over exposing a public no-op processor as the main migration path.
Acceptance Criteria
gradle-plugin can be published and resolved from a Maven repository without local included-build substitution.
- Generated Maven local outputs are explicitly verified after publication.
- A minimal consumer can apply the plugin and run
generateGraphQLSources from Maven local.
- Generated GraphQL sources are available before Kotlin compile tasks.
- Generated GraphQL sources are available before KAPT stub generation tasks.
- Generated GraphQL sources are available before KSP tasks.
- Multi-target GraphQL generation works without manual task wiring.
- Codegen-first consumers can use
GraphConverter with a generated registry and no asset/no-op processor shim.
- Existing asset-based consumer APIs remain compatible.
- Migration documentation explains asset-based usage vs codegen registry usage.
Additional Context
This was discovered during migration of a downstream consumer from asset-based GraphQL request injection to generated GraphQL documents/registries.
Environment used during the consumer migration:
- JDK 21
- AGP 9.2.1
- Kotlin 2.4.0
- Retrofit-graphql tag
7f05e86cd9
Temporary downstream workarounds that should no longer be required after this issue is fixed:
- local
includeBuild("../../retrofit-graphql/gradle-plugin") dependency substitution for plugin resolution
- manual
kaptGenerateStubs* -> generateGraphQLSources task dependency wiring
NoOpGraphProcessor shim passed into GraphConverter despite generated registry usage
The fix should be validated with repository-local fixtures or documented synthetic consumers because future implementers may not have access to the original downstream applications that exposed the failures.
Description Of Bug
During a full consumer migration from asset-based
@GraphQueryusage to codegen-first usage ofretrofit-graphqltag7f05e86cd9, three upstream gaps were identified.The consumer migration required local workarounds, but this issue should be treated as a self-contained upstream handoff. Implementers should not assume access to downstream applications. The expected fix should be validated inside this repository using host-side publication checks and minimal synthetic Gradle consumer fixtures.
1.
gradle-pluginartifact is not resolvable from JitPackThe Gradle plugin does not resolve from JitPack using:
classpath("com.github.AniTrend.retrofit-graphql:gradle-plugin:7f05e86cd9")or an equivalent plugin-resolution path.
The root build currently publishes repository modules, but the Gradle plugin is structurally handled as an included build. That makes it easy for the root
publishMavenPublicationToMavenLocalpath to miss the plugin implementation artifact and/or the Gradle plugin marker artifact.This blocks external consumers from applying the codegen plugin without local
includeBuild(...)substitution.2. Generated GraphQL sources are not safely wired into all Kotlin processing paths
The current plugin generates sources, then wires generation into Kotlin compile and KSP tasks. However, consumer migration exposed task validation and ordering failures around generated source availability.
The observed failure surfaced through KAPT stub generation, where downstream builds needed a manual workaround similar to:
tasks.matching { it.name.startsWith("kaptGenerateStubs") } .configureEach { dependsOn("generateGraphQLSources") }This must not be fixed as a KAPT-only issue.
Future and existing consumers may use different Kotlin processing stacks:
Debug,Release, and custom build types/flavorsanilistandedgeThe plugin contract should guarantee that generated GraphQL sources are available before any Kotlin compile, KAPT stub, or KSP task that can consume those sources.
3.
GraphConverterstill requires anAbstractGraphProcessorfor codegen-only usageGraphConverteraccepts aGraphQLDocumentRegistry, but still requires anAbstractGraphProcessorpath. This forces codegen-first consumers to keep compatibility shims such as a no-op processor even when all GraphQL documents are supplied by generated registry code.That keeps asset-era runtime assumptions in codegen-only consumers and makes migration noisier than necessary.
Expected Behaviour
Plugin publishing
gradle-pluginshould publish as a resolvable artifact for external consumers.plugins {}DSL.codegen-coreshould resolve transitively from the plugin artifact.includeBuild(".../retrofit-graphql/gradle-plugin")substitution.Generated source task wiring
compile*Kotlintasks should depend on the relevant GraphQL generation task.ksp*Kotlintasks should depend on the relevant GraphQL generation task.kaptGenerateStubs*Kotlintasks should depend on the relevant GraphQL generation task when KAPT is applied.dependsOn("generateGraphQLSources")workarounds for KAPT, KSP, or Kotlin compilation.Registry-only converter API
GraphConverterwith aGraphQLDocumentRegistrywithout requiring Android assets or anAbstractGraphProcessorshim.:librarycompatibility surface.Implementation Guidance
This issue should ideally be implemented as three isolated PRs because each concern has a different blast radius.
PR 1: Publish the Gradle plugin correctly
Verify the publication graph rather than assuming root publication covers the plugin.
Implementation options may include:
gradle-pluginpublish from its own included buildjava-gradle-plugin+maven-publishproduce the plugin implementation artifact and marker artifactDo not stop at
./gradlew buildpassing. The output must be inspected.Host verification should include:
Then verify the expected local Maven outputs exist under
~/.m2/repository, including:codegen-core.pomfiles with valid dependency metadataAlso add or document a minimal consumer check that resolves the plugin from Maven local.
Example verification shape:
pluginManagement { repositories { mavenLocal() google() mavenCentral() gradlePluginPortal() } } plugins { id("co.anitrend.retrofit.graphql.codegen") version "<local-version>" }Run:
./gradlew help ./gradlew generateGraphQLSourcesPR 2: Make generated source wiring processor-safe
Do not solve this by only adding a string match for
kaptGenerateStubs.The plugin should define a clear contract for generated source availability across Kotlin, KAPT, and KSP.
At minimum, the implementation should handle task names matching these families:
compile*Kotlinksp*KotlinkaptGenerateStubs*KotlinHowever, prefer Gradle/AGP-aware source registration where possible so task dependencies are inferred safely. Where AGP/Kotlin APIs do not infer the correct ordering, explicit
dependsOn(...)wiring is acceptable.The implementation must consider:
Synthetic consumer fixtures should be added inside this repository, or an equivalent test strategy should be documented, because implementers may not have access to real downstream consumers.
Suggested verification matrix:
Fixture A: Android module with no KAPT/KSP
Fixture B: Android module with KAPT
kotlin-kapt.Fixture C: Android module with KSP
com.google.devtools.ksp.Fixture D: Multi-target GraphQL config
Configure at least two GraphQL targets, for example:
retrofitGraphQL { common { generateDocuments.set(true) generateOperationConstants.set(true) generateHashes.set(true) generateVariables.set(false) } target("anilist") { packageName.set("test.generated.anilist") schema.set(file("src/main/graphql/anilist/schema.graphql")) operations.from(fileTree("src/main/graphql/anilist") { include("**/*.graphql") }) } target("edge") { packageName.set("test.generated.edge") schema.set(file("src/main/graphql/edge/schema.graphql")) operations.from(fileTree("src/main/graphql/edge") { include("**/*.graphql") }) } }Verify:
PR 3: Add a registry-only converter path
Avoid making consumers provide a fake
AbstractGraphProcessorwhen a generatedGraphQLDocumentRegistryis already available.Prefer a codegen-first API such as:
or:
This path should not require:
ContextAssetManagerDiscoveryPluginExisting asset-based APIs should remain available.
If deeper refactoring is needed, prefer introducing a small document resolver abstraction over exposing a public no-op processor as the main migration path.
Acceptance Criteria
gradle-plugincan be published and resolved from a Maven repository without local included-build substitution.generateGraphQLSourcesfrom Maven local.GraphConverterwith a generated registry and no asset/no-op processor shim.Additional Context
This was discovered during migration of a downstream consumer from asset-based GraphQL request injection to generated GraphQL documents/registries.
Environment used during the consumer migration:
7f05e86cd9Temporary downstream workarounds that should no longer be required after this issue is fixed:
includeBuild("../../retrofit-graphql/gradle-plugin")dependency substitution for plugin resolutionkaptGenerateStubs* -> generateGraphQLSourcestask dependency wiringNoOpGraphProcessorshim passed intoGraphConverterdespite generated registry usageThe fix should be validated with repository-local fixtures or documented synthetic consumers because future implementers may not have access to the original downstream applications that exposed the failures.