ajoberstar / reckon

Infer a project's version from your Git repository.
Apache License 2.0
185 stars 28 forks source link

NPE: Must provide a scope supplier #147

Closed aesteve closed 2 years ago

aesteve commented 3 years ago

Hello and thanks for reckon. Been using it for a while and it really really helps managing versions.

On some project, I'm trying to upgrade kotlin JVM plugin from 1.3.72 to 1.4.10 and get the following error:

* What went wrong:
Failed to apply plugin [id 'org.jetbrains.kotlin.jvm']
> Must provide a scope supplier.

I probably did something wrong, but everything was working completely fine with 1.3.72, any idea?

Thanks a lot.

aesteve commented 3 years ago

I seem to have the same error by upgrading Gradle from 6.6 to 6.7.

An exception occurred applying plugin request [id: 'org.gradle.java-test-fixtures']
> Failed to apply plugin 'org.gradle.java-test-fixtures'.
   > Must provide a scope supplier.
aesteve commented 3 years ago

Relevant (maybe) info:

reckon { scopeFromProp() stageFromProp("milestone", "rc", "final") }

grolljak commented 3 years ago

I am experiencing the same issue! I am using (among others) these plugins:

kotlin("jvm") version "1.3.72"
kotlin("plugin.jpa") version "1.3.72"
kotlin("plugin.spring") version "1.3.72"
kotlin("kapt") version "1.3.72"

When I switch to version 1.4.0 upon reimport of gradle projects I get this weird exception

org.gradle.api.ProjectConfigurationException: A problem occurred configuring root project '<name-of-the-project>'. 
Caused by: java.util.NoSuchElementException: Collection is empty.
    at kotlin.collections.CollectionsKt___CollectionsKt.single(_Collections.kt:534)
    at org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinTarget.sourcesJarArtifact(kotlinTargets.kt:200)
    at org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinTarget.sourcesJarArtifact$default(kotlinTargets.kt:188)
    at org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinTarget$kotlinComponents$2.invoke(kotlinTargets.kt:78)
    at org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinTarget$kotlinComponents$2.invoke(kotlinTargets.kt:36)
    at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
    at org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinTarget.getKotlinComponents$kotlin_gradle_plugin(kotlinTargets.kt)
    at org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPluginKt$applyUserDefinedAttributes$1.invoke(KotlinMultiplatformPlugin.kt:320)
    at org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPluginKt$applyUserDefinedAttributes$1.invoke(KotlinMultiplatformPlugin.kt)
    at org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginKt$whenEvaluated$1.execute(KotlinMultiplatformPlugin.kt:213)
    at org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginKt$whenEvaluated$1.execute(KotlinMultiplatformPlugin.kt)

When I try to gradle clean or build I get the already mentioned exception:

An exception occurred applying plugin request [id: 'org.jetbrains.kotlin.jvm', version: '1.4.0']
> Failed to apply plugin 'org.jetbrains.kotlin.jvm'.
   > Must provide a scope supplier.

Could you guys have a look? We would like to use some features of 1.4.0.

grolljak commented 3 years ago

Hey @aesteve I solved the problem by reordering the plugins:

    kotlin("jvm") version "1.4.10"
    kotlin("plugin.jpa") version "1.4.10"
    kotlin("plugin.spring") version "1.4.10"
    kotlin("kapt") version "1.4.10"
    id("org.ajoberstar.reckon") version "0.13.0"

More specifically I put the reckon plugin after kotlin plugins.

aesteve commented 3 years ago

Glad you figured this out for your use-case. It doesn't work for me though :( that's the order I had already. It may be related to the fact that I'm having a multi-module build.

renato-zannon commented 3 years ago

@aesteve I had the same issue in a multi-project build. For me, the issue was that the subprojects block was located (on gradle.build), before the reckon {} block, and apparently the kotlin plugin now needs access to project.version, before reckon is ready to provide it. This seem to match the situation on this test

I was able to fix it just by moving the reckon configuration block right below the plugins {} block, before subprojects

aesteve commented 3 years ago

Thanks a lot for your help. I'm still not managing to find the right order for the plugins block. In my case, that's the java-test-fixtures which causes these issues.

I tried many many different orders but no one seems to fix the issue with java-test-fixtures, unfortunately.

Edit: note, no matter the order in plugins block, or where I put the reckon block, it's always gonna be this java-test-fixtures plugin that's gonna be causing issues. Edit²: it always fails in the plugins block of the root project where I declare the java-test-fixtures, not where I use it. Which definitely sounds like a bug in this plugin.

moaxcp commented 3 years ago

I have the same issue here but with a normal java project. This is with a project using the java-test-fixtures plugin.

moaxcp commented 3 years ago

I just tried this again with gradle 6.8.3. This is the stack trace I'm getting. It looks like reckon is being configured when the test-fixture-plugin is applied. This is before the extension has been setup and the scope supplier ends up being null.

Caused by: java.lang.NullPointerException: Must provide a scope supplier.
    at org.ajoberstar.reckon.core.Reckoner$Builder.build(Reckoner.java:250)
    at org.ajoberstar.reckon.gradle.ReckonExtension.reckonVersion(ReckonExtension.java:79)
    at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:167)
    at org.ajoberstar.reckon.gradle.ReckonPlugin$DelayedVersion.toString(ReckonPlugin.java:87)
    at org.gradle.api.internal.artifacts.ProjectBackedModule.getVersion(ProjectBackedModule.java:42)
    at org.gradle.api.internal.artifacts.dsl.PublishArtifactNotationParserFactory$FileProviderNotationConverter.parseType(PublishArtifactNotationParserFactory.java:127)
    at org.gradle.api.internal.artifacts.dsl.PublishArtifactNotationParserFactory$FileProviderNotationConverter.parseType(PublishArtifactNotationParserFactory.java:111)
    at org.gradle.internal.typeconversion.TypedNotationConverter.convert(TypedNotationConverter.java:43)
    at org.gradle.internal.typeconversion.CompositeNotationConverter.convert(CompositeNotationConverter.java:34)
    at org.gradle.internal.typeconversion.NotationConverterToNotationParserAdapter.parseNotation(NotationConverterToNotationParserAdapter.java:31)
    at org.gradle.internal.typeconversion.JustReturningParser.parseNotation(JustReturningParser.java:40)
    at org.gradle.internal.typeconversion.ErrorHandlingNotationParser.parseNotation(ErrorHandlingNotationParser.java:48)
    at org.gradle.api.internal.artifacts.configurations.DefaultConfigurationPublications.artifact(DefaultConfigurationPublications.java:155)
    at org.gradle.api.plugins.jvm.internal.DefaultJvmPluginServices$DefaultElementsConfigurationBuilder.build(DefaultJvmPluginServices.java:335)
    at org.gradle.api.plugins.jvm.internal.DefaultJvmPluginServices.createOutgoingElements(DefaultJvmPluginServices.java:245)
    at org.gradle.api.plugins.jvm.internal.DefaultJvmVariantBuilder.build(DefaultJvmVariantBuilder.java:192)
    at org.gradle.api.plugins.jvm.internal.DefaultJvmPluginServices.createJvmVariant(DefaultJvmPluginServices.java:270)
    at org.gradle.api.plugins.JavaTestFixturesPlugin.lambda$apply$1(JavaTestFixturesPlugin.java:59)
    at org.gradle.api.internal.plugins.DefaultPluginManager$2.execute(DefaultPluginManager.java:248)
    at org.gradle.api.internal.plugins.DefaultPluginManager$2.execute(DefaultPluginManager.java:245)
    at org.gradle.configuration.internal.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:100)
    at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:95)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
    at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
    at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:92)
    at org.gradle.api.internal.DefaultDomainObjectCollection.all(DefaultDomainObjectCollection.java:163)
    at org.gradle.api.internal.plugins.DefaultPluginManager.withPlugin(DefaultPluginManager.java:251)
    at org.gradle.api.plugins.JavaTestFixturesPlugin.apply(JavaTestFixturesPlugin.java:58)
    at org.gradle.api.plugins.JavaTestFixturesPlugin.apply(JavaTestFixturesPlugin.java:46)
    at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
    at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:51)
    at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:177)
    at org.gradle.api.internal.plugins.DefaultPluginManager.access$100(DefaultPluginManager.java:51)
    at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:272)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
    at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
    at org.gradle.api.internal.plugins.DefaultPluginManager.lambda$doApply$0(DefaultPluginManager.java:157)
    at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:43)
    at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:156)
    ... 145 more
moaxcp commented 3 years ago

This is what I did to fix the issue. I moved 'java-test-fixtures to be applied after the extension is configured.

plugins {
    id 'org.ajoberstar.reckon' version '0.13.0'
    id 'java-library'
    id 'io.freefair.lombok' version '5.3.0'
    id 'org.unbroken-dome.test-sets' version '3.0.1'
    id 'org.sonarqube' version '3.1.1'
    id 'com.github.moaxcp.x11protocol'
    //id 'java-test-fixtures'
    id 'maven-publish'
    id 'signing'
    id 'jacoco'
}

group = 'com.github.moaxcp.x11'
description = 'An x11 client implemented in java'

reckon {
    scopeFromProp()
    snapshotFromProp()
}

apply plugin: 'java-test-fixtures'
chadlwilson commented 2 years ago

Hmm, maybe #170 is duplicate but it only started happening for me after a Gradle 7.4 upgrade, with jacoco.

I have no idea how determinate it is in Gradle; but just changing the order in the plugins block seemed enough to go from working to broken. 😬

ajoberstar commented 2 years ago

The general problem here is likely other plugins that are too eagerly trying to get the version from project.getVersion().toString(). Reckon's approach is only quasi-supported by Gradle by them declaring the type of project.getVersion() as Object instead of String. Nowadays it should really be a Provider<String> which would work better with how Reckon wants to behave.

I'm not sure if it's resolvable by Reckon, since it almost looks like a plugin trying to access the version before the reckon {} extension block runs, which really seems like a bug in another plugin. I could be misinterpreting here though.

I would be curious if 0.14.0 works any better for those of you with this issue. The fundamental approach is unchanged, but it more deeply uses BuildService and Property/Provider that before.

chadlwilson commented 2 years ago

Thanks @ajoberstar.

For what it's worth, the call in Jacoco's case (built-in Gradle plugin maintained by Gradle team) is triggered during apply at this line here, where it calls ConfigurationPublications.artifact(notation) which internally inside Gradle eventually comes to project.getVersion().toString() at this line here.

The full stack is in #170 if you're curious.

I am wondering, if using the plugins { } block, is there some kind of determinism here that might indicate the "safest" place for Reckon to be in the list (the start? the end? or it's a hash and un-ordered)

chadlwilson commented 2 years ago

I would be curious if 0.14.0 works any better for those of you with this issue. The fundamental approach is unchanged, but it more deeply uses BuildService and Property/Provider that before.

Also, after upgrade to 0.14.0 it still breaks in the same place with my simple reprod on #170, just with a slightly different Reckon stacetrace.

ajoberstar commented 2 years ago

I'm not clear on the determinism of the plugin block, but if reckon is applied after jacoco, it would work because reckon wouldn't have taken over the version yet. Unless something else comes along to correct the version on that artifact later, it likely has a null version because Gradle's still processing plugins and hasn't reached your real buildscript logic yet.

If reckon is applied earlier than jacoco you would get this error because reckon has reassigned the version to DelayedVersion, but again Gradle hasn't processed your build logic yet so you haven't configured how reckon should behave and it bombs.

Without Gradle making changes to make evaluation of version more lazy, I'm not positive if there's any way to resolve this generally. A partial idea is that maybe having reckon applied as a settings plugin instead of a project plugin could allow it to inject it's logic in earlier. I've never written a settings plugin, but might be worth exploring.

chadlwilson commented 2 years ago

Interesting, thanks for the reply. At least it's clear that doing something to try and affect the order of plugin loading is a workaround.

It's weird that simplify moving from Gradle 7.3.3 to 7.4 broke it for me, but perhaps they changing something in the ordering of plugin loads there...

ajoberstar commented 2 years ago

Re: @x80486 in #170 (which is a duplicate of this discussion)

Do you think there could be a resolution for this or would it be like that because the issue with the plugins block?

I do have other plugins in use and as soon as I start using the apply plugin way (even with Reckon only), they start failing as well; for instance, this is one of them: com.github.jakemarsden.git-hooks — unable to get the version number.

I guess it's around the same subject all this.

I have a handful of thoughts around this:

  1. If reckon could configure the version even earlier, it could be done in time for other plugins that "behave badly" in toStringing version eagerly to not notice. This could maybe be done with a Plugin<Settings>, which aren't common and I've not used before, but should be early enough to stop any normal Plugin<Project> from interfering. It would also reinforce the idea that reckon is setting a build-wide version not a project-specific version.
  2. Reckon could be a little more forgiving. If you haven't configured the reckon block yet, but something needed the version, just return "unspecified" or whatever the default version is, log a warning to the console, and let someone try again later. Currently, it's trying to be safe in the sense that if you wanted reckon involved you wanted its version to be used. If something is calling version.toString() before the reckon {} has run its going to get the wrong version regardless, because it's probably running before the user had a change to write version = 'whatever' if they weren't using reckon. Maybe that caller doesn't need the version for an important purpose or maybe the plugin or you will go correct it later anyway. However, that's utlimately a problem with the other plugin, and not something reckon really needs to play traffic cop on. The logging should help trace down the offending plugin in case tweaks need to be made there.
  3. Gradle could make the implicit laziness of project.getVersion() explicit by using Property<String>. They have issues open for this in gradle/gradle#13672 and gradle/gradle#15088. Even if they fix this, it will take time for plugins to be updated to use the new method. Since plugins could address this currently anyway, it doesn't replace the need for 1 or 2.
  4. I've seen another case of this error happen where it was actually covering up a different problem. Another failure was happening, but somehow in the chain it needed to access the version, and the reckon exception buried the real problem. Usually in this case, disabling reckon would help you see the real exception. Not applicable to all cases.
  5. Ultimately, the other plugins need to reckon with (pun intended) how they use project.getVersion() and likely make changes to access it more lazily. Again 3 would help them with this, but its not necessary technically.

I'm currently working on updates to other plugins, so this isn't the top of my list, but I do plan to come back to this. And I think both 1 and 2 are approaches I want to pursue.

ajoberstar commented 2 years ago

Issues #173 and #174 are open for the two items I listed above. Once #174 is implemented, I'll close this issue.

ajoberstar commented 2 years ago

The settings plugin org.ajoberstar.reckon.settings solution is available in 0.17.0-beta.2. I think this will provide a more reliable way to ensure that reckon runs early enough.