diff --git a/.github/scripts/check_gamefixes.py b/.github/scripts/check_gamefixes.py index e98229fc..9bf89f56 100644 --- a/.github/scripts/check_gamefixes.py +++ b/.github/scripts/check_gamefixes.py @@ -1,4 +1,6 @@ -import sys # noqa: D100 +"""This provides a check, if all filenames are correct and if all IDs used by GOG and Steam fixes are valid.""" + +import sys from pathlib import Path from urllib.request import urlopen, Request from http.client import HTTPSConnection diff --git a/.github/scripts/check_verbs.py b/.github/scripts/check_verbs.py new file mode 100644 index 00000000..1d3b1995 --- /dev/null +++ b/.github/scripts/check_verbs.py @@ -0,0 +1,97 @@ +"""This provides a check, if all used verbs are valid. It also warns if local verbs are unused.""" + +import os +import re +import subprocess + +from glob import iglob +from pathlib import Path +from tempfile import mkdtemp + +# 'gui' is a virtual verb for opening the Winetricks GUI +# 'vd=1280x720' is a setting for the virtual desktop and valid +whitelist_verbs = { 'gui', 'vd=1280x720' } + +def extract_verbs_from_glob(path_glob: iglob) -> set[str]: + """Simply strip the extension from all found files.""" + return { + file.stem + for file in path_glob + } + + +def find_verbs(root: Path) -> set[str]: + """Find all used verbs in gamefixes""" + verbs: set[str] = set() + game_fixes = root.glob('gamefixes-*/*.py') + + for fix in game_fixes: + f = fix.read_text() + r = re.finditer(r"util\.protontricks\s*\(\s*('|\")(?P.*)\1\s*\)", f, re.MULTILINE) + for match in r: + verbs.add(match.group('verb')) + + return verbs + + +def find_valid_verbs(root: Path) -> set[str]: + """Winetricks will create temporary files with metadata, these include all valid verbs.""" + # Check if winetricks is present and executable + wt_path = root.joinpath('winetricks') + if not wt_path.is_file() or not os.access(wt_path, os.X_OK): + raise FileNotFoundError('Winetricks can not be found or is not executable') + + # Provide a valid path to create the metadata to winetricks + tmp_dir = Path(mkdtemp()) + if not tmp_dir.is_dir() or not os.access(tmp_dir, os.W_OK): + raise PermissionError(f'Can not write into temporary folder "{tmp_dir}".') + + # Setup environment variables + env = os.environ.copy() + env['TMPDIR'] = tmp_dir + env['WINETRICKS_LATEST_VERSION_CHECK'] = 'disabled' + + # Execute winetricks, suppress output + print(f'Executing winetricks, using tmp path "{tmp_dir}" - this may take a moment.') + subprocess.run([wt_path, '--no-clean', 'list-all'], env=env, stdout=subprocess.DEVNULL) + + # Get all verbs + vars_glob = tmp_dir.glob('**/*.vars') + return extract_verbs_from_glob(vars_glob) + + +def main() -> None: + """Validate winetricks and protontricks verbs.""" + # Top-level project directory that is expected to contain gamefix directories + project = Path(__file__).parent.parent.parent + print(project) + + # Find all verbs that we use + verbs = find_verbs(project) + + # Find verbs that are in winetricks + valid_verbs = find_valid_verbs(project) + + # Additionally, we need to include our own verbs. + valid_verbs_local = extract_verbs_from_glob(project.glob('verbs/*.verb')) + + print(f'Local verbs: {len(valid_verbs_local)}') + print(f'Winetricks verbs: {len(valid_verbs)}') + print(f'Unique verbs used: {len(verbs)}') + + # Check for unused local verbs + unused_local_verbs = valid_verbs_local - verbs + if unused_local_verbs: + print(f'WARNING: The following local verbs are unused: {unused_local_verbs}') + + # Compare the results + #FIXME: Implement a more robust mechanism for "setting" type verbs (eg. "vd") + invalid_verbs = verbs - (valid_verbs | valid_verbs_local | whitelist_verbs) + if invalid_verbs: + raise ValueError(f'The following verbs are invalid: {invalid_verbs}') + + print('All verbs are valid!') + + +if __name__ == '__main__': + main() diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 963bbf98..238d0782 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,7 @@ jobs: - name: Validate gamefix modules run: | python3 .github/scripts/check_gamefixes.py + python3 .github/scripts/check_verbs.py - name: Test with unittest run: | python3 protonfixes_test.py diff --git a/gamefixes-umu/umu-model2.py b/gamefixes-umu/umu-model2.py index 309edeb0..32b7e490 100755 --- a/gamefixes-umu/umu-model2.py +++ b/gamefixes-umu/umu-model2.py @@ -10,4 +10,4 @@ def main() -> None: util.protontricks('d3dx9_42') util.protontricks('d3dx9') util.protontricks('xact') - util.protontricks('xact_64') + util.protontricks('xact_x64')