Skip to content

Commit

Permalink
Merge pull request #72 from R1kaB3rN/update
Browse files Browse the repository at this point in the history
Update process when setting latest UMU-Proton
  • Loading branch information
R1kaB3rN authored Mar 27, 2024
2 parents 96eaead + b0337ce commit d9bb010
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 4 deletions.
76 changes: 73 additions & 3 deletions umu/umu_dl_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from umu_log import log
from umu_consts import STEAM_COMPAT
from tempfile import mkdtemp
from threading import Thread

try:
from tarfile import tar_filter
Expand Down Expand Up @@ -275,7 +276,39 @@ def _get_latest(

_fetch_proton(env, tmp, files)

_extract_dir(tmp.joinpath(tarball), steam_compat)
# Set latest UMU/GE-Proton
if version == "UMU-Proton":
threads: List[Thread] = []
log.debug("Updating UMU-Proton")
old_versions: List[Path] = sorted(
[
file
for file in steam_compat.glob("*")
if file.name.startswith(("UMU-Proton", "ULWGL-Proton"))
]
)
tar_path: Path = tmp.joinpath(tarball)

# Extract the latest archive and update UMU-Proton
# Will extract and remove the previous stable versions
# Though, ideally, an in-place differential update would be
# performed instead for this job but this will do for now
log.debug("Extracting %s -> %s", tar_path, steam_compat)
extract: Thread = Thread(target=_extract_dir, args=[tar_path, steam_compat])
extract.start()
threads.append(extract)
update: Thread = Thread(
target=_update_proton, args=[proton, steam_compat, old_versions]
)
update.start()
threads.append(update)
for thread in threads:
thread.join()
else:
# For GE-Proton, keep the previous build. Since it's a rebase
# of bleeding edge, regressions are more likely to occur
_extract_dir(tmp.joinpath(tarball), steam_compat)

environ["PROTONPATH"] = steam_compat.joinpath(proton).as_posix()
env["PROTONPATH"] = environ["PROTONPATH"]

Expand All @@ -289,7 +322,6 @@ def _get_latest(
tarball: str = files[1][0]

# Digest mismatched
# Refer to the cache for old version next
# Since we do not want the user to use a suspect file, delete it
tmp.joinpath(tarball).unlink(missing_ok=True)
return None
Expand All @@ -299,11 +331,49 @@ def _get_latest(

# Exit cleanly
# Clean up extracted data and cache to prevent corruption/errors
# Refer to the cache for old version next
_cleanup(tarball, proton_dir, tmp, steam_compat)
return None
except HTTPException: # Download failed
log.exception("HTTPException")
return None

return env


def _update_proton(proton: str, steam_compat: Path, old_versions: List[Path]) -> None:
"""Create a symbolic link and remove the previous UMU-Proton.
The symbolic link will be used by clients to reference the PROTONPATH
which can be used for tasks such as killing the running wineserver in
the prefix
Assumes that the directories that are named ULWGL/UMU-Proton is ours
and will be removed.
"""
threads: List[Thread] = []
old: Path = None
log.debug("Old: %s", old_versions)
log.debug("Linking UMU-Latest -> %s", proton)
steam_compat.joinpath("UMU-Latest").unlink(missing_ok=True)
steam_compat.joinpath("UMU-Latest").symlink_to(proton)

if not old_versions:
return

old = old_versions.pop()
if old.is_dir():
log.debug("Removing: %s", old)
oldest: Thread = Thread(target=rmtree, args=[old.as_posix()])
oldest.start()
threads.append(oldest)

for proton in old_versions:
if proton.is_dir():
log.debug("Old stable build found")
log.debug("Removing: %s", proton)
sibling: Thread = Thread(target=rmtree, args=[proton.as_posix()])
sibling.start()
threads.append(sibling)

for thread in threads:
thread.join()
71 changes: 71 additions & 0 deletions umu/umu_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,77 @@ def test_latest_offline(self):
self.assertFalse(self.env["PROTONPATH"], "Expected PROTONPATH to be empty")
self.assertFalse(result, "Expected None to be returned from _get_latest")

def test_latest_umu(self):
"""Test _get_latest when online and when an empty PROTONPATH is set.
Tests that the latest UMU-Proton was set to PROTONPATH and old
stable versions were removed in the process.
"""
result = None
latest = Path("UMU-Proton-9.0-beta16")
latest.mkdir()
Path(f"{latest}.sha512sum").touch()
files = [(f"{latest}.sha512sum", ""), (f"{latest}.tar.gz", "")]

# Mock the latest Proton in /tmp
test_archive = self.test_cache.joinpath(f"{latest}.tar.gz")
with tarfile.open(test_archive.as_posix(), "w:gz") as tar:
tar.add(latest.as_posix(), arcname=latest.as_posix())

# Mock old versions
self.test_compat.joinpath("UMU-Proton-9.0-beta15").mkdir()
self.test_compat.joinpath("UMU-Proton-9.0-beta14").mkdir()
self.test_compat.joinpath("ULWGL-Proton-8.0-5-2").mkdir()

# Create foo files and GE-Proton. We do *not* want unintended
# removals
self.test_compat.joinpath("foo").mkdir()
self.test_compat.joinpath("GE-Proton9-2").mkdir()

os.environ["PROTONPATH"] = ""

with (
patch("umu_dl_util._fetch_proton"),
):
result = umu_dl_util._get_latest(
self.env, self.test_compat, self.test_cache, files
)
self.assertTrue(result is self.env, "Expected the same reference")
# Verify the latest was set
self.assertEqual(
self.env.get("PROTONPATH"),
self.test_compat.joinpath(latest).as_posix(),
"Expected latest to be set",
)
# Verify that the old versions were deleted
self.assertFalse(
self.test_compat.joinpath("UMU-Proton-9.0-beta15").exists(),
"Expected old version to be removed",
)
self.assertFalse(
self.test_compat.joinpath("UMU-Proton-9.0-beta14").exists(),
"Expected old version to be removed",
)
self.assertFalse(
self.test_compat.joinpath("ULWGL-Proton-8.0-5-2").exists(),
"Expected old version to be removed",
)
# Verify foo files survived
self.assertTrue(
self.test_compat.joinpath("foo").exists(), "Expected foo to survive"
)
self.assertTrue(
self.test_compat.joinpath("GE-Proton9-2").exists(),
"Expected GE-Proton9-2 to survive",
)
self.assertTrue(
self.test_compat.joinpath("UMU-Latest").is_symlink(),
"Expected UMU-Latest symlink",
)

latest.rmdir()
Path(f"{latest}.sha512sum").unlink()

def test_steamcompat_nodir(self):
"""Test _get_from_steamcompat when Proton doesn't exist in compat dir.
Expand Down
2 changes: 1 addition & 1 deletion umu/umu_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def _update_umu(
if not local.joinpath("reaper").is_file():
log.warning("Reaper not found")
copy(root.joinpath("reaper"), local.joinpath("reaper"))
log.console(f"Restored {key} to {val} ...")
log.console(f"Restored {key} to {val}")

# Update
if val != reaper:
Expand Down

0 comments on commit d9bb010

Please sign in to comment.