Added build script for graal and temurin builds
This commit is contained in:
commit
ad88921f1c
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
versions/
|
||||
*.cache
|
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@ -0,0 +1,19 @@
|
||||
ARG DOCKER_IMAGE
|
||||
FROM ${DOCKER_IMAGE}
|
||||
|
||||
ARG VERSION_ID
|
||||
ARG VERSION_SHA1
|
||||
ENV MINECRAFT_VERSION=${VERSION_ID}
|
||||
ENV INIT_MEMORY="512M"
|
||||
ENV MAX_MEMORY="2G"
|
||||
WORKDIR /server
|
||||
|
||||
# Copy server file and verify checksum
|
||||
VOLUME [ "/server" ]
|
||||
COPY versions/${VERSION_ID}/server.jar /server.jar
|
||||
COPY eula.txt /server/
|
||||
RUN echo -n "${VERSION_SHA1} /server.jar" | sha1sum -c -
|
||||
|
||||
# Run server
|
||||
EXPOSE 25565
|
||||
ENTRYPOINT ["java", "-jar", "/server.jar", "--nogui", "-Xms${INIT_MEMORY}", "-Xmx${MAX_MEMORY}"]
|
194
build.py
Normal file
194
build.py
Normal file
@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
from typing import Any, Literal, TypedDict
|
||||
from pathlib import Path
|
||||
import urllib.request
|
||||
import json
|
||||
import pickle
|
||||
import subprocess
|
||||
import argparse
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
ReleaseType = Literal["release", "snapshot", "old_beta", "old_alpha"]
|
||||
|
||||
|
||||
class LatestVersions(TypedDict):
|
||||
release: str
|
||||
snapshot: str
|
||||
|
||||
|
||||
class Version(TypedDict):
|
||||
id: str
|
||||
type: ReleaseType
|
||||
url: str
|
||||
time: str
|
||||
releaseTime: str
|
||||
|
||||
|
||||
class JavaVersion(TypedDict):
|
||||
component: str
|
||||
majorVersion: int
|
||||
|
||||
|
||||
class DownloadInfo(TypedDict):
|
||||
sha1: str
|
||||
size: int
|
||||
url: str
|
||||
|
||||
|
||||
class VersionDownloads(TypedDict):
|
||||
client: DownloadInfo
|
||||
client_mappings: DownloadInfo
|
||||
server: DownloadInfo
|
||||
server_mappings: DownloadInfo
|
||||
|
||||
|
||||
class VersionManifestFull(TypedDict):
|
||||
id: str
|
||||
mainClass: str
|
||||
type: ReleaseType
|
||||
time: str
|
||||
releaseTime: str
|
||||
minimumLauncherVersion: int
|
||||
javaVersion: JavaVersion
|
||||
downloads: VersionDownloads
|
||||
|
||||
|
||||
class DockerfileBuildArgs(TypedDict):
|
||||
DOCKER_IMAGE: str
|
||||
VERSION_ID: str
|
||||
VERSION_SHA1: str
|
||||
|
||||
class VersionManifest(TypedDict):
|
||||
latest: LatestVersions
|
||||
versions: list[Version]
|
||||
|
||||
|
||||
def load_version_manifest(version: Version):
|
||||
# Download new manifest
|
||||
with urllib.request.urlopen(version["url"]) as url:
|
||||
data: VersionManifestFull = json.load(url)
|
||||
return data
|
||||
|
||||
|
||||
def load_manifest(reload: bool = False, max_age: timedelta = timedelta(hours=1)):
|
||||
manifest_cache = "manifest.cache"
|
||||
# Load cached manifest
|
||||
if reload:
|
||||
print("Forced manifest reload.")
|
||||
else:
|
||||
try:
|
||||
with open(manifest_cache, "rb") as f:
|
||||
time: datetime
|
||||
data: VersionManifest
|
||||
(time, data) = pickle.load(f)
|
||||
cache_age = datetime.now() - time
|
||||
if cache_age <= max_age:
|
||||
print(f"Loaded cache file with age {cache_age}.")
|
||||
return data
|
||||
print(f"Cache is older than {max_age} and will be reloaded.")
|
||||
except FileNotFoundError:
|
||||
print("No cached manifest found!")
|
||||
|
||||
# Download new manifest
|
||||
with urllib.request.urlopen("https://launchermeta.mojang.com/mc/game/version_manifest.json") as url:
|
||||
data: VersionManifest = json.load(url)
|
||||
|
||||
# Save manifest to cache
|
||||
with open(manifest_cache, "wb") as f:
|
||||
pickle.dump((datetime.now(), data), f)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
BUF_SIZE = 65536
|
||||
|
||||
|
||||
def calculate_hash(filename: Path):
|
||||
with filename.open("rb") as f:
|
||||
sha1 = hashlib.sha1()
|
||||
while True:
|
||||
data = f.read(BUF_SIZE)
|
||||
if not data:
|
||||
break
|
||||
sha1.update(data)
|
||||
return sha1
|
||||
|
||||
|
||||
def download_file(file: DownloadInfo, filename: Path):
|
||||
if not filename.exists():
|
||||
filename.parent.mkdir(exist_ok=True)
|
||||
print(f"Downloading new file {file['url']}")
|
||||
urllib.request.urlretrieve(file["url"], filename)
|
||||
sha1 = calculate_hash(filename)
|
||||
if sha1.hexdigest() == file["sha1"]:
|
||||
print("Hashes match!")
|
||||
else:
|
||||
raise Exception(
|
||||
f"File has sha1 {sha1.hexdigest()}, expected {file['sha1']}")
|
||||
return filename
|
||||
|
||||
|
||||
default_build_platforms = ["linux/arm64/v8", "linux/arm/v7", "linux/amd64", "linux/ppc64le"]
|
||||
|
||||
|
||||
def docker_buildx(repository: str, tags: list[str], build_platforms: list[str] | None = None, dockerfile: str = "Dockerfile", build_args: DockerfileBuildArgs | dict[str, Any] | None = None, directory: str = "."):
|
||||
if build_platforms is None:
|
||||
build_platforms = default_build_platforms
|
||||
if build_args is None:
|
||||
build_args = dict()
|
||||
labels = [f"{repository}:{tag}"for tag in tags]
|
||||
command = ["docker", "buildx", "build",
|
||||
"--platform", ",".join(build_platforms),
|
||||
*[t for (key, value) in build_args.items()
|
||||
for t in ("--build-arg", f"{key}={value}")],
|
||||
"--file", dockerfile,
|
||||
*[t for label in labels for t in ("--tag", label)],
|
||||
"--pull", "--push", directory]
|
||||
print(" ".join(command))
|
||||
subprocess.run(command)
|
||||
|
||||
|
||||
def build_version(manifest: VersionManifest, version_id: str, repository: str = "hub.cnml.de/minecraft"):
|
||||
matching_version = list(
|
||||
filter(lambda v: v["id"] == version_id, manifest["versions"]))
|
||||
if len(matching_version) == 0:
|
||||
raise Exception(f"Version {version_id} not found in manifest!")
|
||||
version = load_version_manifest(matching_version[0])
|
||||
java_version = version['javaVersion']
|
||||
print(
|
||||
f"Version [{version['type']}] {version['id']} requires java version {java_version['majorVersion']} ({java_version['component']})")
|
||||
server_jar_file = Path(f"versions/{version['id']}/server.jar")
|
||||
server_jar = version["downloads"]["server"]
|
||||
download_file(server_jar, server_jar_file)
|
||||
|
||||
build_args: DockerfileBuildArgs = {
|
||||
"VERSION_ID": version['id'],
|
||||
"VERSION_SHA1": server_jar['sha1'],
|
||||
"DOCKER_IMAGE": ""
|
||||
}
|
||||
|
||||
# Build GraalVM images
|
||||
build_args["DOCKER_IMAGE"] = f"ghcr.io/graalvm/jdk:java{java_version['majorVersion']}"
|
||||
docker_buildx(repository, [version["id"], f"{version['id']}-graalvm"], build_args=build_args, build_platforms=["linux/amd64", "linux/arm64"])
|
||||
|
||||
# Build Temurin
|
||||
build_args["DOCKER_IMAGE"] = f"eclipse-temurin:{java_version['majorVersion']}-jre"
|
||||
docker_buildx(repository, [f"{version['id']}-temurin"], build_args=build_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
"build", description="Utility script to build docker images for Minecraft servers.")
|
||||
parser.add_argument("versions", nargs="*", type=str,
|
||||
help="Versions to build for.")
|
||||
parser.add_argument("--reload", action="store_true", help="Force reload the manifest.")
|
||||
parser.add_argument("--all", action="store_true", help="Build all versions.")
|
||||
args = parser.parse_args()
|
||||
manifest = load_manifest(args.reload)
|
||||
versions: list[str] = list(version["id"] for version in manifest["versions"] if version["type"] == "release") if args.all else args.versions
|
||||
for version_id in versions:
|
||||
try:
|
||||
build_version(manifest, version_id)
|
||||
except Exception as e:
|
||||
print(f"Failed building images for version {version_id}:\n\t{e}")
|
Loading…
Reference in New Issue
Block a user