Hey team, we are getting dependency resolution err...
# apollo-kotlin
g
Hey team, we are getting dependency resolution errors with our v4 setup. I haven’t fully identified the cause but here’s my theory. 🧵
The setup:
Copy code
Repository A
-------------
:schema-module - The schema lives here. This publishes types and metadata as "schema-module-apollo"
  :common-module - dependsOn(project("schema-module")) and publishes its metadata as "common-module-apollo"


Repository B (depends on Repository A modules)
--------------
:app-module
 - dependsOn("com.x.y.z.schema-module-apollo")
 - dependsOn("com.x.y.z.common-module-apollo")
The error we get is:
Copy code
Could not resolve com.x.y.z:schema-module:1.0.0.
     Required by:
         project :app-module > com.x.y.z:common-module-apollo:1.0.0:20241211.235659-1

No matching variant of com.x.y.z.schema-module:1.0.0:20241211.185912-1 was found. The consumer was configured to find a component for use during 'OtherOptions', as well as attribute 'com.apollographql.direction' with value 'Upstream', attribute 'com.apollographql.service' with value 'production' but:
In my opinion, when
common-module
depends on the source code of
schema-module
it’s publication doesn’t contain any information about the
metadata
of
schema-module
. That’s why
app
module is looking for
schema-module
instead of
schema-module-apollo
. Any thoughts?
m
probably. if that's the case, you should see references to
schema-module
in the published
common-module-apollo.module
file
You might be able to workaround by making
common-module-apollo
non-transitive. Something like:
Copy code
configurations.configureEach {
  if (name.lowercase().contains("apollo")) {
    dependencies.forEach {
      if (it is ExternalDependency && it.module.name.contains("ommon-module-apollo")) {
        it.isTransitive = false
      }
    }
  }
}
👀 1
I'm AFK right now so take this with a grain of salt but I can look more tomorrow
thank you color 1
g
Yeah, I checked the
.module
file already, no references to
-apollo
.
Copy code
{
      "name": "apolloProductionOtherOptionsConsumable",
      "attributes": {
        "com.apollographql.direction": "Upstream",
        "com.apollographql.service": "production",
        "org.gradle.usage": "OtherOptions"
      },
      "dependencies": [
        {
          "group": "com.x.y.z",
          "module": "schema-module",
          "version": {
            "requires": "1.0.0"
          }
        }
      ]
    },
👍 1
m
Looking into this, I can't even publish. I get this error:
Copy code
Execution failed for task ':fragments:generatePomFileForApolloPublication'.
    > Failed to query the value of property 'dependencies'.
       > Publishing is not able to resolve a dependency on a project with multiple publications that have different coordinates.
         Found the following publications in project ':schema':
           - Maven publication 'default' with coordinates com.schema:schema:1.0.0
           - Maven publication 'apollo' with coordinates com.schema:schema-apollo:1.0.0
g
Do you have a branch with your setup? We don't have problem with publishing. Maybe your dependencies are different?
m
Was basically testing using the Gradle test projects (remove
useGradleVariants.set(true)
here and here). But I wouldn’t try understanding this too much.
useGradleVariants.set(true)
should be much better
g
isTransitive=fals approach didn’t work. I’m not sure how it would solve this tbh. I guess this means we have to wait for your PR to be merged? Another thing I noticed is,
other-options.json
is missing from
common-module
publication. It exists in
schema-module
publication. I cloned it into
common-module
publication to see if that would cause this but no luck.
I got this working locally by updating
common-models.module
file, and changing all references of
schema-module
to
schema-module-apollo
.
m
PR is merged and should be in SNAPSHOTs in ~20min: https://github.com/apollographql/apollo-kotlin/actions/runs/12349572179
thank you color 1
g
I’m getting
"Impossible to find an AdHocComponent"
error on this. I think that’s because we are using
kotlin-android
plugin?
m
Mmm probably yes...
g
Do you have any idea how we can navigate this? Is there a fix swe can make, similar to what did above, which is to add apollo publication to
.module
file?
m
Looking into this, we might be able to expose the variants directly so you can add them to the android publication
Taking a step back, would using a plain JVM module be an option?
Android adds a lot of complexity
Apologies for the back and forth, the publication APIs are quite something
g
I don't think we can switch it. It is in a central module shared by multiple apps and changing that module would bring more complexities. Especially not in the middle of v4 migration lol it is already complex as is, with PRs across 10 repos :)
👍 1
m
10 repos mind blown
Out of curiosity, why is it that you split accross multiple repos? Several apps? Build times? Something else?
g
Multiple apps, plus core and shared repos that serve them, plus legacy core and feature modules lol
🙂 1
👍 1
Now getting. Not sure what causes this, still debugging
Copy code
Software component 'org.jetbrains.kotlin.gradle.plugin.mpp.KotlinTargetSoftwareComponentImpl@624ec2c8' is not an instance of AdhocComponentWithVariants
m
If you’re using KMP, there’s no way to add to the existing publication, see https://youtrack.jetbrains.com/issue/KT-58830
So we’re back to the
isTransitive = false
workaround
g
We are not. That's why I'm confused. IsTransitivr didn't work either. I'll try to debug what else I can find. But I don't understand how would preventing transitive dependency would prevent this?
m
How are you using
outgoingVariantsConnection
?
I made us a small reproducer project here. It'll be easier to discuss with actual code: https://github.com/martinbonnin/apollo-publishing
g
Looks like it was related to a flavour we had. One of the flavours is showing up as MPP. I got it to build and publish and I will test consumer side next. Let me try this and if it doesn't work we can work over the reproducer
👍 1
m
Sounds good 👍
Also maybe MPP != KMP? I'm not 100% sure
g
I compared with the producer and I don’t see difference in setup. Except, our modules are Android libraries but we are manually publishing Jar from them, because one of the consumer apps has java libraries and it requires jar. I tried get around this by publishing both JAR and AAR but
.module
points to
.aar
so it is still same issue. I will need to find a way to get this variants added to JAR publication I think. Let me know if you know any easy way to do this.
m
our modules are Android libraries but we are manually publishing Jar from them, because one of the consumer apps has java libraries and it requires jar
Would a regular jar do for this library? (no ressources, no Android specific APIs?) If yes, which seems to be the case since your consumer can consume pure jar files, I wouldn't bother with the Android plugin at all in the first place
g
I’m trying to entangle this, but I think some modules are consuming it as aar, and some others are consuming it as jar. That’s why we have it as Android, but we publish .jar
m
Do you need aar? Using jar not only will simplify your build logic but also speed up your build
g
We thought we did, but looks like we don't. The only thing is, this depends on another aar and we need to convert both to Java library because Java library cannot depend on Android library. It will create more work. If we can get this variant data to jar manually that would be the quickest solution to unblock the v4 migration.
m
Can you modify the reproducer to publish jar + aar?
👀 1
g
I modified it to publish jar and got the same error. https://github.com/martinbonnin/apollo-publishing/pull/1
m
I pushed a "fix" here
It's still failing because the jar in
intermediates/aar_main_jar/release/classes.jar
doesn't use Gradle metadata and is therefore not exposing its dependencies (not generated code)
What the fix is doing is it is publishing the apollo metadata as a separate publication, like it was the case in v3
But IMO, it'd be easier to just publish everything as a plain jar, without AGP
g
That seems to be the only option. I tried the fix, the fix works fine for Metadata module, but the generated type publication is failing because of missing Gradle metadata like you said. It looks like the only option is to convert them to Java libraries
From the changes you made, which ones will be merged int? Is metadata publication still merged with the types?
m
Can you elaborate?
With the change, there are 2 publications, the apollo one and the java one
The java one is missing the dependencies in the .pom/.module file
That's because you're adding the jar directly and not the outgoing variant
g
Yes, that part is clear. So we will work on converting them to Java libraries. I was asking about the change you made to the plugin, combining metadata and types. Will it be merged and make it to next release or will you revert the changes and we will have 2 separate publications.
m
I don't think I made any change to the plugin in the reproducer?
Oh yes, the enableGradleVariant stuff?
nod 1
This is merged already
👍 1
Planned to be released today
g
Cool thanks! I will see how it will go and keep you posted
thank you color 1
Hey Martin, with Java library do you know if it is possible to publish different variants? In Android library we had Stage/Prod variants mapped to different Services, but we will lose this with Java library. I tried messing around with sourcesets and publishing output of the sourceset but that causes the same problem, it misses Gradle metadata. Next I will try connecting sourceset to the component, I think that’s the way to do it Update: We can get away with publishing by only publishing Prod variant, but I think this will be critical for main repo. When the devs change build flavour of the app from Prod to Stage we want to be able to point to matching types.
m
It's probably doable. Maybe look at how KGP publishes variants for different Gradle versions. Probably requires a bunch of Gradle magic...
If it were me, I would probably publish 2 different artifacts
g
Yeah it does...I've been lost in the Gradle jungle past few days and I think I'm getting close. Trying the registerFeature now. Publishing is fine, we can only publish prod schema. Where this is important is in the main multi module repo. I don't know if we can achieve this unless we create two separate modules
> Maybe look at how KGP publishes variants for different Gradle versions I think it is using the same thing I mentioned about, registerFeature functionality a.k.a Feature Variants. I set it up in the reproducer repo. I have 2 features of the Java and I can depend on them. But I don’t think Apollo Kotlin plugin can depend on it. In the app module this is how you add it as dependency:
Copy code
add("prodApi", project(":schema")) {
        capabilities {
            requireCapability("com.schema:prod")
        }
    }

    add("stageApi", project(":schema")) {
        capabilities {
            requireCapability("com.schema:stage")
        }
    }
But in Apollo config
dependsOn(project(":schema"))
doesn’t capture the capability. If Java only library is Apollo’s recommendation, how is this not handled I’m surprised. I would think it is a common use case to have a service per flavour.
I think this was caused because I didn’t set srcDir() to separate directory for Stage schema. Making more progress now…
m
In theory you should be able to do something like so:
Copy code
dependencies {
  listOf("Prod", "Staging").forEach { variant ->
    listOf("Ir", "OtherOptions", "CodegenMetadata", "CodegenSchema").forEach { configuration ->
      add("apollo${variant}${configuration}", project(":schema")){
        capabilities {
          requireCapability("com.schema:schema-${variant.lowercase()}")
        }
      }
    }
  }
I tried it here but for some reason Gradle is not happy:
Copy code
Could not determine the dependencies of task ':fragments:generateProdApolloSources'.
> Could not resolve all dependencies for configuration ':fragments:apolloProdCodegenMetadataResolvable'.
   > Could not resolve project :schema.
     Required by:
         project :fragments
      > No matching variant of project :schema was found. The consumer was configured to find a component for use during 'CodegenMetadata', as well as attribute 'com.apollographql.direction' with value 'Upstream', attribute 'com.apollographql.service' with value 'prod' but:
          - Variant 'apiElements':
              - Incompatible because this component declares a component for use during compile-time and the consumer needed a component for use during 'CodegenMetadata'
              - Other compatible attributes:
                  - Doesn't say anything about com.apollographql.direction (required 'Upstream')
                  - Doesn't say anything about com.apollographql.service (required 'prod')
          - Variant 'apolloProdCodegenMetadataConsumable' declares a component for use during 'CodegenMetadata', as well as attribute 'com.apollographql.direction' with value 'Upstream', attribute 'com.apollographql.service' with value 'prod'
Why on earth doesn't Gradle want to use
apolloProdCodegenMetadataConsumable
is beyond me. Can you use 2 different modules instead? •
:schema-prod
:schema-stage
g
I finally got it working. Tomorrow I'll clean up and push to producer repo you can take a look. I'm in the phone now. I haven't tested publishing yet but maybe I can get around that by having multiple publications for each or prod only
🎉 1
m
Nice!
I'll revert my latest changes to the reproducer
g
No it can stay. I'll take a look how you did as well. I'll push to my fork and send to you
👍 1
Sorry this took long because we decided to go with Android modules and converted remaining Java modules to Android. Although we got it working, the setup didn’t look great and it would have maintainability cost. I still pushed my commit and compared with yours. Seems like we both did the same thing, however, I think you missed defining the capability. You are requesting it without creating and it cannot resolve it. And finally migrated to v4! Thanks for all the support!
🎉 1