gradle-release icon indicating copy to clipboard operation
gradle-release copied to clipboard

GradleBuild is not compatible with composite builds

Open rupebac opened this issue 5 years ago • 12 comments

I know the reasons why you used this GradleBuild:

https://github.com/researchgate/gradle-release/issues/62

But we had a great surprise when we tried to release our recently new stuff using composite builds. GradleBuild is not supporting them:

https://github.com/gradle/gradle/issues/8246

This is a huge limitation. Composite Builds are receiving everytime more attention.

We have managed to workaround it by splitting it in 3 phases and calling it from different build cycles from the pipeline:

phase 1:

task releaseSym_1 {
    dependsOn "createScmAdapter"
    dependsOn "initScmAdapter"
    dependsOn "checkCommitNeeded"
    dependsOn "checkUpdateNeeded"
    dependsOn "checkoutMergeToReleaseBranch"
    dependsOn "unSnapshotVersion"
    dependsOn "confirmReleaseVersion"
    dependsOn "checkSnapshotDependencies"
}

phase 2: then we call our build stuff

phase 3:

task releaseSym_2 {
    dependsOn "createScmAdapter"
    dependsOn "initScmAdapter"
    dependsOn "preTagCommit"
    dependsOn "createReleaseTag"
    dependsOn "checkoutMergeFromReleaseBranch"
    dependsOn "updateVersion"
    dependsOn "commitNewVersion"
}

I also have to rework the "mustRunAfter" to guarantee the order.

In the end it is kind of working, but it is not nice at all. Would you be willing to accept Pull request and work on it together?

rupebac avatar Jul 24 '19 10:07 rupebac

Why do you try to use composite builds for this and not a multi project setup? I think composite builds are not really made for this as they just shall help to develop in multiple projects. In multi project setups you explicitly specify the dependency in a project but for composite builds you get a drop in replacement of the referenced projects and their sub projects. How do you handle the difference of the declared dependencies and the referenced project? Because you might specify projectX with version 1.0.0 as a compile dependency but the composite build build brings in version 2.0.0 and if you don't update declaration the build is not really reproducible except you look into the pom.xml (I think it contains the replaced version) to know which version was really used at build. We actually have a check that doesn't allow releases with composite build setups because of that.

Hillkorn avatar Jul 29 '19 09:07 Hillkorn

@Hillkorn sorry I recover this topic now, just forgot about it. I think I get what you mean, while I would agree with you a few years ago, things have evolve in a different direction. The problem you describe is not intrinsic with composite builds, it is already happening in multi project setups. You may be depending on junit:4.2, and that having a non strict version requirement on, commons-lang for instance. something like ":commons-lang3:latest.release". Am I understanding you correctly?

This is why you have dependency locking in gradle: https://docs.gradle.org/current/userguide/dependency_locking.html

And also, keep in mind the new concept of "platforms" in Gradle. A composite build can share one platform (which we do), so all version requirements are actually "shared", so one project included in the composite build cannot violate any of our version rules.

rupebac avatar Oct 07 '19 13:10 rupebac

This is really a problem. I also agree that times where composite build were just for drop-in replacement are long gone. Composite builds are used for a better buildSrc replacement, they are used for another level of structuring builds, they are used if you use the built-in source dependencies feature, they are used if you use the includegit-gradle-plugin, ....

The last one in the list is for example my current situation. I'm developing three private projects, one is a base for the other too, let's call them common, A, and B. A and B use the includegit-gradle-plugin to get a specific tag of common automatically cloned and included, as there is no private repository where these artifacts are going to be published.

So please fix this somehow.

Vampire avatar Mar 30 '22 20:03 Vampire

Using the tooling api to kick off the build instead of the GradleBuild task type works fine, so that should probably be used instead, it is also what IDEs use, so it is highly likely that it will be compatible with future changes.

For example:

tasks.register("runBuildTasks") {
    doLast {
        GradleConnector
            .newConnector()
            .forProjectDirectory(layout.projectDirectory.asFile)
            .connect()
            .use { projectConnection ->
                projectConnection
                    .newBuild()
                    .forTasks("build")
                    .setStandardInput(System.`in`)
                    .setStandardOutput(System.out)
                    .setStandardError(System.err)
                    .run()
            }
    }
}

That's in Kotlin DSL, but you should be able to easily transform it to Groovy too.

Vampire avatar Mar 30 '22 21:03 Vampire

Yep, this works as consumer-side work-around, replacing the GradleBuild task action by a tooling API call:

configure(listOf(tasks.release, tasks.runBuildTasks)) {
    configure {
        actions.clear()
        doLast {
            GradleConnector
                .newConnector()
                .forProjectDirectory(layout.projectDirectory.asFile)
                .connect()
                .use { projectConnection ->
                    val buildLauncher = projectConnection
                        .newBuild()
                        .forTasks(*tasks.toTypedArray())
                        .setStandardInput(System.`in`)
                        .setStandardOutput(System.out)
                        .setStandardError(System.err)
                    gradle.startParameter.excludedTaskNames.forEach {
                        buildLauncher.addArguments("-x", it)
                    }
                    buildLauncher.run()
                }
        }
    }
}

So it should also work fine if you do it in the plugin right away. You probably need to add a bit more gradle.startParameter to commandline argument translations like -s and -S and so on.

Vampire avatar Mar 30 '22 22:03 Vampire

@Vampire Your suggestion would also help to fix #346 I think. We can get the result inside the task and do the cleanup on failure.

Hillkorn avatar May 31 '22 09:05 Hillkorn

@Vampire Your suggestion would also help to fix https://github.com/researchgate/gradle-release/issues/346 I think. We can get the result inside the task and do the cleanup on failure.

The afterTask part probably, yes.

Vampire avatar May 31 '22 13:05 Vampire

But this would remove the need for the listener as we only listen for failures of the release task to run scmAdapter.revert() if configured.

Hillkorn avatar Jun 01 '22 10:06 Hillkorn

This issue has actually one more implication. From Gradle 7 on the type-safe accessors for Kotlin DSL precompiled script plugins cannot be generated anymore and from Gradle 8 on this results in a hard failure of the build. Here the according Gradle issue: https://github.com/gradle/gradle/issues/23747 If you would not use GradleBuild, but the tooling API as suggested, I think this should also work properly again.

Vampire avatar Feb 02 '23 14:02 Vampire

Or if you would not eagerly create the tasks, according to that issue, but not using GradleBuild would be better of course.

Vampire avatar Feb 10 '23 13:02 Vampire