Getting started with gradle - kuhl1/kuhl1.github.io GitHub Wiki

This post is about getting comfortable with some of the magic that your build.gradle file is doing, assuming you're working with a gradle project. For a baseline, I'm going to use the gradle file from dev central station as many of the teams at Ford are using EcoBoost or Quartermaster to generate their projects. Disclaimer: I don't claim to be a gradle or groovy expert, only someone who has edited one at least five times.

Introductory:

Gradle Wrapper

Most gradle projects will have a gradle wrapper included, which specifies the version of gradle you would be using. It also allows you to run your CI (continuous integration) pipeline, such as Jenkins, against the exact same gradle version.

! When using a gradle wrapper you will see ./gradlew commands instead of gradle

Basic commands:

There are some built in, basic commands that you can expect to run when working on a project. In general, you run them from the root of the project โ€“ at the same level as the gradlew folder.

Clean build: ./gradlew clean build

Clean build deletes all the previous artifacts, which ensures there are no issues with subsequent builds.

Bootrun: ./gradlew bootRun

There is a default bootrun defined to boot up the application on your localhost. However, if you need a different spring profile you may want to modify the build.gradle to create a custom bootrun task, or to export the profile as part of the task.

bootRun {
args = ["--spring.profiles.active=local"]
}

While the bootrun is active in the terminal, all the system logs will print as they would on the server.

When something goes wrong

If a test is failing when you run the test suite, gradle will give you something like below. It gives you the file where it failed, which test failed, and a short explanation of the error.

image

This is nice when you are validating that all the tests are passing before a commit or merge, but probably not as good as running through the IDE when you are actively working on a class.

Getting more interesting

To really understand what's happening under the hood, try adding the flag dry-run to any gradle command:

./gradlew clean build โ€“dry-run

You will see all the tasks that would have run printed out on the console. This can be especially helpful when trying to debug what is happening when you run a particular task. If you ran the clean build with dry run, you might have noticed that many tasks were executed that were not specified, such as "compileJava". That is because tasks depend on other tasks, and require they complete before starting.
We can also purposefully exclude tasks in the cli command

../gradlew clean build โ€“x test

This command would run clean build, but skip the test task โ€“ although ๐Ÿ˜€ I am not suggesting you skip tests.

There are also many other modifiers we can add, such as refresh dependencies. By default, gradle is going to use the dependencies already downloaded to make the build faster. If you are using IntelliJ, you will see the gradle refresh symbol pop up if you change anything in the build.gradle file because it wants to check if the changes made any difference to the downloaded dependencies. If you are changing versions and need to trigger the refresh manually, that would be the appropriate occasion to use the refresh dependency flag.

../gradlew clean build โ€“refresh-dependencies

Dependency Management

One of the largest responsibilities of build.gradle files is to include dependencies. Some of my favorite examples are when the dependencies are grouped by responsibility, which you can see in the dcs generated projects. A section for logging (sleuth), security, documentation (swagger), metrics (SRE), database, etc.

For the versions, there are a few different strategies. If you don't have specific requirements, you can use the bill of materials (BOM) and let Spring manage the dependencies. You can see the dependency versions that it will select in the Spring documentation. Example shown below, where all the required versions will be supplied from the dependencyManagement section.

apply plugin: 'io.spring.dependency-management' 
dependencyManagement { 
     imports { 
          mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR1' 
          mavenBom 'io.pivotal.spring.cloud:spring-cloud-services-dependencies:2.1.4.RELEASE' 
          mavenBom 'org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES' 
     } 
} 

Alternatively, you can put specific versions on each dependency, but you may get different versions if you include a direct dependency and there is another version included as a transitive dependency.

Multiple build projects

For those of us who have gone down the road of a single repository for multiple applications, you might need to get a bit fancier with your gradle files. Questions start coming up regarding hierarchy of gradle files, building other jars first to include as a dependency, etc.

You will also find out that gradle chooses to build the directories in alphabetical order, so there is a legit suggestion when you google to name the directories such that they build according to the dependency order (like A-first-project-to-buid, B-second-project-to-build) and other fun hacks. We ended up solving this issue in our own project using a combination of compile project and fileTree commands.

Clean Code โ€“ Everywhere

When It's time for Spring cleaning (ha pun) try not to neglect your poor gradle files. There is no visual indicator (at least that I am aware of) to let you know that you are importing a whole dependency that is completely unused. Many times we start including plugins, dependencies, and custom tasks and forget to remove them when the functionality has been removed. The smaller you can keep these files, the easier it will be to do maintenance.

Here is one of my favorite hair-pulling/chuckle stories. Sometime last year, one of the developers realized we were calling some extra tasks during our build process to create a zip file and tar file, presumably for publishing to a library. However, we are not using that functionality, so we all got in to the habit of creating an alias or typing ./gradlew clean build โ€“x distZip -x distTar because it would save a couple of minutes during builds. Fast forward to recently, someone pointed out that we didn't need to include the plugin in our gradle that was creating those files!

Staying Compliant

The brightside of working with a gradle project is that when your Product Manager asks for a list of versions used in the project, you can essentially send them a copy of your build.gradle files. This makes checking for deprecated versions very simple.