Search code examples
javajenkinsgradlegrailsgroovy

Grails tests launched from Jenkins grails plugin doesn't pick up jars generated by gradle build


I have a large project that I'm helping migrate to Grails 3 and Gradle from Grails 2.5. In the short term, we are moving much of the business code into framework-agnostic gradle modules and then importing them to grails 2.5, eventually planning to move to a fully grails-3 build.

The migration approach plan is to:

  • Create gradle modules and move most of the code into these.

  • Have grails execute the gradle build

  • Gradle puts the result of each module's build into the /lib directory

  • Grails finds them on the classpath and uses them

  • I don't specify the dependencies in BuildConfig.groovy, I simply use the classes in the grails code and everything turns out to be there by the compile phase of app startup.

This works fantastically locally. I can start with a fresh checkout of the project from source control, execute grails run-app --refresh-dependencies (or test-app), and see the result of my build. I've found that--refresh-dependenciesis a magic grails voodoo that tells grails that it might find some classes it needs in itslib/` directory.

This does not work on Jenkins. I am using the Jenkins Gradle plugin. Jenkins can't resolve my (single, POC) class:

   [groovyc] /mnt/jenkins/workspace/Java8/Steps/MyApp-Grails-2.5.2-Java8-Pull-Request-Unit-Tests
    /src/groovy/com/myapp/package/SomeServiceWithADependency.groovy: 
    10: unable to resolve class com.myapp.module.util.MyUtil
   [groovyc]  @ line 10, column 1.
   [groovyc]    com.myapp.module.util.MyUtil
   [groovyc]    ^

Here's what I've tried:

  • I've checked the box for Jenkins to add the --refresh-dependencies flag to all build cycle commands

  • There are 3 other jars in the lib/ directory, checked into git source control. Grails finds those without a problem.

  • I've added a print statement to the end of the gradle module build to tell me what's in the /lib directory, and I both the dependencies gradle is generating and the ones checked into source control. They seem to be there and the path matches the expected workspace path.

Any ideas? Hoping someone on planet earth has dealt with a similar problem


Solution

  • We solved the issue. The build was working locally because of gradle's caches and was failing remotely because Jenkins was working with a fresh repository.

    Given the requrirements of starting with a clean pull of the git repo, executing a single grails command that would build the gradle modules and pull in the dependencies, our solution was to use the gradlew shell script to build gradle first, failing if the gradle build failed:

    (exec ./modules/gradlew -p modules build jar publishToMavenLocal);  gradlew_return_code=$?
    
    if [ "$gradlew_return_code" -eq "0" ]; then
        echo "Gradle build task success!"
    else
        echo "Gradle build task failed with return code $gradlew_return_code; aborting."
        exit -1
    fi
    

    Downsides are:

    • All developers working on the project must use ./grailsw (instead of grails) to execute any grails tasks, and all jenkins builds have accordingly been modified.

    • Any transitive dependencies inside any module must also be added to BuildConfig.groovy so that grails puts them on the classpath at runtime. Failure to add a dependency in BuildConfig.groovy will not throw a compile-time error but will blow up with no class def at runtime.

    However the benefits of a modularized build, especially the discipline it is already bringing to code organization and the comforts of knowing a big part of our application code is framework-agnostic, are well worth these inconveniences.

    Hope this helps someone else out there.