Files
sanctuary-beta/scripts/server_pack.py
T
2026-10-26 01:21:39 +01:00

81 lines
4.2 KiB
Python

#!/usr/bin/env python3
"""Build a Linux server bootstrap kit; never install into an existing server."""
import hashlib
import json
from pathlib import Path
from urllib.request import urlopen
import zipfile
import pack
TOOLS = {
'fabric-server-launch.jar': (
'https://meta.fabricmc.net/v2/versions/loader/26.3/0.19.5/1.1.2/server/jar',
'0b56ad54d762172e8b8748e467f584f071e4dedecd93dc336cf2c68837e790be'),
'packwiz-installer.jar': (
'https://github.com/packwiz/packwiz-installer/releases/download/v0.5.14/packwiz-installer.jar',
'c9f646908d340d84773948a9a7d98bc1dae250d35e1016dc6e2b8459760b5598'),
'packwiz-installer-bootstrap.jar': (pack.BOOTSTRAP_URL, pack.BOOTSTRAP_SHA256),
}
PACK_URL = 'https://git.botsu.net/koka/sanctuary-beta/raw/branch/packwiz/server/pack.toml'
def put(archive, name, data, mode=0o100644):
entry = zipfile.ZipInfo(name, date_time=(1980, 1, 1, 0, 0, 0))
entry.compress_type = zipfile.ZIP_DEFLATED
entry.external_attr = mode << 16
archive.writestr(entry, data)
def assemble():
values = pack.properties()
manifest, _ = pack.verify_server(pack.ROOT / 'build/packwiz-server')
pack.require(manifest['versions'] == {'minecraft': '26.3', 'fabric': '0.19.5'},
'Re-pin the server launcher for an explicit Minecraft/Fabric migration')
cache = pack.ROOT / 'build/server-tools'
cache.mkdir(parents=True, exist_ok=True)
for name, (url, digest) in TOOLS.items():
target = cache / name
if not target.is_file():
with urlopen(url, timeout=45) as response:
data = response.read()
pack.require(hashlib.sha256(data).hexdigest() == digest, 'Server tool hash mismatch: ' + name)
target.write_bytes(data)
pack.require(pack.sha256(target) == digest, 'Cached server tool hash mismatch: ' + name)
platform = {'versions': manifest['versions'], 'pack_url': PACK_URL,
'sha256': {name: digest for name, (_, digest) in TOOLS.items()}}
output = pack.ROOT / 'build' / f'Sanctuary-Server-{values["pack_version"]}.zip'
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as archive:
for name in TOOLS:
put(archive, '.tools/' + name, (cache / name).read_bytes())
put(archive, '.tools/server.py', (pack.ROOT / 'scripts/server_runtime.py').read_bytes())
put(archive, '.tools/platform.json', json.dumps(platform, indent=2) + '\n')
put(archive, 'server.sh', '#!/bin/sh\nset -eu\ncd "$(dirname "$0")"\nexec python3 .tools/server.py "$@"\n', mode=0o100755)
put(archive, 'README.md', (pack.ROOT / 'docs/server-pack.md').read_bytes())
# Examples are deliberately outside config/ and outside the pack index.
put(archive, 'examples/sanctuary-community.json.example', json.dumps({
'mode': 'database', 'serverId': 'REPLACE_WITH_EXISTING_SERVER_UUID',
'jdbcUrl': 'jdbc:mariadb://db.example.net:3306/sanctuary?sslMode=verify-full',
'user': 'sanctuary_game', 'passwordEnv': 'SANCTUARY_COMMUNITY_DB_PASSWORD',
}, indent=2) + '\n')
put(archive, 'examples/sanctuary.env.example',
'SANCTUARY_APP_URL=https://sanctuary-minecraft.net\n'
'SANCTUARY_API_TOKEN=\nSANCTUARY_COMMUNITY_DB_PASSWORD=\n'
'SANCTUARY_MEMORY=4G\n')
put(archive, 'examples/sanctuary-beta.service',
'[Unit]\nDescription=Sanctuary Beta Minecraft server\n'
'Wants=network-online.target\nAfter=network-online.target\n\n'
'[Service]\nType=simple\nUser=minecraft\nGroup=minecraft\n'
'WorkingDirectory=/srv/minecraft/sanctuary-beta\n'
'EnvironmentFile=/etc/sanctuary/beta.env\n'
'ExecStart=/srv/minecraft/sanctuary-beta/server.sh start\n'
'Restart=on-failure\nRestartSec=30\nTimeoutStopSec=90\nKillMode=mixed\n'
'UMask=0077\nNoNewPrivileges=true\nPrivateTmp=true\nProtectHome=true\n'
'ProtectSystem=strict\nReadWritePaths=/srv/minecraft/sanctuary-beta\n'
'LimitNOFILE=65535\n\n[Install]\nWantedBy=multi-user.target\n')
print('Server bootstrap kit: ' + str(output))
if __name__ == '__main__':
assemble()