Save the Wrapper
Evan J B | Apr 12, 2017TL;DR (Abstract)¶
Get started with the "wrapper" implementations for maven and gradle projects. Peruse a sample project on github.
Intro ¶
It's hard to get away from dependencies. Even if you properly list all compile and runtime dependencies for your app, you are still dependent on the build tools themselves.
Is maven installed? How about Java?
This is where the wrappers come in!
The maven and gradle wrappers provide a simple pair of scripts (along with a jar and properties file) that fetch the dependency managment build tool. These scripts are meant to be part of your project's collective source code now just like the build files.
Usage ¶
Let's apply this to a maven-based project first.
Maven Wrapper
The maven wrapper won't create the pom.xml for you so let's add a simple maven project to test building with the wrapper.
mvn archetype:generate -DgroupId=com.evanjbowling.blog -DartifactId=new-project -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
Now add the maven wrapper:
cd new-project && mvn -N io.takari:maven:wrapper
This will result in the following:
new-project └ .mvn/wrapper/maven-wrapper.jar └ .mvn/wrapper/maven-wrapper.properties └ mvnw └ mvnw.cmd └ pom.xml └ src/
Now test it with:
# Unix users ./mvnw clean compile # Windows users mvnw.cmd clean compile
Maven Wrapper in Docker
Our project builds successfully now, but we used a maven installation to run the archetype plugin and create the project in the first place. We can create a simple docker container to create an isolated environment where maven doesn't exist. Create a file called "Dockerfile" in the same directory with the following contents:
FROM openjdk:8 COPY . /usr/src/myapp WORKDIR /usr/src/myapp CMD ["./mvnw clean compile"]
Build the docker image with:
docker build -t my-mvnw-app .
Now run the docker container in interactive mode and start a bash shell:
docker run --rm -it my-mvnw-app bash root@1e2827d18962:/usr/src/myapp# which mvn # no output proves maven isn't installed
Now run the maven wrapper:
./mvnw clean compile
You should see the following output:
[INFO] Installing /usr/src/myapp/pom.xml to /root/.m2/repository/com/evanjbowling/blog/new-project/1.0-SNAPSHOT/new-project-1.0-SNAPSHOT.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 37.066 s [INFO] Finished at: 2017-04-08T01:16:26+00:00 [INFO] Final Memory: 18M/121M [INFO] ------------------------------------------------------------------------
Gradle Wrapper
The maven wrapper above was created by using a plugin. The gradle wrapper is a part of the standard gradle distribution. It can be used with the following command:
gradle init
This creates the following:
new-project └ build.gradle └ gradle/wrapper/gradle-wrapper.jar └ gradle/wrapper/gradle-wrapper.properties └ gradlew └ gradlew.bat └ settings.gradle └ src/
Now execute a build:
./gradlew build
Gradle Wrapper in Docker
Let's use the same Dockerfile as before with a new default command:
FROM openjdk:8 COPY . /usr/src/myapp WORKDIR /usr/src/myapp CMD ["./gradlew build"]
Build the docker image with:
docker build -t my-gradlew-app .
Once again, let's un the docker container in interactive mode and start a bash shell:
docker run --rm -it my-gradlew-app bash
Now run the gradle wrapper:
root@5cdf9749ef83:/usr/src/myapp# ./gradlew build ------------------------------------------------------------ Root project ------------------------------------------------------------ classpath No dependencies BUILD SUCCESSFUL Total time: 16.159 secs
So how far should this go with our buildchain? A Java wrapper? A docker wrapper? I'm not sure but these two wrappers are a great start. The gradle init method is simple, fast and includes the wrapper by default. Ultimately, this provides a more portable build.
Critiques¶
- The maven wrapper is an excellent third-party plugin developed and maintained on Github. Direct integration with the tool would be better.
- Using a wrapper in different sub-projects could lead to different versions of the same build tool being used throughout a product. While this isn't an immediate issue, it is worth thinking about. A workaround would be to simply check the various wrapper projects every so often and update them to the same version.
- The executable jar for pulling the build tool is a good portable solution, but at first glance this is confusing. I've been on code reviews where people thought it was build output being checked in. The only other option that comes to mind is to have an all-script approach - not sure if it really matters that much.
References ¶
- Github
- Project source code: https://github.com/blog-evanjbowling/2-save-the-wrapper
- Maven Wrapper
- Gradle Wrapper