It would be consistent with most other languages' setups to run gradle build
in a Dockerfile. You may want to set up a multi-stage build so that the first stage has a complete JDK, COPY
s in the build.gradle
and source code, and builds it; and then the second stage only contains the JRE and the jar file COPY --from=
the first stage. (Java is unusual in that it's possible to build a portable jar file outside Docker and COPY
it directly into an image; most other languages either interpret the source directly, or build a native artifact that's not as portable.)
The advantages of doing this are IMHO somewhat minimal. If you have a multi-language environment, being able to git clone; docker build
every service the same way is useful. You will use the JVM specified by the Dockerfile's FROM
line, instead of what's installed on the host, but you will probably need a JVM anyways for day-to-day development and IME the precise patch level of the JRE isn't that important in practice.
I would not run tests in a Dockerfile.
At a philosophical level, they don't really belong there. The Dockerfile has one task: it takes source files or a jar file in, and produces a Docker image out. The Dockerfile system is designed for it to be hard for there to be side effects, so it's very difficult to start a support process or access a database even if you otherwise would want to. Running tests are in some way a "side effect" like this.
Mechanically, it makes sense to run tests both before and after the image build, but not necessarily during. You can break your tests into unit tests and integration tests. The unit tests should only depend on your application code, with mocks for any external dependencies; since they don't depend on anything but the code and the JRE, you should get consistent results whether you run them on the host or in a container, and you can first run unit tests and then only build the image if they pass. The integration tests depend on the entire system, including things you can't connect to from the Dockerfile, and so a good practice here is to run integration tests against the built image as you're about to ship it.
Source code (checked in to a repository)
|
v
Unit tests (on the host, no external dependencies)
|
v
Docker image build (push to a repository now if needed)
| (only this step is the Dockerfile)
v
Integration tests (on the built container, with dependencies
| running in other containers, either launched
v from the host or a dedicated test-runner container)
Deploy
I would avoid complex Dockerfiles that have multiple optional stages that sometimes include test frameworks and sometimes don't. Build one image and run it in all environments, and keep the Dockerfile simple and readable.