#!/usr/bin/env python3 from argparse import ArgumentParser from datetime import datetime from re import search from typing import Iterable import docker from ftbtypes import * from jsongetcache import * import jsongetcache default_base_url = "https://api.modpacks.ch" cache_path = Path("cache") def get_entity(route: str, base_url: str = default_base_url): slashes = '/\\' cachefile = cache_path / f"{route.strip(slashes)}.json" return jsongetcache.load_cached(base_url + route, cachefile) def get_latest_release(versions: Iterable[ModPackVersion]): return max((version for version in versions if version["type"].lower() == "release"), key=lambda version: version["updated"]) def get_modpack_route(modpack: ModPackManifest | int): return f"/public/modpack/{modpack if isinstance(modpack, int) else modpack['id']}" def get_modpack_manifest(modpack: ModPackManifest | int) -> ModPackManifest: return get_entity(get_modpack_route(modpack)) def get_version_route(modpack: ModPackManifest | int, version: ModPackVersion | int): return f"{get_modpack_route(modpack)}/{version if isinstance(version, int) else version['id']}" def get_version_manifest(modpack: ModPackManifest | int, version: ModPackVersion | int) -> ModPackVersionManifest: return get_entity(get_version_route(modpack, version)) def version_without_build(version: str): print(version) match = search(r"^(\d+(.\d+)*)\+(\d+)?$", version) if match is None: raise Exception(f"Invalid version string: {version}") else: return match.group(1) def get_modpack_slug(modpack: ModPackManifest): return '-'.join(filter(None, modpack["name"].lower().split(' '))) def get_installer(modpack: ModPackManifest | int, version: ModPackVersion | int, architecture: docker.Platforms): match architecture: case "linux/arm64": return f"{get_version_route(modpack, version)}/server/arm/linux", "83b9ef3f8b0f525da83c10fd8692c12a6a200c5ce79eba9da97ac29a414232fd" case "linux/amd64": return f"{get_version_route(modpack, version)}/server/linux", "9c5eed5e160e329bb6c393db549db356b9cc6a9711a5461aba35607b4124485a" case _: raise Exception(f"Invalid or unsupported architecture {architecture}!") def parse_arguments(): parser = ArgumentParser("build.py", description="FTB Docker image build helper.") parser.add_argument("modpack", type=int, help="Modpack ID") parser.add_argument("--version", "-v", type=int, help="Specific Modpack Version ID, otherwise uses the latest") return parser.parse_args() if __name__ == "__main__": args = parse_arguments() modpack = get_modpack_manifest(args.modpack) slug = get_modpack_slug(modpack) print("Slug", slug) if args.version is None: args.version = get_latest_release(modpack["versions"])["id"] version = get_version_manifest(modpack, args.version) print(f"{modpack['name']} version {version['name']}: updated {datetime.fromtimestamp(version['updated'])}") java_target = next((target for target in version["targets"] if target["type"] == "runtime" and target["name"] == "java"), None) if java_target is None: raise Exception(f"{modpack['name']} version {version['name']} has no java target: {' ,'.join(str(x) for x in version['targets'])}") # java_version = version_without_build(java_target["version"]) java_version = java_target["version"].replace('+', '_') print(f"Required java version is version {java_version}") # base_image = f"azul/zulu-openjdk:{java_version}-jre" base_image = f"eclipse-temurin:{java_version}-jre" repo = f"hub.cnml.de/{slug}" semver_version_tags = docker.semver_tags(version["name"]) platforms: list[docker.Platforms] = ["linux/arm64", "linux/amd64"] for platform in platforms: installer, checksum = get_installer(modpack, version, platform) tags = list(f"{ver}-{platform[(platform.rfind('/')+1):]}" for ver in semver_version_tags) print(tags) print(installer, checksum) docker.buildx(repo, tags, [platform], build_args={ "INSTALLER_URL": default_base_url + installer, "INSTALLER_CHECKSUM": checksum, "MODPACK_ID": str(modpack["id"]), "MODPACK_VERSION": str(version["id"]), "BASE_IMAGE": base_image }, write_command=True) for version_tag in semver_version_tags: tags = list(f"{version_tag}-{platform[(platform.rfind('/')+1):]}" for platform in platforms) docker.create_manifest(repo, version_tag, tags, write_command=True)