Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build-appimage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ jobs:
env:
# appimagetool runs without FUSE on CI runners
APPIMAGE_EXTRACT_AND_RUN: '1'
# Keep AppImages compatible with distributions that provide glibc
# 2.36. The build script rejects artifacts linked against newer versions.
MAX_GLIBC_VERSION: '2.36'
PYTHON: python
run: ./packaging/linux/build_appimage.sh

Expand Down
19 changes: 19 additions & 0 deletions Module/appconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,25 @@ def write_openkh_path(selected_directory):
update_app_config('openkh_folder', selected_directory)


def is_openkh_folder(selected_path: Path) -> bool:
"""Accept both extracted OpenKH installations and Linux config folders."""
mods_manager_names = (
"OpenKh.Tools.ModsManager.exe",
"OpenKh.Tools.ModsManager",
"OpenKh.Tools.ModsManager.Avalonia",
)
tool_directories = (selected_path, selected_path / "Apps")
has_mods_manager = any(
(tool_directory / name).is_file()
for tool_directory in tool_directories
for name in mods_manager_names
)

# Native Linux installations keep Mods Manager data in ~/.config/OpenKh.
has_mods_manager_config = (selected_path / "mods-manager.yml").is_file()
return has_mods_manager or has_mods_manager_config


def read_custom_music_path() -> Optional[Path]:
return _read_directory("custom_music_folder")

Expand Down
13 changes: 4 additions & 9 deletions UI/configui.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,12 @@ def openkh_folder_getter() -> bool:
return False

selected_path = Path(selected_directory)
mods_manager_names = (
"OpenKh.Tools.ModsManager.exe",
"OpenKh.Tools.ModsManager",
"OpenKh.Tools.ModsManager.Avalonia",
)
if not any((selected_path / name).is_file() for name in mods_manager_names):
if not appconfig.is_openkh_folder(selected_path):
show_alert("Not a valid OpenKH folder.")
return False
else:
appconfig.write_openkh_path(selected_directory)
return True

appconfig.write_openkh_path(selected_directory)
return True


def custom_music_folder_getter() -> bool:
Expand Down
5 changes: 5 additions & 0 deletions packaging/linux/build_appimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ BUILD_DIR=build/appimage
APPDIR="$BUILD_DIR/AppDir"
OUTPUT_NAME="KH2.Randomizer-x86_64.AppImage"
APPIMAGETOOL_SHA256="a6d71e2b6cd66f8e8d16c37ad164658985e0cf5fcaa950c90a482890cb9d13e0"
MAX_GLIBC_VERSION=${MAX_GLIBC_VERSION:-2.36}

if [ ! -f extracted_data.zip ]; then
echo "error: extracted_data.zip not found in the repo root (required for bundling)" >&2
Expand All @@ -34,6 +35,10 @@ cp packaging/linux/kh2randomizer.desktop "$APPDIR/"
"$PYTHON" -c "from PIL import Image; Image.open('rando.ico').save('$APPDIR/kh2randomizer.png')"
cp "$APPDIR/kh2randomizer.png" "$APPDIR/usr/share/icons/hicolor/256x256/apps/kh2randomizer.png"

# Fail before packaging if a local or CI build accidentally raises the Linux
# runtime requirement above the compatibility target.
packaging/linux/check_glibc_compatibility.sh "$APPDIR" "$MAX_GLIBC_VERSION"

APPIMAGETOOL=${APPIMAGETOOL:-appimagetool}
if ! command -v "$APPIMAGETOOL" >/dev/null 2>&1; then
APPIMAGETOOL="$BUILD_DIR/appimagetool-x86_64.AppImage"
Expand Down
32 changes: 32 additions & 0 deletions packaging/linux/check_glibc_compatibility.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# Verifies that every ELF binary in a directory supports the requested glibc.
set -euo pipefail

ROOT=${1:?usage: check_glibc_compatibility.sh DIRECTORY [MAX_GLIBC_VERSION]}
MAX_GLIBC_VERSION=${2:-2.36}
highest_version=0
highest_file=

while IFS= read -r -d '' file; do
if ! readelf -h "$file" >/dev/null 2>&1; then
continue
fi

version=$(readelf --version-info "$file" 2>/dev/null \
| grep -oE 'GLIBC_[0-9]+\.[0-9]+' \
| sed 's/^GLIBC_//' \
| sort -V \
| tail -n 1 || true)

if [ -n "$version" ] && [ "$(printf '%s\n%s\n' "$highest_version" "$version" | sort -V | tail -n 1)" = "$version" ]; then
highest_version=$version
highest_file=$file
fi
done < <(find "$ROOT" -type f -print0)

echo "Highest required glibc version: $highest_version${highest_file:+ ($highest_file)}"

if [ "$(printf '%s\n%s\n' "$MAX_GLIBC_VERSION" "$highest_version" | sort -V | tail -n 1)" != "$MAX_GLIBC_VERSION" ]; then
echo "error: build requires glibc $highest_version, newer than supported glibc $MAX_GLIBC_VERSION" >&2
exit 1
fi
19 changes: 19 additions & 0 deletions tests/test_configui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from Module import appconfig


def test_openkh_folder_accepts_linux_config_directory(tmp_path):
(tmp_path / "mods-manager.yml").write_text("installedModsPath: /tmp/mods\n")

assert appconfig.is_openkh_folder(tmp_path)


def test_openkh_folder_accepts_mods_manager_in_apps(tmp_path):
apps_path = tmp_path / "Apps"
apps_path.mkdir()
(apps_path / "OpenKh.Tools.ModsManager").write_text("tool")

assert appconfig.is_openkh_folder(tmp_path)


def test_openkh_folder_rejects_unrelated_directory(tmp_path):
assert not appconfig.is_openkh_folder(tmp_path)
Loading