Composite build works locally, but fails on Jenkins

(Thomas Gerner) #1

We use a composite build with six sub projects. The composite build.gradle and the settings.gradle are in the root project and the sub projects are folders of the root project. Their builds are included builds in the root project settings.gradle. If I do a “gradle build” or use the gradle wrapper for build on a shell (windows or linux) the build finish successful and in every subfolder I have a build folder created which contains the created artifacts. However if the build is executed on a Jenkins build server only one build output folder in the root project is created which contians some artifacts and the build fails with random errors. The artifacts build so far are wrong, they don’t contain the expected files. The build sometimes fails because of missing class dependecy (cannot find symbol), error accessing files and other strange things. I have no idea what causes this behavior on Jenkins. I need some hints to figure out why the build behaves different on the Jenkins build server and what I can do that the build does the same thing on the server like on a local shell.
On the Jenkins server I used freestyle jobs and multi pipeline jobs, using gradlew or the Jenkins gradle plugin. All method have the same behaviour. When I call the build on the build server manually in a shell in the Jenkins working directory it succeeds, executed by Jenkins it fails. Any hints are welcome.

(uklance) #2

Is everything stored in a single (git?) repository? Or is each project in it’s own separate source repository?

(Thomas Gerner) #3

Every project is in a own git repository, but they are referenced as sub modules in the root project. The Jenkins build job does a recursive pull. Everything in the sub folders is available. When I go to the build folder on the Jenkins server and execute a build manually it works as expected.
My guess is that Jenkins injects some behaviour in the job, but I don’t know much about gradle to figure out what can change the behaviour.

(uklance) #4

Without seeing the output from both builds, it’s hard to play “spot the difference”

Just a bit of brainstorming

  • The build somehow starts before everything is fetched from git
  • Jenkins is running the build from a different directory than you expect
  • Jenkins running a different task than you expect
  • Jenkins user has a with different settings

(Thomas Gerner) #5

Thank you for your hints.
I did a bit more of investigation and added an printout of the properties (gradle properties), in the main build.gradle:

task properties {
  dependsOn gradle.includedBuilds*.task(':properties')

A ‘gradle properties’ prints the buildDir of the sub folders like this:
buildDir: root/subProject/build
The jenkins job prints on all sub projects:
buildDir: root/build
For me this looks like the gradle jenkins plugin forces setting of the output folder.
I will try to get help from the jenkins gradle plugin development.

(Thomas Gerner) #6

Finally I figured out what happens: Jenkins creates a init script in the gradle home folder init.d containing

        def envBuildDir = System.env.WORKSPACE?.trim()
        gradle.projectsLoaded {
            if (!envBuildDir) return
            configProject(rootProject, envBuildDir)

Then the environment variable WORKSPACE points to the root folder of the project. This configures the build to use a single build directory.

(uklance) #7

The more orthodox approach would be to build each project separately and publish to a repository (eg nexus). Each separate Jenkins build would then reference dependencies from nexus. The separate builds would only be triggered by changes in their own git repository.

Developers could still develop locally with a composite when changes to multiple projects are required. Or use the same nexus dependencies (without a composite) when working on a single project

(Thomas Gerner) #8

This is how we currently build our product. Every component publisches its result to a repository and other components use them. But the disadvantage is that we must manually configure the build order and dendendencies between components.

(uklance) #9

It’s possible the Jenkins groovy DSL could help automating that piece

(Thomas Gerner) #10

I solved the problem by adding a line “unset WORKSPACE” to gradlew. Now the composite build works as expected.
I don’t know the intention of the configProject(…) method of gradle, what it should do. But it breaks the composite build as it forces usage of a single build folder for all sub projects of the composite build.