Skip to content

Commit

Permalink
Dockerize project to build, test and run the game
Browse files Browse the repository at this point in the history
Create a [Dockerfile][1] with [multi-stage building][2] to create
a build environment based on the [Gradle Docker image][3] and
a lightweight runner environment based on the [Eclipse Temurin
Docker image][4] (both based on OpenJDK 17).

Create a .dockerignore file based on .gitignore to skip files that
[should not be uploaded in the docker image][5].

Create a [docker-compose.yml][6] to setup four services:
- `game` to run the app for end users.
- `linter` to run all linters once.
- `tester` to rerun all tests with live reload.
- `demo` to rerun the app with live reload.

Add the same image name to [build the docker image only once][7] for
multiple services sharing with the same codebase.

Create a .env file to [configure environment variables][8] used inside
the compose file. Add the [`COMPOSE_PROJECT_NAME` variable][9] to assign
a prefix for all resources managed by compose.

Update the pre-commit config to use `docker compose` with the `linter`
image instead of raw `docker` commands to speed up the execution.

Update the README.md to use `docker compose` instead of raw `docker`
commands to execute tasks related to the project.

[1]: https://docs.docker.com/engine/reference/builder/
[2]: https://docs.docker.com/develop/develop-images/multistage-build/
[3]: https://hub.docker.com/_/gradle
[4]: https://hub.docker.com/_/eclipse-temurin
[5]: https://codefresh.io/docker-tutorial/not-ignore-dockerignore-2/
[6]: https://docs.docker.com/compose/compose-file/
[7]: https://stackoverflow.com/questions/40899236/how-to-prevent-docker-compose-building-the-same-image-multiple-times
[8]: https://docs.docker.com/compose/environment-variables/
[9]: https://docs.docker.com/compose/reference/envvars/#compose_project_name
  • Loading branch information
renatocf committed Jul 17, 2022
1 parent b9aad52 commit 046bd49
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 13 deletions.
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Ignore Git files
.git
.gitignore
.gitattributes

# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
COMPOSE_PROJECT_NAME=galaxy-raiders

GALAXY_RAIDERS_IMAGE=galaxy-raiders
GALAXY_RAIDERS_VERSION=dev
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ repos:
name: Run Kotlin linters
description: This hook runs Gradle to execute linters
language: system
entry: bash -c "docker container run --rm --user gradle --volume $PWD:/home/gradle/app --workdir /home/gradle/app gradle:7.4.2-jdk17 gradle --no-daemon formatKotlin detekt"
entry: docker compose --profile dev run -T --rm linter
types: [kotlin]
pass_filenames: false
62 changes: 62 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
################################################################################
# BASE IMAGE #
################################################################################

# Use Gradle image to run Gradle commands
FROM gradle:7.4.2-jdk17 AS base

# Set default docker build args
ARG APP_DIR=/home/gradle/galaxy-raiders

# Change default workdir
WORKDIR ${APP_DIR}

# Set environment variable to disable Gradle Daemon
ENV GRADLE_OPTS=-Dorg.gradle.daemon=false

# Give the ownership of workdir to the Gradle user
RUN chown -R gradle:gradle .

# Change to non-priviled user (PID 1000)
USER gradle

# NOTE: This repo has gradle wrappers to ensure developers use the same version
# of gradle in every environment. However, using gradle's docker image already
# guarantees that. Therefore, commands below can use the `gradle` CLI directly.

# Copy source code to workdir
COPY --chown=gradle:gradle . .

# NOTE: Gradle download dependencies only when they are necessary, which slows
# tasks the first time they execute. Running them here will allow docker to
# cache these dependencies to speed up containers that execute gradle tasks.

# Force gradle to download dependencies to build, lint and test code
RUN gradle --no-build-cache clean

################################################################################
# BUILDER IMAGE #
################################################################################

# Use base image to build the project
FROM base AS builder

# Use gradle wrapper to generate an uber JAR
RUN gradle --no-daemon shadowJar

################################################################################
# PRODUCTION IMAGE #
################################################################################

# Use Eclipse Temurin image to run the project
FROM eclipse-temurin:17-jdk AS runner

# Reuse default docker build args
ARG APP_DIR=/home/gradle/galaxy-raiders

# Copy runtime from image
COPY --from=builder ${APP_DIR}/app/build/libs/galaxy-raiders.jar \
/bin/runner/galaxy-raiders.jar

# Run uber JAR to start application
CMD ["java", "-jar", "galaxy-raiders.jar"]
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,22 @@ pre-commit run --all-files

Alternatively, to execute the linters manually, run:
```bash
docker container run --rm --user gradle --volume "$PWD":/home/gradle/app --workdir /home/gradle/app gradle:7.4.2-jdk17 gradle --no-daemon formatKotlin detekt
docker compose --profile dev run --rm linter
```

## Compilation

To compile all classes in the project, run:
The compilation of all classes and the generation of a runnable self-contained
JAR is made "behind the scenes" by [docker][4].

To build the development images, run:
```bash
docker container run --rm --user gradle --volume "$PWD":/home/gradle/app --workdir /home/gradle/app gradle:7.4.2-jdk17 gradle --no-daemon clean assemble
docker compose --profile dev build
```

To generate a runnable self-contained JAR of the project, run:
To build the production images, run:
```bash
docker container run --rm --user gradle --volume "$PWD":/home/gradle/app --workdir /home/gradle/app gradle:7.4.2-jdk17 gradle --no-daemon clean shadowJar
docker compose --profile prod build
```

## Tests
Expand All @@ -51,32 +54,31 @@ All tests in the project are developed using [JUnit 5][8].

To execute all tests (with live reload), run:
```bash
docker container run --rm --user gradle --volume "$PWD":/home/gradle/app --workdir /home/gradle/app gradle:7.4.2-jdk17 gradle --continuous test
docker compose --profile dev up tester
```

## Execution

To execute the project in development mode (with live reload), run:
```bash
docker container run --rm --user gradle --volume "$PWD":/home/gradle/app --workdir /home/gradle/app gradle:7.4.2-jdk17 gradle --continuous run
docker compose --profile dev up demo --build
```

To execute the project in production mode, generate a runnable self-contained
JAR of the project, then run:
To execute the project in production mode, run:
```bash
docker container run --rm --volume "$PWD"/app/build/libs/galaxy-raiders.jar:/bin/runner/galaxy-raiders.jar --workdir /bin/runner eclipse-temurin:17-jdk java -jar galaxy-raiders.jar
docker compose --profile prod up game --build
```

## Other tasks

To find available gradle tasks, run:
```bash
docker container run --rm --user gradle --volume "$PWD":/home/gradle/app --workdir /home/gradle/app gradle:7.4.2-jdk17 gradle tasks
docker compose --profile dev run --rm demo gradle --no-daemon tasks
```

To execute any task, run:
```bash
docker container run --rm --user gradle --volume "$PWD":/home/gradle/app --workdir /home/gradle/app gradle:7.4.2-jdk17 gradle {task}
docker compose --profile dev run --rm demo gradle --no-daemon {task}
```

[1]: https://uspdigital.usp.br/jupiterweb/obterDisciplina?sgldis=MAC0218
Expand Down
48 changes: 48 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
services:
# The `game` service is the production app for end users.
game:
image: "${GALAXY_RAIDERS_IMAGE}:${GALAXY_RAIDERS_VERSION}"
build:
context: .
target: "runner"
profiles:
- "prod"

# The `linter` service allows running all linters.
# Whenever a file changes, Gradle will rerun detekt and ktlint.
linter:
image: "${GALAXY_RAIDERS_IMAGE}:${GALAXY_RAIDERS_VERSION}"
build:
context: .
target: "base"
command: "gradle formatKotlin detekt"
volumes:
- "./:/home/gradle/galaxy-raiders/"
profiles:
- "dev"

# The `tester` service allows running all tests.
# Whenever a file changes, JUnit will rerun all tests related to it.
tester:
image: "${GALAXY_RAIDERS_IMAGE}:${GALAXY_RAIDERS_VERSION}"
build:
context: .
target: "base"
command: "gradle --continuous test"
volumes:
- "./:/home/gradle/galaxy-raiders/"
profiles:
- "dev"

# The `demo` service allows running the application.
# Whenever a file changes, the app will be restarted automatically.
demo:
image: "${GALAXY_RAIDERS_IMAGE}:${GALAXY_RAIDERS_VERSION}"
build:
context: .
target: "base"
command: "gradle --continuous runShadow"
volumes:
- "./:/home/gradle/galaxy-raiders/"
profiles:
- "dev"

0 comments on commit 046bd49

Please sign in to comment.