Add build configuration system and paper image

- Add 'make paper' target to build a Paper image with plugins based on JRE
- Introduce dynamic .env and plugins.json configuration using Makefile
- Enable users to extend builds by managing their own directories in scratch/
- Implement copy_build_files macro for reproducible build management
- Add BUILDKIT_PROGRESS and DOCKER_BUILDKIT as configurable make vars
This commit is contained in:
Kris Lamoureux 2024-05-24 03:15:54 -04:00
parent 813b9de410
commit 4ce50becd2
Signed by: kris
GPG Key ID: 3EDA9C3441EDA925
11 changed files with 321 additions and 13 deletions

5
.gitignore vendored
View File

@ -1,3 +1,4 @@
data
.env
/.env
/plugins.json
scratch
screenlog.0

View File

@ -1,16 +1,24 @@
DOCKER_COMPOSE_BUILD = docker compose -f docker-compose.build.yml build
DOCKER_BUILDKIT ?= 1
BUILDKIT_PROGRESS ?= auto
BUILD ?= basic
DOCKER_COMPOSE_OPTS = BUILDX_GIT_LABELS=full \
DOCKER_BUILDKIT=$(DOCKER_BUILDKIT) \
BUILDKIT_PROGRESS=$(BUILDKIT_PROGRESS)
DOCKER_COMPOSE_BUILD = $(DOCKER_COMPOSE_OPTS) docker compose -f docker-compose.build.yml build
DOCKER_COMPOSE_UP = docker compose up -d
PRUNE_IMAGES = \
localhost/minecraft:latest \
localhost/minecraft:latest-paper \
localhost/minecraft:latest-spigot \
localhost/minecraft:latest-craftbukkit \
localhost/minecraft-jre:latest \
localhost/minecraft-jdk:latest
.PHONY: all clean craftbukkit default install jdk jre spigot vanilla
.PHONY: all clean configure craftbukkit default install jdk jre spigot vanilla
default: vanilla
all: vanilla spigot craftbukkit
all: vanilla paper spigot craftbukkit
jre:
$(DOCKER_COMPOSE_BUILD) minecraft-jre
@ -21,6 +29,9 @@ jdk:
vanilla: jre
$(DOCKER_COMPOSE_BUILD) minecraft-vanilla
paper: jre
$(DOCKER_COMPOSE_BUILD) minecraft-paper
spigot: jre jdk
$(DOCKER_COMPOSE_BUILD) minecraft-spigot
@ -30,6 +41,54 @@ craftbukkit: jre jdk
install:
$(DOCKER_COMPOSE_UP)
# Macro to copy files if they don't already exist or are the same
define copy_build_files
set -eu; \
BUILD=$(1) && \
FILE=$(2) && \
DIRECTORY=$(if $(3),$(3),builds) && \
SRC_FILE="./$${DIRECTORY}/$${BUILD}/$${FILE}" && \
DEST_FILE="./$${FILE}" && \
TEMP_DEST_FILE="$$(mktemp)" && \
if [ -f "$${SRC_FILE}" ]; then \
if [ -f "$${DEST_FILE}" ]; then \
cp "$${DEST_FILE}" "$${TEMP_DEST_FILE}"; \
if [ "$${FILE}" = ".env" ] && grep -q 'EULA=false' "$${SRC_FILE}"; then \
sed -i 's/EULA=true/EULA=false/' "$${TEMP_DEST_FILE}"; \
fi; \
if cmp -s "$${SRC_FILE}" "$${TEMP_DEST_FILE}"; then \
echo "INFO: \"$${DEST_FILE}\" is up to date."; \
rm "$${TEMP_DEST_FILE}"; \
else \
echo "ERROR: \"$${DEST_FILE}\" is different from \"$${SRC_FILE}\""; \
diff -u "$${DEST_FILE}" "$${SRC_FILE}"; \
rm "$${TEMP_DEST_FILE}"; \
exit 1; \
fi; \
else \
cp "$${SRC_FILE}" "$${DEST_FILE}"; \
echo "INFO: \"$${SRC_FILE}\" copied to \"$${DEST_FILE}\""; \
fi; \
else \
echo "ERROR: Source file \"$${SRC_FILE}\" does not exist."; \
exit 1; \
fi
endef
configure:
@if [ -d "./builds/$(BUILD)" ]; then \
echo "INFO: Configuring build $(BUILD) (./builds/$(BUILD))" && \
$(call copy_build_files,$(BUILD),.env); \
$(call copy_build_files,$(BUILD),plugins.json); \
elif [ -d "./scratch/$(BUILD)" ]; then \
echo "INFO: Configuring build $(BUILD) (./scratch/$(BUILD))" && \
$(call copy_build_files,$(BUILD),.env,scratch); \
$(call copy_build_files,$(BUILD),plugins.json,scratch); \
else \
echo "ERROR: Build directory for \"$(BUILD)\" not found"; \
exit 1; \
fi
clean:
docker image rm $(PRUNE_IMAGES) || true
docker builder prune -f

28
builds/basic/.env Normal file
View File

@ -0,0 +1,28 @@
# While setting EULA to false in 'builds' is potentially annoying, it is done
# intentionally to bring to attention the need to manually and explicitly agree
# to Minecraft's EULA before being able to run or build these images
EULA=false
VERSION=1.20.1
RUN_TAG=${VERSION}-paper
# Builds
VANILLA_TAG=${VERSION}
PAPER_TAG=${VERSION}-paper
SPIGOT_TAG=${VERSION}-spigot
CRAFTBUKKIT_TAG=${VERSION}-craftbukkit
########################
# Extra image settings #
########################
#
# JAVA_VERSION=latest
# JRE_IMAGE=localhost/minecraft-jre
# JDK_IMAGE=localhost/minecraft-jdk
# JRE_TAG=latest
# JDK_TAG=latest
#
# VANILLA_IMAGE=localhost/minecraft
# PAPER_IMAGE=localhost/minecraft
# SPIGOT_IMAGE=localhost/minecraft
# CRAFTBUKKIT_IMAGE=localhost/minecraft
#

39
builds/basic/plugins.json Normal file
View File

@ -0,0 +1,39 @@
{
"plugins": [
{
"name": "EssentialsX",
"version": "2.20.1",
"hash": "sha256:802ea30bda460ca4597e818925816933c123b08d8126a814fac28d03a61bf542",
"url": "https://github.com/EssentialsX/Essentials/releases/download/${version}/EssentialsX-${version}.jar",
"info_url": "https://essentialsx.net/wiki/Home.html"
},
{
"name": "EssentialsXChat",
"version": "2.20.1",
"hash": "sha256:40aa5c20241ceb3007ebcb5cfbf19bf2c467b0c090ae50e70653ee87ab775ca6",
"url": "https://github.com/EssentialsX/Essentials/releases/download/${version}/EssentialsXChat-${version}.jar",
"info_url": "https://essentialsx.net/wiki/Module-Breakdown.html#essentialsx-chat"
},
{
"name": "EssentialsXSpawn",
"version": "2.20.1",
"hash": "sha256:650d7c6a33865a02c5ffa4eb710def28e73d972c9aef85b2b1f4e71b9bd261a0",
"url": "https://github.com/EssentialsX/Essentials/releases/download/${version}/EssentialsXSpawn-${version}.jar",
"info_url": "https://essentialsx.net/wiki/Module-Breakdown.html#essentialsx-spawn"
},
{
"name": "WorldEdit",
"version": "7.3.1",
"hash": "md5:c44cd1c16c3d84d8efc57bbf417606cb",
"url": "https://dev.bukkit.org/projects/worldedit/files/5326355/download",
"info_url": "https://dev.bukkit.org/projects/worldedit"
},
{
"name": "WorldGuard",
"version": "7.0.9",
"hash": "md5:70d6418dd6a2e4492a9f18e5f9ecb10c",
"url": "https://dev.bukkit.org/projects/worldguard/files/4675318/download",
"info_url": "https://dev.bukkit.org/projects/worldguard"
}
]
}

View File

@ -29,12 +29,24 @@ services:
environment:
EULA: "${EULA:-false}"
minecraft-paper:
build:
context: .
dockerfile: ./dockerfiles/Dockerfile.paper
args:
VERSION: ${VERSION:-latest}
image: ${PAPER_IMAGE:-localhost/minecraft}:${PAPER_TAG:-latest-paper}
depends_on:
- minecraft-jre
environment:
EULA: "${EULA:-false}"
minecraft-spigot:
build:
context: .
dockerfile: ./dockerfiles/Dockerfile.bukkit
args:
VERSION: ${MINECRAFT_VERSION:-latest}
VERSION: ${VERSION:-latest}
SPIGOT: 'true'
image: ${SPIGOT_IMAGE:-localhost/minecraft}:${SPIGOT_TAG:-latest-spigot}
depends_on:
@ -48,7 +60,7 @@ services:
context: .
dockerfile: ./dockerfiles/Dockerfile.bukkit
args:
VERSION: ${MINECRAFT_VERSION:-latest}
VERSION: ${VERSION:-latest}
SPIGOT: 'false'
image: ${CRAFTBUKKIT_IMAGE:-localhost/minecraft}:${CRAFTBUKKIT_TAG:-latest-craftbukkit}
depends_on:

View File

@ -1,6 +1,6 @@
services:
minecraft:
image: ${IMAGE:-localhost/minecraft}:${TAG:-latest}
image: ${RUN_IMAGE:-localhost/minecraft}:${RUN_TAG:-latest}
ports:
- "0.0.0.0:25565:25565"
environment:

View File

@ -7,6 +7,9 @@ ARG VERSION=latest
# Defaults to building Spigot over CraftBukkit
ARG SPIGOT=true
# Plugins prefix
ARG PREFIX="PLUGIN_"
# SpigotMC BuildTools URL
ARG BASE_URL="https://hub.spigotmc.org/jenkins/job/BuildTools/"
ARG ARTIFACT_PATH="lastSuccessfulBuild/artifact/target/BuildTools.jar"
@ -15,7 +18,7 @@ ARG ARTIFACT_PATH="lastSuccessfulBuild/artifact/target/BuildTools.jar"
WORKDIR /build
# Download and build Spigot using BuildTools
RUN set -ux && \
RUN set -eux && \
# Grab latest version if not specified
if [ "$VERSION" = "latest" ]; then \
VERSION="$( \
@ -38,9 +41,59 @@ RUN set -ux && \
# Run BuildTools to build specified version
java -jar BuildTools.jar --rev "$VERSION" --compile "$BUILD_TYPE" && \
ln -s \
"$(echo $BUILD_TYPE | tr '[:upper:]' '[:lower:]')-${VERSION}.jar" \
"$(echo "$BUILD_TYPE" | tr '[:upper:]' '[:lower:]')-${VERSION}.jar" \
"server.jar"
# Move into a directory just for storing plugins
WORKDIR /plugins
# Copy in plugins
COPY ../plugins.json /plugins/
# Download defined plugins
RUN set -eux && \
# Download defined plugins and check against hash
tmp_downloads="$(mktemp -d)" && \
# Iterate over all plugins in plugins.json
jq -c '.plugins[]' plugins.json | while read -r PLUGIN; do \
# Set variables from plugins.json
name=$(echo "$PLUGIN" | jq -r '.name') && \
version=$(echo "$PLUGIN" | jq -r '.version') && \
# Interpolate instances of '${version}' in the URL
url=$(echo "$PLUGIN" | jq -r '.url' | sed "s/\${version}/$version/g") && \
hash=$(echo "$PLUGIN" | jq -r '.hash') && \
info=$(echo "$PLUGIN" | jq -r '.info_url') && \
# Extract hash type and value, e.g., `md5:6f5902ac237024bdd0c176cb93063dc4`
hash_type=$(echo "$hash" | cut -d':' -f1) && \
hash_value=$(echo "$hash" | cut -d':' -f2-) && \
# Download and compare the hash
tmp_file="${tmp_downloads}/${name}-${version}.jar" && \
curl -s -L "$url" -o "${tmp_file}" && \
case "$hash_type" in \
sha256) \
echo "${hash_value} $tmp_file" | sha256sum -c - || { \
echo "SHA256 hash mismatch for ${name}-${version}.jar"; \
rm -rf "$tmp_downloads"; \
exit 1; \
} \
;; \
md5) \
echo "${hash_value} $tmp_file" | md5sum -c - || { \
echo "MD5 hash mismatch for ${name}-${version}.jar"; \
rm -rf "$tmp_downloads"; \
exit 1; \
} \
;; \
*) \
echo "Unsupported hash type: ${hash_type}"; \
rm -rf "$tmp_downloads"; \
exit 1; \
;; \
esac && \
mv "$tmp_file" "${name}-${version}.jar"; \
done && \
rm -rf "$tmp_downloads"
# Use OpenJRE image for runtime
FROM "${JRE_IMAGE:-localhost/minecraft-jre}":"${JRE_TAG:-latest}" as runtime
@ -51,6 +104,9 @@ WORKDIR /app
# Copy the built bukkit jar from the build stage
COPY --from=build /build/server.jar /app/server.jar
# Copy in plugins
COPY --from=build /plugins/ /app/plugins/
# Generate initial settings
RUN java -jar server.jar --initSettings --nogui

View File

@ -11,7 +11,7 @@ RUN apt-get update && \
rm -rf /var/lib/apt/lists/*
# Eclipse Adoptium DEB installer package
RUN set -ux && \
RUN set -eux && \
# Download the Eclipse Adoptium GPG key
curl -s https://packages.adoptium.net/artifactory/api/gpg/key/public \
| gpg --dearmor | tee /etc/apt/trusted.gpg.d/adoptium.gpg > /dev/null && \
@ -21,7 +21,7 @@ RUN set -ux && \
| tee /etc/apt/sources.list.d/adoptium.list
# Install Adoptium Temurin (OpenJDK/OpenJRE)
RUN set -ux && \
RUN set -eux && \
# Grab latest LTS version if not specified
if [ "$JAVA_VERSION" = "latest" ]; then \
JAVA_VERSION="$( \

View File

@ -0,0 +1,111 @@
FROM "${JRE_IMAGE:-localhost/minecraft-jre}":"${JRE_TAG:-latest}"
# Minecraft version to download
ARG VERSION=latest
# Plugins prefix
ARG PREFIX="PLUGIN_"
# PaperMC base URL
ARG BASE_URL="https://api.papermc.io/v2/projects/paper/versions/${VERSION}"
# Download files
USER root
WORKDIR /app
# Download and verify sha256sum for PaperMC server.jar
RUN set -eux && \
# Grab latest version if not specified
if [ "$VERSION" = "latest" ]; then \
VERSION="$( \
curl -s https://api.papermc.io/v2/projects/paper | \
jq -r '.versions[-1]' \
)"; \
fi && \
# Get latest build for the specified version
BUILD="$( \
curl -s "$BASE_URL" \
| jq -r '.builds[-1]' \
)" && \
URL="${BASE_URL}/builds/${BUILD}/downloads/paper-${VERSION}-${BUILD}.jar" && \
curl -s -o /tmp/server.jar "$URL" && \
# Get SHA256 hash of server.jar and compare
SHA256="$(sha256sum /tmp/server.jar | awk '{print $1}')" && \
EXPECTED="$( \
curl -s "$BASE_URL/builds/$BUILD" \
| jq -r '.downloads.application.sha256' \
)" && \
if [ ! "$SHA256" = "$EXPECTED" ]; then \
echo "[ERROR] SHA256=\"$SHA256\" expected \"$EXPECTED\""; \
exit 1; \
fi && \
mv /tmp/server.jar /app/server.jar
# Move into a directory just for storing plugins
WORKDIR /app/plugins
# Copy in plugins
COPY ../plugins.json /app/plugins
# Download defined plugins
RUN set -eux && \
# Download defined plugins and check against hash
tmp_downloads="$(mktemp -d)" && \
# Iterate over all plugins in plugins.json
jq -c '.plugins[]' plugins.json | while read -r PLUGIN; do \
# Set variables from plugins.json
name=$(echo "$PLUGIN" | jq -r '.name') && \
version=$(echo "$PLUGIN" | jq -r '.version') && \
# Interpolate instances of '${version}' in the URL
url=$(echo "$PLUGIN" | jq -r '.url' | sed "s/\${version}/$version/g") && \
hash=$(echo "$PLUGIN" | jq -r '.hash') && \
info=$(echo "$PLUGIN" | jq -r '.info_url') && \
# Extract hash type and value, e.g., `md5:6f5902ac237024bdd0c176cb93063dc4`
hash_type=$(echo "$hash" | cut -d':' -f1) && \
hash_value=$(echo "$hash" | cut -d':' -f2-) && \
# Download and compare the hash
tmp_file="${tmp_downloads}/${name}-${version}.jar" && \
curl -s -L "$url" -o "${tmp_file}" && \
case "$hash_type" in \
sha256) \
echo "${hash_value} $tmp_file" | sha256sum -c - || { \
echo "SHA256 hash mismatch for ${name}-${version}.jar"; \
rm -rf "$tmp_downloads"; \
exit 1; \
} \
;; \
md5) \
echo "${hash_value} $tmp_file" | md5sum -c - || { \
echo "MD5 hash mismatch for ${name}-${version}.jar"; \
rm -rf "$tmp_downloads"; \
exit 1; \
} \
;; \
*) \
echo "Unsupported hash type: ${hash_type}"; \
rm -rf "$tmp_downloads"; \
exit 1; \
;; \
esac && \
mv "$tmp_file" "${name}-${version}.jar"; \
done && \
rm -rf "$tmp_downloads" && \
chown minecraft:minecraft /app/plugins/
# Generate initial settings
USER minecraft
WORKDIR /app
RUN java -jar server.jar --initSettings --nogui
# Back to root to copy the entrypoint in
USER root
COPY ../entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# Run app as minecraft user
USER minecraft
WORKDIR /app
# Expose port and run entrypoint script
EXPOSE 25565
ENTRYPOINT ["entrypoint.sh"]

View File

@ -8,7 +8,7 @@ USER minecraft
WORKDIR /app
# Download and verify sha1sum for server.jar
RUN set -ux && \
RUN set -eux && \
# Grab latest version if not specified
if [ "$VERSION" = "latest" ]; then \
VERSION="$( \

2
scratch/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore