gradle-nexus / publish-plugin

Gradle plugin for publishing to Nexus repositories
Apache License 2.0
401 stars 29 forks source link

Isolation in multi-project build #81

Open flelli opened 3 years ago

flelli commented 3 years ago

Hi,

I have a polyglot multi-project build where sub projects are isolated by technology. Say I have 2 subprojects:

The reason I do this is also because I want the tools and plugins to be isolated. With the former 2 plugins I had them applied only in the modules:java along with Maven, the publishing, etc but the new one fails to apply if not declared in the root project:

`Failed to apply plugin 'io.github.gradle-nexus.publish-plugin'.

Plugin must be applied to the root project but was applied to :modules:java`

Moving the plugin to the root project also implies declaring all the publishing etc in the root project and that would mess up all the configurations I have, basically giving up on isolation.

Is there a way to keep the new plugin in a sub-project?

marcphilipp commented 3 years ago

Moving the plugin to the root project also implies declaring all the publishing etc in the root project and that would mess up all the configurations I have, basically giving up on isolation.

That's not entirely true. You only need to configure nexusPublishing in the root project but can continue declaring your publications in modules:java.

Is there a way to keep the new plugin in a sub-project?

I'm afraid not. The alternative to a root project plugin would be to convert it into a settings plugin but that wouldn't achieve isolation, either.

flelli commented 3 years ago

Thanks Marc for your prompt reply,

The point is if I move nexusPublishing only, the build breaks as it can't find any Maven publications defined. So I have to move them as well, which will likely need the signing to be moved too, ending up with moving it all.

Moving the plugin to the root project also implies declaring all the publishing etc in the root project and that would mess up all the configurations I have, basically giving up on isolation.

That's not entirely true. You only need to configure nexusPublishing in the root project but can continue declaring your publications in modules:java.

marcphilipp commented 3 years ago

@flelli It shouldn't break without any defined publications. Could you please create a reproducer project?

flelli commented 3 years ago

Sure. In my project I created two additional branches:

As soon as I run Gradle using the plugin in the root project, I get:

> Could not find method publishing() for arguments [build_3ihi3zopyxie2mmev6s23q6n5$_run_closure1$_closure3@7619cca7] on root project 'nyx' of type org.gradle.api.Project.

As you can see in the modules/java/build.gradle, publishing is statically defined for the modules/java project but also as a closure on its (further) child projects

vlsi commented 3 years ago

The root project only requirement breaks Gradle's precompiled script plugins :-/

> Task :build-logic:root-build:generatePrecompiledScriptPluginAccessors
Failed to generate type-safe Gradle model accessors for the following precompiled script plugins:
 - src/main/kotlin/allure-gradle.build-logic.root-build.gradle.kts

org.gradle.internal.exceptions.LocationAwareException: Precompiled script plugin '/Users/vlsi/Documents/code/allure-gradle/build-logic/root-build/src/main/kotlin/allure-gradle.build-logic.root-build.gradle.kts' line: 1
An exception occurred applying plugin request [id: 'io.github.gradle-nexus.publish-plugin']
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugin(DefaultPluginRequestApplicator.java:183)
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.lambda$applyPlugins$1(DefaultPluginRequestApplicator.java:125)
        at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugins(DefaultPluginRequestApplicator.java:125)
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugins(DefaultPluginRequestApplicator.java:121)
        at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.SyntheticProjectSchemaBuilder.applyPluginsTo(GeneratePrecompiledScriptPluginAccessors.kt:446)
        at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.SyntheticProjectSchemaBuilder.childProjectWith(GeneratePrecompiledScriptPluginAccessors.kt:388)
        at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.SyntheticProjectSchemaBuilder.schemaFor(GeneratePrecompiledScriptPluginAccessors.kt:374)
        at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors.projectSchemaImpliedByPluginGroups(GeneratePrecompiledScriptPluginAccessors.kt:296)
        at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors.generateTypeSafeAccessorsFor(GeneratePrecompiledScriptPluginAccessors.kt:158)
        at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors.generate(GeneratePrecompiledScriptPluginAccessors.kt:139)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
...
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.gradle.api.plugins.InvalidPluginException: An exception occurred applying plugin request [id: 'io.github.gradle-nexus.publish-plugin']
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.exceptionOccurred(DefaultPluginRequestApplicator.java:198)
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugin(DefaultPluginRequestApplicator.java:180)
        ... 141 more
Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin 'io.github.gradle-nexus.publish-plugin'.
        at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:163)
        at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:127)
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.lambda$applyPlugins$0(DefaultPluginRequestApplicator.java:127)
        at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugin(DefaultPluginRequestApplicator.java:176)
        ... 141 more
Caused by: java.lang.IllegalArgumentException: Plugin must be applied to the root project but was applied to :test
        at io.github.gradlenexus.publishplugin.NexusPublishPlugin.apply(NexusPublishPlugin.kt:48)
        at io.github.gradlenexus.publishplugin.NexusPublishPlugin.apply(NexusPublishPlugin.kt:40)
        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)
        ... 144 more
JavierSegoviaCordoba commented 1 year ago

This probably break the future Gradle project isolation support, right?

What if the plugin must be applied to each project?

sureshg commented 1 year ago

@vlsi hi, could you able to find any workaround for pre-compiled script plugins (organized as part of build-logic)? I ended up using something like this

if (project == rootProject) {
  apply(plugin = "io.github.gradle-nexus.publish-plugin")
}
mauritssilvis commented 1 year ago

Like @flelli, I bumped into the following problem:

An exception occurred applying plugin request [id: 'io.github.gradle-nexus.publish-plugin', version: '1.3.0']
> Failed to apply plugin 'io.github.gradle-nexus.publish-plugin'.
   > Plugin must be applied to the root project but was applied to :other-project

This problem occurred when I had two projects I wanted to publish independently using the Gradle nexus publish plugin. One project depended on the other, though: it was a multi-build project through the setting include("other-project") and the dependency implementation(project(":other-project")).

Following @sureshg's suggestion, I found two workarounds for the above problem:

For future reference, I'll post both (most likely imperfect but functioning) workarounds below.

Conditional plugin application

Main project

settings.gradle.kts:

// Include the other project as a project
include("other-project")
project(":other-project").projectDir = file("path/to/other/project")

build.gradle.kts:

plugins {
    id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
}

dependencies {
    // Reference the other project
    implementation(project(":other-project"))
}

nexusPublishing {
    repositories {
        // ...
    }
}

Other project

build.gradle.kts:

plugins {
    // Do not apply the plugin
    id("io.github.gradle-nexus.publish-plugin") version "1.3.0" apply false
}

// Conditionally apply and configure the plugin
if (project == rootProject) {
    apply(plugin = "io.github.gradle-nexus.publish-plugin")

    configure<io.github.gradlenexus.publishplugin.NexusPublishExtension> {
        repositories {
            // ...
        }
    }
}

Composite build

Main project

settings.gradle.kts:

// Include the other project as a build
includeBuild("path/to/other/project")

build.gradle.kts:

plugins {
    id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
}

dependencies {
    // Reference the other project using its project coordinates
    implementation("<other project group>:<other project name>:<other project version>")
}

nexusPublishing {
    repositories {
        // ...
    }
}

Other project

build.gradle.kts:

plugins {
    id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
}

nexusPublishing {
    repositories {
        // ...
    }
}
Vampire commented 10 months ago

@marcphilipp would you mind listing the actual reasons why the plugin has to be a root or settings plugin? Just to make it clear why this is the case, and for the case that someone wants to come up with a PR that maybe changes this. The afterEvaluate { allprojects { ... } } is a double-bad-practice anyway and is better replaced by something different anyway. :-D

marcphilipp commented 6 months ago

I originally made it a root project plugin so there's a single place to configure Nexus repositories that are shared across subprojects. While each subproject has an "init" task, they use a resource attached to the project to synchronize on to ensure a shared staging repo is created. This could probably be changed to create a checksum of the repo config or require some kind of identifier and a BuildService but I lack the time to give it a try.