Codex skill for development added. Tested by using it to set up Play Palace.

This commit is contained in:
Storm Dragon
2026-02-21 19:19:54 -05:00
parent efc5f20b5b
commit 1f7858e1fa
9 changed files with 675 additions and 0 deletions

View File

@@ -0,0 +1,202 @@
#!/usr/bin/env python3
"""Audit Linux Game Manager catalog consistency."""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
def read_first_line(file_path: Path) -> str:
try:
with file_path.open("r", encoding="utf-8") as handle:
return handle.readline().strip()
except OSError:
return ""
def has_run_update(file_path: Path) -> bool:
try:
content = file_path.read_text(encoding="utf-8")
except OSError:
return False
return "run_update()" in content or "run_update ()" in content
def collect_names(file_paths: list[Path], suffix: str) -> list[str]:
return sorted(file_path.name[: -len(suffix)] for file_path in file_paths)
def count_disabled(file_paths: list[Path]) -> list[str]:
disabledNames = []
for file_path in file_paths:
if read_first_line(file_path).startswith("#//"):
disabledNames.append(file_path.name)
return sorted(disabledNames)
def build_report(repo_root: Path) -> dict:
installDir = repo_root / ".install"
launchDir = repo_root / ".launch"
updateDir = repo_root / ".update"
missingDirs = [
str(path.relative_to(repo_root))
for path in [installDir, launchDir, updateDir]
if not path.exists()
]
if missingDirs:
raise FileNotFoundError(
f"Expected directories not found: {', '.join(missingDirs)}"
)
installPaths = sorted(installDir.glob("*.sh"))
launchGamePaths = sorted(launchDir.glob("*.game"))
launchShPaths = sorted(launchDir.glob("*.sh"))
updatePaths = sorted(updateDir.glob("*.sh"))
installerNames = collect_names(installPaths, ".sh")
launcherGameNames = collect_names(launchGamePaths, ".game")
launcherShNames = collect_names(launchShPaths, ".sh")
launcherSymlinkCount = sum(1 for path in launchShPaths if path.is_symlink())
launcherFileCount = len(launchShPaths) - launcherSymlinkCount
missingLauncherGameForInstaller = sorted(
set(installerNames) - set(launcherGameNames)
)
missingInstallerForLauncherGame = sorted(
set(launcherGameNames) - set(installerNames)
)
missingLauncherShForLauncherGame = sorted(
set(launcherGameNames) - set(launcherShNames)
)
orphanLauncherSh = sorted(set(launcherShNames) - set(launcherGameNames))
updateWithoutRunFunction = sorted(
path.name for path in updatePaths if not has_run_update(path)
)
report = {
"repoRoot": str(repo_root),
"criticalMismatches": [
"missingLauncherGameForInstaller",
"updateScriptsMissingRunUpdate",
],
"warningMismatches": [
"missingInstallerForLauncherGame",
"missingLauncherShForLauncherGame",
"orphanLauncherSh",
],
"counts": {
"installers": len(installPaths),
"launcherGameDefinitions": len(launchGamePaths),
"launcherRunnableEntries": len(launchShPaths),
"launcherRunnableSymlinks": launcherSymlinkCount,
"launcherRunnableRegularFiles": launcherFileCount,
"updateScripts": len(updatePaths),
},
"disabledEntries": {
"installers": count_disabled(installPaths),
"launchers": count_disabled(launchShPaths),
},
"mismatches": {
"missingLauncherGameForInstaller": missingLauncherGameForInstaller,
"missingInstallerForLauncherGame": missingInstallerForLauncherGame,
"missingLauncherShForLauncherGame": missingLauncherShForLauncherGame,
"orphanLauncherSh": orphanLauncherSh,
"updateScriptsMissingRunUpdate": updateWithoutRunFunction,
},
}
return report
def print_human_report(report: dict) -> None:
counts = report["counts"]
mismatches = report["mismatches"]
criticalMismatches = report["criticalMismatches"]
warningMismatches = report["warningMismatches"]
disabledEntries = report["disabledEntries"]
print(f"Repository: {report['repoRoot']}")
print("Counts:")
print(f" Installers: {counts['installers']}")
print(f" Launcher .game files: {counts['launcherGameDefinitions']}")
print(f" Launcher .sh entries: {counts['launcherRunnableEntries']}")
print(f" Symlinks: {counts['launcherRunnableSymlinks']}")
print(f" Regular files: {counts['launcherRunnableRegularFiles']}")
print(f" Update scripts: {counts['updateScripts']}")
print("Disabled entries:")
print(f" Installers: {len(disabledEntries['installers'])}")
if disabledEntries["installers"]:
print(" " + ", ".join(disabledEntries["installers"]))
print(f" Launchers: {len(disabledEntries['launchers'])}")
if disabledEntries["launchers"]:
print(" " + ", ".join(disabledEntries["launchers"]))
print("Critical checks:")
for key in criticalMismatches:
values = mismatches[key]
print(f" {key}: {len(values)}")
if values:
print(" " + ", ".join(values))
print("Warnings:")
for key in warningMismatches:
values = mismatches[key]
print(f" {key}: {len(values)}")
if values:
print(" " + ", ".join(values))
criticalTotal = sum(len(mismatches[key]) for key in criticalMismatches)
if criticalTotal == 0:
print("Result: OK (no critical mismatches)")
else:
print("Result: ATTENTION REQUIRED (critical mismatches detected)")
def parse_args() -> argparse.Namespace:
defaultRepoRoot = Path(__file__).resolve().parents[4]
parser = argparse.ArgumentParser(
description="Audit installer/launcher/update consistency for linux-game-manager."
)
parser.add_argument(
"--repo-root",
default=str(defaultRepoRoot),
help=f"Repository root path (default: {defaultRepoRoot})",
)
parser.add_argument(
"--json",
action="store_true",
help="Emit JSON instead of human-readable text",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
repoRoot = Path(args.repo_root).resolve()
try:
report = build_report(repoRoot)
except FileNotFoundError as error:
print(str(error), file=sys.stderr)
return 2
if args.json:
print(json.dumps(report, indent=2, sort_keys=True))
else:
print_human_report(report)
criticalTotal = sum(
len(report["mismatches"][bucket]) for bucket in report["criticalMismatches"]
)
return 1 if criticalTotal else 0
if __name__ == "__main__":
raise SystemExit(main())