Compare commits

...

1 Commits

Author SHA1 Message Date
Alexander Whitestone
35dad6211a fix: add Evennia VPS repair script (#534)
Some checks are pending
Smoke Test / smoke (pull_request) Waiting to run
2026-04-15 01:50:26 -04:00
3 changed files with 179 additions and 0 deletions

View File

@@ -6,6 +6,12 @@ Local runtime target:
Main commands:
- `python3 scripts/evennia/bootstrap_local_evennia.py`
- `python3 scripts/evennia/verify_local_evennia.py`
- `python3 scripts/evennia/repair_evennia_vps.py --settings-path /root/wizards/bezalel/evennia/bezalel_world/server/conf/settings.py --game-dir /root/wizards/bezalel/evennia/bezalel_world --execute`
Bezalel VPS repair target from issue #534:
- host: `104.131.15.18`
- purpose: remove broken port tuple overrides (`WEBSERVER_PORTS`, `TELNET_PORTS`, `WEBSOCKET_PORTS`) and rewrite `SERVERNAME` only
- the repair script prints recovery commands by default and can execute them when the Evennia runtime paths are correct
Hermes control path:
- `scripts/evennia/evennia_mcp_server.py`

View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import shlex
import subprocess
from pathlib import Path
BAD_SETTING_KEYS = (
"WEBSERVER_PORTS",
"TELNET_PORTS",
"WEBSOCKET_PORTS",
"SERVERNAME",
)
def repair_settings_text(text: str, server_name: str = "bezalel_world") -> str:
"""Remove broken port tuple overrides and rewrite SERVERNAME only."""
kept: list[str] = []
for line in text.splitlines():
if any(key in line for key in BAD_SETTING_KEYS):
continue
kept.append(line)
while kept and kept[-1] == "":
kept.pop()
kept.append(f'SERVERNAME = "{server_name}"')
kept.append("")
return "\n".join(kept)
def repair_settings_file(path: Path, server_name: str = "bezalel_world") -> str:
original = path.read_text()
repaired = repair_settings_text(original, server_name=server_name)
path.write_text(repaired)
return repaired
def build_superuser_python(game_dir: str, username: str, email: str, password: str) -> str:
game_dir_q = repr(game_dir)
username_q = repr(username)
email_q = repr(email)
password_q = repr(password)
return f"""import os, sys
sys.setrecursionlimit(5000)
os.environ['DJANGO_SETTINGS_MODULE'] = 'server.conf.settings'
os.chdir({game_dir_q})
import django
django.setup()
from evennia.accounts.accounts import AccountDB
if not AccountDB.objects.filter(username={username_q}).exists():
AccountDB.objects.create_superuser({username_q}, {email_q}, {password_q})
print('SUPERUSER_OK')
"""
def build_recovery_commands(
game_dir: str,
evennia_bin: str,
python_bin: str,
username: str = "Timmy",
email: str = "timmy@tower.world",
password: str = "timmy123",
) -> list[str]:
quoted_game = shlex.quote(game_dir)
quoted_evennia = shlex.quote(evennia_bin)
quoted_python = shlex.quote(python_bin)
superuser_code = build_superuser_python(game_dir, username, email, password)
superuser_cmd = f"{quoted_python} -c {shlex.quote(superuser_code)}"
return [
f"cd {quoted_game}",
"rm -f server/evennia.db3",
f"{quoted_evennia} migrate",
superuser_cmd,
f"{quoted_evennia} start",
f"{quoted_evennia} status",
]
def execute(commands: list[str]) -> int:
shell = "set -euo pipefail\n" + "\n".join(commands)
return subprocess.run(["bash", "-lc", shell], check=False).returncode
def main() -> int:
parser = argparse.ArgumentParser(description="Repair an Evennia VPS settings file and print/apply recovery commands.")
parser.add_argument("--settings-path", default="/root/wizards/bezalel/evennia/bezalel_world/server/conf/settings.py")
parser.add_argument("--game-dir", default="/root/wizards/bezalel/evennia/bezalel_world")
parser.add_argument("--evennia-bin", default="/root/wizards/bezalel/evennia/venv/bin/evennia")
parser.add_argument("--python-bin", default="/root/wizards/bezalel/evennia/venv/bin/python3")
parser.add_argument("--server-name", default="bezalel_world")
parser.add_argument("--username", default="Timmy")
parser.add_argument("--email", default="timmy@tower.world")
parser.add_argument("--password", default="timmy123")
parser.add_argument("--execute", action="store_true", help="Apply settings and run recovery commands instead of printing them.")
args = parser.parse_args()
settings_path = Path(args.settings_path)
if args.execute:
repair_settings_file(settings_path, server_name=args.server_name)
else:
print(f"# Would rewrite {settings_path} to remove broken port tuple overrides")
if settings_path.exists():
print(repair_settings_text(settings_path.read_text(), server_name=args.server_name))
else:
print(f"# Settings file not found: {settings_path}")
commands = build_recovery_commands(
game_dir=args.game_dir,
evennia_bin=args.evennia_bin,
python_bin=args.python_bin,
username=args.username,
email=args.email,
password=args.password,
)
if args.execute:
return execute(commands)
print("# Recovery commands")
print("\n".join(commands))
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,48 @@
from pathlib import Path
from scripts.evennia.repair_evennia_vps import build_recovery_commands, repair_settings_text
SCRIPT = Path("scripts/evennia/repair_evennia_vps.py")
README = Path("scripts/evennia/README.md")
def test_repair_script_exists() -> None:
assert SCRIPT.exists()
def test_repair_settings_text_removes_bad_port_tuple_overrides() -> None:
original = """# settings\nSERVERNAME = \"old\"\nWEBSERVER_PORTS = [(4101, None)]\nTELNET_PORTS = [(4000, 4001)]\nWEBSOCKET_PORTS = [(4102, None)]\nDEBUG = False\n"""
repaired = repair_settings_text(original, server_name="bezalel_world")
assert 'WEBSERVER_PORTS' not in repaired
assert 'TELNET_PORTS' not in repaired
assert 'WEBSOCKET_PORTS' not in repaired
assert 'SERVERNAME = "old"' not in repaired
assert 'SERVERNAME = "bezalel_world"' in repaired
assert 'DEBUG = False' in repaired
def test_build_recovery_commands_contains_evennia_recovery_steps() -> None:
commands = build_recovery_commands(
game_dir="/root/wizards/bezalel/evennia/bezalel_world",
evennia_bin="/root/wizards/bezalel/evennia/venv/bin/evennia",
python_bin="/root/wizards/bezalel/evennia/venv/bin/python3",
username="Timmy",
email="timmy@tower.world",
password="timmy123",
)
joined = "\n".join(commands)
assert "rm -f server/evennia.db3" in joined
assert "evennia migrate" in joined
assert "create_superuser" in joined
assert "evennia start" in joined
assert "evennia status" in joined
def test_evennia_readme_mentions_repair_script() -> None:
content = README.read_text()
assert "repair_evennia_vps.py" in content
assert "104.131.15.18" in content