Skip to content

Codegen follow-ups: plugin publishing, generated source task wiring, and registry-only converter API #418

Description

@wax911

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions