Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CI: added check for verbs #128

Merged
merged 6 commits into from
Sep 10, 2024
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
4 changes: 3 additions & 1 deletion .github/scripts/check_gamefixes.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
97 changes: 97 additions & 0 deletions .github/scripts/check_verbs.py
Original file line number Diff line number Diff line change
@@ -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<verb>.*)\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()
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion gamefixes-umu/umu-model2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')