afegit setup-quake

This commit is contained in:
2026-03-16 10:17:30 +01:00
parent 773fb30ee6
commit c13beff6b5
6 changed files with 212 additions and 0 deletions
+4
View File
@@ -2,3 +2,7 @@ setup-network/build/
setup-network/dist/
setup-network/*.spec
setup-network/.venv/
setup-quake/build/
setup-quake/dist/
setup-quake/*.spec
setup-quake/.venv/
+45
View File
@@ -58,3 +58,48 @@ hostname_prefix = retro-alcoi-
`/etc/network/interfaces`.
Re-running with the same or a different machine number is safe (idempotent).
### setup-quake
Installs `ioquake3` from the Debian package manager and downloads/extracts the
Quake 3 data files from a private server to the real user's home directory.
```
sudo ./setup-quake
```
See [`setup-quake/`](setup-quake/) for source and build instructions.
#### Quick start
```bash
cd setup-quake/
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
bash build.sh
# Binary is at dist/setup-quake
```
Copy `dist/setup-quake` and `dist/config.ini` to a USB stick, then on each
fair PC:
```bash
sudo ./setup-quake
```
#### Configuration
Edit `config.ini` (alongside the binary) before distributing:
```ini
[quake]
files_url = https://php.sustancia.synology.me/files/ioquake3-files.zip
install_dir = .q3a
```
#### How it works
1. Installs `ioquake3` via `apt-get`.
2. Downloads the Quake 3 data files zip from the configured URL.
3. Extracts the data files to `~/.q3a` (resolved via `$SUDO_USER` so the real
user's home is used, not root's).
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
# Navigate to setup-quake/ regardless of where we're called from
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Activate virtualenv if present, otherwise require pyinstaller in PATH
if [[ -f .venv/bin/activate ]]; then
source .venv/bin/activate
echo "Activated .venv"
elif ! command -v pyinstaller &>/dev/null; then
echo "Error: pyinstaller not found. Either create a .venv or install pyinstaller." >&2
echo " python3 -m venv .venv && .venv/bin/pip install -r requirements.txt" >&2
exit 1
fi
echo "Building setup-quake binary..."
pyinstaller --onefile --clean --name setup-quake setup_quake.py
echo "Copying config.ini to dist/..."
cp config.ini dist/
echo ""
echo "Build complete. Output:"
echo " ${SCRIPT_DIR}/dist/setup-quake"
echo " ${SCRIPT_DIR}/dist/config.ini"
+3
View File
@@ -0,0 +1,3 @@
[quake]
files_url = https://php.sustancia.synology.me/files/ioquake3-files.zip
install_dir = .q3a
+1
View File
@@ -0,0 +1 @@
pyinstaller
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
"""
setup-quake: Install ioquake3 and download Quake 3 data files
to the real user's home directory for use at a retro gaming fair.
Usage: sudo ./setup-quake
"""
import argparse
import configparser
import os
import pwd
import subprocess
import sys
import urllib.request
import zipfile
def get_script_dir():
"""Return the directory containing the script or binary."""
if getattr(sys, "frozen", False):
return os.path.dirname(sys.executable)
return os.path.dirname(os.path.abspath(__file__))
def parse_args():
parser = argparse.ArgumentParser(
description="Install ioquake3 and download Quake 3 data files."
)
parser.parse_args()
def check_root():
if os.geteuid() != 0:
print("Error: this script must be run as root (use sudo).", file=sys.stderr)
sys.exit(1)
def load_config():
config_path = os.path.join(get_script_dir(), "config.ini")
cfg = configparser.ConfigParser()
cfg.read(config_path)
files_url = cfg.get("quake", "files_url", fallback="https://php.sustancia.synology.me/files/ioquake3-files.zip")
install_dir = cfg.get("quake", "install_dir", fallback=".q3a")
return files_url, install_dir
def get_real_home():
"""Resolve the actual user's home when running under sudo."""
sudo_user = os.environ.get("SUDO_USER")
if sudo_user:
home_dir = pwd.getpwnam(sudo_user).pw_dir
return home_dir, sudo_user
home_dir = os.path.expanduser("~")
return home_dir, "root"
def install_ioquake3():
print(" Running: apt-get install -y ioquake3")
subprocess.run(["apt-get", "install", "-y", "ioquake3"], check=True)
def download_files(url):
"""Download the Quake 3 files zip to /tmp. Returns destination path."""
dest = "/tmp/ioquake3-files.zip"
print(f" Downloading: {url}")
print(f" Destination: {dest}")
with urllib.request.urlopen(url) as response:
total = response.headers.get("Content-Length")
total_mb = f"{int(total) / 1024 / 1024:.1f} MB" if total else "unknown size"
print(f" File size: {total_mb}")
downloaded = 0
chunk_size = 1024 * 1024 # 1 MB
with open(dest, "wb") as f:
while True:
chunk = response.read(chunk_size)
if not chunk:
break
f.write(chunk)
downloaded += len(chunk)
print(f" Downloaded: {downloaded / 1024 / 1024:.1f} MB", end="\r")
print() # newline after progress
return dest
def extract_files(zip_path, home_dir, install_dir):
target = os.path.join(home_dir, install_dir)
print(f" Extracting to: {target}")
os.makedirs(target, exist_ok=True)
with zipfile.ZipFile(zip_path) as zf:
zf.extractall(target)
os.remove(zip_path)
print(f" Cleaned up: {zip_path}")
def main():
parse_args()
check_root()
files_url, install_dir = load_config()
home_dir, username = get_real_home()
print("=== setup-quake ===")
print(f" User : {username}")
print(f" Home directory : {home_dir}")
print(f" Install dir : {os.path.join(home_dir, install_dir)}")
print()
print("[1/3] Installing ioquake3...")
install_ioquake3()
print()
print("[2/3] Downloading Quake 3 data files...")
zip_path = download_files(files_url)
print()
print("[3/3] Extracting data files...")
extract_files(zip_path, home_dir, install_dir)
print()
print("Done. Verify with:")
print(f" dpkg -l ioquake3")
print(f" ls {os.path.join(home_dir, install_dir)}")
if __name__ == "__main__":
main()