Complete 26.3 parity acceptance and dual-version release workflow
Build and smoke / changes (push) Waiting to run

This commit is contained in:
dnorberg committed 2026-11-14 12:17:49 -05:00
1 parent 41daff1e61
commit 4a59fb5d02
26 files changed
+1241 -369

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""Small dual-target dedicated smoke. Worlds are disposable; stalled checks hand off."""
import argparse
from server_test_lock import server_test_lock
import json
import os
from pathlib import Path
import shutil
import signal
import subprocess
import tempfile
import time
ROOT = Path(__file__).resolve().parents[1]
class StalledTest(RuntimeError):
pass
def wait_for(process, log, marker, timeout=60):
started = time.monotonic()
while time.monotonic() - started < timeout:
text = log.read_text(errors="replace") if log.exists() else ""
if marker in text:
return text
if process.poll() is not None:
raise RuntimeError(f"Server exited before {marker!r}; inspect {log}")
time.sleep(0.25)
raise StalledTest(f"No result for {marker!r} within {timeout}s; stop and hand off, do not retry")
def stop(process):
if process is None or process.poll() is not None:
return
try:
process.stdin.write("stop\n")
process.stdin.flush()
process.wait(timeout=25)
except (BrokenPipeError, subprocess.TimeoutExpired):
os.killpg(process.pid, signal.SIGTERM)
try:
process.wait(timeout=10)
except subprocess.TimeoutExpired:
os.killpg(process.pid, signal.SIGKILL)
process.wait()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--target", choices=("mc1211", "mc263"), required=True)
args = parser.parse_args()
logs = ROOT / "build/verification" / args.target
logs.mkdir(parents=True, exist_ok=True)
work = Path(tempfile.mkdtemp(prefix=f"rivencraft-{args.target}-smoke-"))
settings = {"layers": [{"height": 1, "block": "minecraft:bedrock"},
{"height": 2, "block": "minecraft:dirt"},
{"height": 1, "block": "minecraft:grass_block"}],
"biome": "minecraft:plains"}
(work / "eula.txt").write_text("eula=true\n")
(work / "server.properties").write_text(
"server-ip=127.0.0.1\nserver-port=0\nonline-mode=false\nwhite-list=false\n"
"view-distance=2\nsimulation-distance=2\ngenerate-structures=false\n"
"level-type=minecraft:flat\nlevel-seed=263100\ngenerator-settings=" + json.dumps(settings) + "\n")
command = [str(ROOT / "gradlew")]
if args.target == "mc263":
command += ["-p", str(ROOT / "ports/mc263")]
command += ["--no-daemon", "runServer", f"-PserverRunDir={work}", "--console=plain"]
process = None
try:
for phase in ("first", "restart"):
log = logs / f"{phase}.log"
with log.open("w") as output:
process = subprocess.Popen(command, cwd=ROOT, stdin=subprocess.PIPE,
stdout=output, stderr=subprocess.STDOUT, text=True, start_new_session=True)
wait_for(process, log, "Done (")
if args.target == "mc263":
if phase == "first":
for name, seed in (("smoke_a", 123456789), ("smoke_b", 987654321)):
process.stdin.write(f"rivencraft_port age create {name} {seed} flat\n")
process.stdin.flush()
wait_for(process, log, f"Created rivencraft:age/{name} with seed {seed}")
else:
text = log.read_text()
for name, seed in (("smoke_a", 123456789), ("smoke_b", 987654321)):
if f"Loaded 26.3 prototype Age rivencraft:age/{name} with seed {seed}" not in text:
raise RuntimeError("Age identity/seed failed full restart")
process.stdin.write("rivencraft_port age list\n")
process.stdin.flush()
process.stdin.write("save-all flush\n")
process.stdin.flush()
wait_for(process, log, "Saved the game")
else:
# 1.21.1's Gradle launcher does not forward this pipe. Its
# unchanged gameplay uses the recorded/manual regression;
# this smoke checks startup and graceful process shutdown.
os.killpg(process.pid, signal.SIGINT)
if args.target == "mc1211":
try:
process.wait(timeout=25)
except subprocess.TimeoutExpired:
stop(process)
else:
stop(process)
if process.returncode not in (0, 130, -signal.SIGINT):
raise RuntimeError(f"Server exited with {process.returncode}; inspect {log}")
process = None
print(f"PASS {args.target} {phase}", flush=True)
except StalledTest as error:
print(f"HANDOFF: {error}. Manual check: start {args.target}, create two Ages, travel, "
"restart and revisit both; confirm names/seeds and return travel.", flush=True)
raise SystemExit(2)
finally:
stop(process)
shutil.rmtree(work)
print(f"Removed temporary world; logs: {logs}", flush=True)
if __name__ == "__main__":
with server_test_lock():
main()