11708 Commits

Author SHA1 Message Date
Daniel Johnson
9f1043b177 Use the Wine version an installer script pins for winetricks
A script that pins a Wine version had that version used to create the
prefix, but winetricks then ran against the default Wine runner:
winetricks() discarded the wine_path it was given and asked a
default-configured runner for its executable. On a stock config that
put umu-run in $WINE, so every Wine call winetricks made went through
Proton, into a prefix built by a different Wine.

That fallback was correct when it was written - it was the else of
'if wine_path' - but 1ae2fe108 restructured the conditional so the
no-wine_path case is handled by the Umu branch, leaving the fallback
as the path taken when wine_path *is* set.

The default runner leaked into the other branches too:

- The Umu branch left wine_path unset, so wineexec() fell back to the
  default runner's executable. That is only Umu by coincidence; with a
  plain Wine default it ran 'wine winetricks <verbs>'.

- wineexec() builds its environment from the runner first, and
  Wine.get_env() sets PROTONPATH when the *runner's* configured
  version is ge-proton. update_proton_env() will not replace an
  existing value, so a script pinning a specific Proton still got
  GE-Proton.

- get_installer_runner_version() normalizes the pinned version, and
  format_version_architecture() exempted only 'GE-Proton' from the
  architecture suffix, not the lower-case 'ge-proton' sentinel. That
  turned 'ge-proton' into 'ge-proton-x86_64', which resolves to a Wine
  path that does not exist, and was also written into the installed
  game's runner config.

Verified with the script attached to the issue: winetricks reported
"with umu-launcher version 1.4.4" before and "with wine-11.13
(Staging)" after, with all 38 Wine invocations using the pinned build.

Resolves #6804

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-26 13:10:55 -04:00
Daniel Johnson
0e72a98f76 Merge pull request #6810 from mbarrio/master
Update Spanish translation
2026-07-25 17:37:16 -04:00
Miguel Barrio Orsikowsky
b0a94de9ac Update Spanish translation 2026-07-25 09:29:28 +02:00
Daniel Johnson
1b0f116788 Merge pull request #6803 from ItsAllAboutTheCode/unit-test-importlib-fix
Fixed Unit Test builds when running on CI / Enabled util test
2026-07-21 19:02:52 -04:00
ItsAllAboutTheCode
eb72dca3c2 Fixed Unit Test builds when running on CI
Removed the importlib machinery used in `_test_gog_dlcs.py` and `_test_collection_progress.py` to fix issues with due to test module discovery order causing bespoke mocked modules to be placed in the sys.modules.

Instead the builtin Python UnitTest Mock and Patch framework is used to properly patch the methods needed to run the UnitTest instead of manipulating the importlibs and a bespoke manner.

Added missing `__init__.py` to the `lutris/tests/util` directory to allow discovery of those UnitTest by the nose2 and the Python builtin UnitTest framework.
Those test weren't not being run by either `nose2` nor the `UnitTest` framework.

Fixed the UnitTest that imports the pytest module.
Replaced the usage of `pytest` inside of the `lutris/tests/util` directory to use the python UnitTest framework. Lutris does NOT specify a dependency on `pytest `and therefore when those test were actually enabled to be run, they would complain about the missing `pytest` import.
2026-07-21 13:03:26 -05:00
Daniel Johnson
8663afc110 wine: treat ge-proton (umu) as Proton in runtime guards
The effective 'ge-proton' version resolves via get_executable() to the
umu-run launcher, which is is_umu_path() but not is_proton_path(). Several
guards gated Proton-specific behavior on is_proton_path() alone, so they
silently failed for the default ge-proton setup:

- set_regedit_keys(): the Windowed (virtual desktop) option is hidden for
  Proton but its stale setting was still written to the prefix (#6795); the
  winewayland Graphics key was likewise written instead of skipped.
- get_dll_managers(): Lutris' own DXVK/VKD3D managers were left enabled and
  injected on top of the ones Proton already ships.
- get_env(): gstreamer override guard (latent; files_dir is None here).
- get_wine_executable_pids() / filter_game_pids(): PID tracking/filtering
  used the non-Proton path despite umu running under pressure-vessel.

Extend each to `is_proton_path(exe) or is_umu_path(exe)`, matching the idiom
already used in get_command() and finish_env().

Fixes #6795

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-14 19:41:43 -04:00
Daniel Johnson
d6b2595cd7 Merge pull request #6794 from ford220102/patch-1
Update pre-commit
2026-07-14 19:07:27 -04:00
Daniel Johnson
af9254581d storage: make oversized BIOS folders a warning instead of an error
The BIOS path validation rejected folders larger than 5 GB (and folders
with too many files or too deep a hierarchy) outright, so legitimate
full BIOS packs like retrobios could not be used. Raise the size limit
to 8 GB and turn all three checks into non-blocking warnings that
explain scanning may be slow, so the path is still saved and used.

Also re-run the check in populate() so the warning persists when the
settings are reopened, not just when the path is edited.

Fixes #6783

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-14 17:03:18 -04:00
Daniel Johnson
49e8060659 Merge pull request #6796 from victorsouzaleal/translation-pt-br-update
translation: updated pt_BR
2026-07-14 16:44:17 -04:00
victorsouzaleal
f599d0020f translation: updated pt_BR 2026-07-14 11:53:32 -03:00
ford220102
67590b97c9 Update pre-commit
The previous commit fixed two logic bugs in .hooks/pre-commit but
explained them via inline "# FIX:" comments, which reviewers noted
should live in the PR description instead. This commit removes those
comments; the code changes themselves are unchanged.

See PR description for details on the two bugs that were fixed:
1. Incorrect boolean parsing of `git config --type=bool` output
2. Wrong variable interpolated in the "command is missing" message
2026-07-14 07:16:27 +02:00
ford220102
132b9bf228 Update pre-commit 2026-07-13 07:08:33 +02:00
Daniel Johnson
07470764e7 xenia: fix the download URL and the launch command
The download pointed at the xenia-canary-releases mirror, which went
stale and renamed its asset, so runner installation failed with a 404.
Point it at the active xenia-canary repo's Windows build
(xenia_canary_windows.7z); Lutris extracts .7z like .zip, so the
extracted xenia_canary.exe still lands where the runner expects it.

Also fix launching: the runner is Wine-based, and with a umu/Proton
wine version get_command() returns just the umu-run launcher. play()
appended the Xenia exe, but running the runner standalone did not, so
umu-run was invoked with no executable and only printed its usage.
Override get_command() to append the Xenia binary so both play() and
the standalone path launch the emulator; play() now just adds the game.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-12 11:04:57 -04:00
Daniel Johnson
2383cdf1a2 wine: finalize the Proton/umu env for standalone runs too
Launching a game calls Runner.finish_env(), which for the wine runner is
where the Proton/umu environment (GAMEID, PROTONPATH, PROTON_VERB, ...)
gets set up via update_proton_env(). The standalone run() path used by
runnable-alone runners goes through get_run_data() instead and never
called finish_env(), so a umu/Proton wine version launched on its own was
missing that setup.

Add wine.get_run_data() that reuses finish_env() to finalize the same
environment, passing no game (Proton/umu then falls back to a default
GAMEID). finish_env() now accepts game=None to make that context explicit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-12 11:04:32 -04:00
Daniel Johnson
3b03c7bb6e libretro: honor custom RetroArch cores directory
When a custom RetroArch executable and config file are used, Lutris still
forced the --libretro core path to its own managed cores folder
(~/.local/share/lutris/runners/retroarch/cores), so the custom RetroArch
could not find the core and refused to launch.

get_core_path() now resolves the cores directory from the 'libretro_directory'
setting in the effective RetroArch config file (expanding a leading ':' to the
config file's directory, as RetroArch does), falling back to Lutris's managed
cores folder when unset. This also makes is_installed() check the real core
location.

Fixes #6784

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-05 14:57:07 -04:00
Daniel Johnson
bc460440f6 sysinfo: add a Lutris section with the default Wine version
Group the Lutris application facts (Lutris version, Python version) into
their own [Lutris] section rather than lumping them under [System], and
add the Wine version configured in the Wine runner configuration there.
This surfaces it in the Preferences panel and in copied bug reports.

Fixes #6786

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-05 10:19:26 -04:00
Daniel Johnson
b3e3c3c2d6 Merge pull request #6782 from masonliiu/fix-moddb-resolution-fallback
installer: fall back to file selection when ModDB resolution fails
2026-07-04 09:20:55 -04:00
masonliiu
ed453f53a8 installer: fall back to file selection when ModDB resolution fails 2026-07-04 05:45:53 -05:00
Daniel Johnson
7550fe8731 appimage: install a modern setuptools + packaging in the build image
Noble's apt-shipped setuptools (68.1.2) predates PEP 639 SPDX string
license support, added in setuptools 77.0.0. Since pyproject.toml
declares `license = "GPL-3.0-or-later"` in that form, `setup.py
install` now aborts with

    configuration error: `project.license` must be valid exactly by
    one definition (2 matches found)

on every AppImage build.

Add three things to the existing pycairo pip install:

* `setuptools>=77` — the actual PEP 639 fix.
* `packaging>=24.2` — setuptools 77+ delegates SPDX normalization to
  `packaging.licenses`, which landed in packaging 24.2; Noble ships
  24.0, so the setuptools upgrade alone raises
  `ImportError: Cannot import packaging.licenses`.
* `--ignore-installed packaging` — Noble's `python3-packaging` is
  apt-managed with no RECORD file, so pip refuses to upgrade it in
  place ("Cannot uninstall packaging 24.0"). Ignoring the installed
  copy lets the newer packaging land alongside and shadow it on
  sys.path, without disturbing dpkg's view of the system.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-03 09:44:34 -04:00
Daniel Johnson
299ef057ea Widen the sidebar slightly so it better fits the download queue UI when that appears (so it does not need to widen then) 2026-07-03 09:00:13 -04:00
Mathieu Comandon
27ac9b3e90 fix: skip runner install for umu-managed Wine versions
The "ge-proton" sentinel and Proton builds are launched through umu, which
fetches Proton on demand. There is no downloadable Wine runner build for these
versions, so treating them as an installable runner makes the installer flow
call Runner.install(), which fails with "The 'ge-proton' version of the 'wine'
runner can't be downloaded" (e.g. when installing Ubisoft Connect while latest
Wine is already set up).

Recognize umu-managed versions in the Wine runner instead of guarding the
generic base install():

- is_installed_for() returns True for umu-managed versions, so they are never
  added to runners_to_install.
- Wine.install() no-ops (and fires the callback) for umu-managed versions,
  covering the install-runner dialog and CLI install paths too.

Specific pinned Wine builds still go through the normal download path, so a
required-but-missing version is still installed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 23:03:25 -07:00
Mathieu Comandon
02385542bf Only set Wine Mono/Gecko cache dirs when they exist
create_prefix() unconditionally pointed WINE_MONO_CACHE_DIR and
WINE_GECKO_CACHE_DIR at <wine_root>/mono and <wine_root>/gecko. This is
correct for Lutris-managed Wine builds, which bundle the installers there,
but for system Wine the path resolves to a non-existent location (e.g.
/usr/mono), so wineboot found no cached installer and created prefixes
without Mono/Gecko. Only set these variables when the directories actually
exist, letting system Wine fall back to its own default discovery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 23:03:25 -07:00
silverhikari
564dfba382 update url to release 0.3.0 2026-07-02 17:15:41 -07:00
silverhikari
8e0a4b1d05 update ruffle version as nearly a year old 2026-07-02 17:15:41 -07:00
ItsAllAboutTheCode
9748c64152 Adding Lutris Project info to pyproject.toml
This allows tools such as uv and pip to install the project dependencies
ex. `uv sync --group dev` or `pip install -e .`
2026-07-02 10:11:33 -07:00
Daniel Johnson
7f996c589e appimage: bundle adwaita-icon-theme so GtkSpinner renders on Fedora
Gtk 3's Adwaita CSS (compiled into libgtk-3 as a GResource) draws
GtkSpinner via -gtk-icontheme("process-working-symbolic"). Recent
adwaita-icon-theme releases (Fedora 43 ships 49) dropped that icon,
and linuxdeploy-plugin-gtk doesn't bundle icon themes — it only
extends XDG_DATA_DIRS to /usr/share so lookups fall through to the
host. On Fedora hosts the lookup fails: the spinner widget occupies
its CSS min-size but shows no visible content. Sidebar service
reloads and Preferences dialogs both looked frozen even though the
underlying operation was progressing.

Add adwaita-icon-theme to apt and copy /usr/share/icons/Adwaita into
$APPDIR/usr/share/icons/Adwaita. Since AppRun places $APPDIR/usr/share
before /usr/share in XDG_DATA_DIRS, GTK finds the bundled Noble copy
(which still has the icon) before the host's newer, trimmed one.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-01 20:31:12 -04:00
Daniel Johnson
116916c060 appimage: bump base image to Ubuntu 24.04 for newer WebKit2 4.1
Jammy's WebKit2GTK 4.1 point release (2.42) has a bug that kills the
WebKitWebProcess after one input event on the ZOOM Platform login
page; once the renderer dies the dialog freezes, stops repainting,
and accepts no further input. The dialog frame itself stays alive
so it wasn't obvious from the outside — the page just seemed to
hang. Non-AppImage runs weren't affected because they load the
host's newer WebKit.

Noble's 2.44+ point release fixes it. Bump the base image, update
apt package names where they've been renamed in the t64 transition
(libfuse2 → libfuse2t64, libgnome-desktop-3-19 →
libgnome-desktop-3-20t64), switch to apt's meson (Noble's is
current enough for meson-python), and pass --break-system-packages
to pip since Noble's system Python is PEP 668-managed.

Trade-off: glibc floor moves from 2.35 to 2.39, so the AppImage
stops running on hosts older than roughly 2024 distros (Ubuntu 22.04
and Debian 12 are too old). Documented in the Dockerfile header.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-01 19:49:12 -04:00
Daniel Johnson
dcb0f518e1 appimage: make WebKit login work on non-Ubuntu hosts
Three related bugs prevented the AppImage's WebKit-based service
login from working on Fedora / Arch / openSUSE — invisible on Ubuntu
because the host happens to satisfy the AppDir's missing pieces.

1. TLS backend not bundled. linuxdeploy-plugin-gtk copies libgio but
   not the GIO modules directory that holds glib-networking's
   libgiognutls.so. Without it GIO falls back to a compile-time libdir
   that doesn't exist off-Debian, and WebKit reports "TLS is not
   available" on the first HTTPS request. Copy the modules directory
   into $APPDIR/usr/lib/gio/modules with an $ORIGIN/../.. RPATH, add
   glib-networking to apt explicitly (it was pulled transitively but
   its modules weren't being copied), and export GIO_MODULE_DIR in
   AppRun.

2. WebKit shared libraries not bundled. libwebkit2gtk-4.1 and
   libjavascriptcoregtk-4.1 are loaded lazily via gi.repository when
   the user clicks a login button, so they never appear in any
   binary's NEEDED chain — linuxdeploy has no reason to bundle them.
   On non-Ubuntu hosts dlopen() falls through to the host copies with
   different symbols, e.g. `undefined symbol: webkit_web_context_new`.
   Pass both sonames to linuxdeploy explicitly.

3. WebKit helper processes not bundled and hardcoded path unreachable.
   WebKit2 4.1 forks WebKitNetworkProcess / WebKitWebProcess from a
   compile-time path baked into libwebkit2gtk-4.1.so.0
   (/usr/lib/x86_64-linux-gnu/webkit2gtk-4.1/). linuxdeploy bundles
   shared libraries, not auxiliary executables, and WEBKIT_EXEC_PATH
   was removed upstream — the path is only overridable by byte-patching
   the .so. Copy the webkit2gtk-4.1/ directory into the AppDir with
   $ORIGIN/.. RPATH on each helper, and byte-patch the hardcoded
   compile-time path inside libwebkit2gtk-4.1.so.0 to a writable
   /tmp/.lutris-appimage.dir/webkit2gtk-4.1 (same byte length). AppRun
   creates the symlink there before launch.

Also disable WebKit's bubblewrap sandbox
(WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS=1): bwrap opens a fresh
mount namespace that can't see the AppImage's FUSE mount at
/tmp/.mount_LutrisXXX, so any helper spawned inside it fails to reach
either the symlink target or the bundled libs. Lutris already runs
unsandboxed and spawns Wine, so an additional WebKit sandbox buys
negligible isolation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-01 19:32:36 -04:00
Daniel Johnson
d0fad86485 Add AppImage to changelog 2026-07-01 15:38:17 -04:00
Mathieu Comandon
e77226fc27 Raise on wine prefix creation failures instead of returning silently
create_prefix() logged an error and returned on the three failure paths
(missing wineboot, umu crashing during prefix creation, and no user.reg
afterwards). Callers had no way to tell a successful prefix creation from
a failed one, so the install would continue against a broken or missing
prefix. Raise RuntimeError with a translatable message instead, and give
the umu and user.reg cases more actionable text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 00:53:07 -07:00
Mathieu Comandon
36e3f7c1e9 Default to 64-bit prefixes on 64-bit hosts with modern Wine
detect_arch() inferred a 32-bit-only Wine build from the absence of a
separate 'wine64' binary, then fell through to "win32" for prefixes that
don't exist yet. Modern Wine builds using the new WoW64 mode ship a single
'wine' binary with no 'wine64', so new prefixes were wrongly created as
32-bit. Fall back to WINE_DEFAULT_ARCH instead, so 32-bit is only used on a
32-bit host or when the user explicitly selects it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:42:09 -07:00
Daniel Johnson
d1a1a45d81 Stop the AppImage from forcing the Adwaita GTK theme
linuxdeploy-plugin-gtk's generated AppRun hook unconditionally sets
GTK_THEME to "Adwaita:light/dark" with a comment that "custom themes
are broken." The "broken" claim is from 2018 and is disputed in
upstream #39 (open since 2023-02 with no maintainer response); the
practical effect is that Lutris's AppImage looks Adwaita-ish on every
host regardless of the user's actual GTK theme — visibly out of place
on KDE, breaks any custom theming, and undoes whatever care the user
took with their desktop.

Patch the vendored hook generator so GTK_THEME is exported only when
the caller explicitly opted in via APPIMAGE_GTK_THEME. The default is
no override at all, letting the host's GTK_THEME (or its absence)
take effect, which is what every other GTK app on the system does.

The colour-scheme detection block above the override is now dead
weight — it existed solely to choose between Adwaita:dark and
Adwaita:light — so drop it.

Each modification is annotated "LUTRIS-MOD" in the vendored script,
matching the convention promised in its file header.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 13:29:57 -07:00
Daniel Johnson
2f6eec4189 Vendor linuxdeploy-plugin-gtk at a pinned commit
The plugin's upstream (linuxdeploy/linuxdeploy-plugin-gtk) has had no
commits since 2023-10-01 and feedback on #6726 flagged its dormancy
as a concern. Fetching the script from `master` on every build also
means the AppImage we ship is implicitly tied to whatever upstream
serves at build time, which is not reproducible and not auditable.

Vendor the script at commit 3b67a1d1 alongside its MIT licence. The
Dockerfile now COPYs the local copy into the build image instead of
curl-ing it from raw.githubusercontent.com, so the AppImage build is
reproducible from the repo alone and we have a place to apply local
patches if upstream stays dead (a separate follow-up commit drops the
plugin's hardcoded GTK theme override, which is the first such patch).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 13:29:57 -07:00
Daniel Johnson
b38c229b0d Stop leaking AppImage PYTHONPATH and PATH into host subprocesses
Previous AppRun exported PYTHONPATH pointing at the bundled stdlib and
prepended $APPDIR/usr/bin to PATH. Both leaked into every host
subprocess Lutris launches: a host umu-launcher invoked via its
`#!/usr/bin/env python3` shebang picked up the bundled Python 3.10
instead of the host's (no Xlib, no requests, no umu deps), and any
host Python that did get used inherited a PYTHONPATH pointing at our
3.10 stdlib (MAGIC mismatch in _sre on Fedora's 3.14).

Both exports were structurally unnecessary: AppRun already execs the
bundled interpreter by full path, so PATH does not need our bin
directory at the front, and switching setup.py to --install-layout=deb
makes Lutris findable via the bundled Python's default sys.path search
(dist-packages, what Debian-patched Python uses) without an env-var
hint.

Net effect: host umu-launcher, wine, xrandr, etc. now inherit a clean
PATH and no PYTHONPATH from the AppImage. The AppImage still requires
host Python 3.10+ for runners that themselves call Python (UMU is the
practical case); modern distros satisfy this by default.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 13:29:57 -07:00
Daniel Johnson
af9a32b110 Install PyGObject and dbus-python via pip instead of apt-copying
Replaces the manual `cp -a /usr/lib/python3/dist-packages/{gi,cairo,
dbus,_dbus_*}` block with `pip install --target` on PyGObject<3.50,
dbus-python, and pycairo, so every runtime Python dep in the AppImage
comes from a single source we can version-pin instead of being
silently tied to whatever Ubuntu 22.04's apt happens to ship.

This requires the build image to be able to compile both packages
from source — they ship as sdist only and have moved to meson-python
as their build backend — so the Dockerfile gains:

* meson via pip (>=0.63.3; apt's 0.61 is too old for meson-python)
* pycairo via pip in the build image, so PyGObject's meson can find
  its C headers (apt's python3-cairo ships no headers; the python3-
  cairo and python3-gi-cairo apt packages are no longer needed and
  have been dropped)
* ninja-build, libdbus-glib-1-dev — meson's runner and dbus-python's
  GLib mainloop bindings respectively

PyGObject is pinned <3.50 because 3.50 switched to the girepository-
2.0 ABI introduced in GLib 2.80, which Ubuntu 22.04 (GLib 2.72)
doesn't have. Apt's python3-gi 3.42 stays installed for build-time
use — setup.py imports lutris/__init__.py which calls
gi.require_version() at import time.

The patchelf RPATH step from the previous commit still applies: pip
installs land in dist-packages with no RPATH set, and linuxdeploy
only patches the copies it makes (in usr/lib), not the originals
where Python actually loads them from.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 13:29:57 -07:00
Daniel Johnson
4b7d73332c Strip Python's config-* from the AppImage's bundled stdlib
The usr/lib/python3.10/config-3.10-x86_64-linux-gnu/ directory holds
link-time-only artifacts (Makefile, libpython symlinks, and a
relocatable object file `python.o` that wraps Py_Main for embedding
Python in a custom executable). None of it is referenced when running
Python code, but the relocatable object made linuxdeploy emit a
spurious "patchelf: wrong ELF type" error on every build — patchelf
refuses to touch ET_REL files, linuxdeploy logs the failure and moves
on. Strip the directory along with the other developer-only chunks of
the stdlib already removed; saves a few MB and leaves the build log
free of distracting errors.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 13:29:57 -07:00
Daniel Johnson
e5c10089e2 Publish AppImage as a release asset via GitHub Actions
Wires up the AppImage build to the existing release process: when a
release (or prerelease) is published, a new workflow spins up an
ubuntu-22.04 runner, runs `make appimage` (same container path
developers use locally, so the artifact is bit-for-bit identical),
and attaches the resulting Lutris-<version>-x86_64.AppImage to the
release using the bundled gh CLI.

A workflow_dispatch trigger is included so the build can be exercised
from the Actions tab without cutting a release; in that mode it stops
at the workflow-artifact upload and never touches a release.

The job declares `contents: write` explicitly because the repo's
default workflow permissions are read-only (verified via
`gh api repos/lutris/lutris/actions/permissions/workflow`), and
attaching a release asset is a contents write under GitHub's
permission model. All actions used are first-party (actions/checkout,
actions/upload-artifact) to match the rest of .github/workflows/.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 13:29:57 -07:00
Daniel Johnson
b88b3fe580 Default LUTRIS_VERSION from lutris/__init__.py in AppImage build
Previously `make appimage` produced Lutris-dev-x86_64.AppImage unless the
caller remembered to set LUTRIS_VERSION. On a tagged release commit that
yielded a misleadingly-named artifact. Have build.sh awk the version out
of lutris/__init__.py when no explicit value is provided; an env override
(e.g. LUTRIS_VERSION=0.5.23-rc1 for a release candidate) still wins.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 13:29:57 -07:00
Daniel Johnson
7ecd2ba969 Add AppImage packaging proof-of-concept
Flathub's recent ban on AI-using projects (see #6724) puts Lutris's
Flatpak channel at risk, and Flatpak has always been an awkward fit
anyway: Lutris is fundamentally an unsandboxed game manager that needs
to call host xrandr, 7z, fuser, wine, flatpak, mount drives, and reach
the user's whole $HOME. AppImage gives us a single-file distribution
that does none of that sandboxing.

This adds `make appimage`, which builds a self-contained AppImage in
a Docker/Podman container based on Ubuntu 22.04 (glibc 2.35, Python
3.10, GTK3, WebKit2GTK 4.1). The pipeline:

  utils/appimage/build.sh          — host wrapper (docker or podman)
  utils/appimage/Dockerfile        — build env with all GI typelibs
  utils/appimage/build-in-container.sh
                                   — assembles AppDir via linuxdeploy
                                     + linuxdeploy-plugin-gtk, runs
                                     appimagetool
  utils/appimage/AppRun            — launcher; sources plugin-gtk hook,
                                     sets PYTHONPATH, execs bundled
                                     python3 with bin/lutris

Design notes worth flagging for review:

* The bundled Python is the build image's /usr/bin/python3.10 plus its
  stdlib, with tkinter/test/idle stripped.

* PyGObject and dbus-python are taken from apt (python3-gi, python3-dbus)
  rather than pip — both have switched to meson-python build backends
  that drag in a sizeable toolchain, and the prebuilt packages link
  against exactly the libgirepository/libdbus we already bundle from
  the same distro.

* AppRun deliberately does NOT export LD_LIBRARY_PATH. linuxdeploy
  already sets $ORIGIN-relative RPATH on every binary it deploys, and
  exporting LD_LIBRARY_PATH would leak into every host subprocess —
  which surfaced immediately when flatpak crashed loading our bundled
  libssl 3.0 instead of the host's 3.4. The build script patchelf's
  the RPATH on the manually-copied _gi.so / _dbus*.so so they still
  find the bundled libgirepository without the env override.

* PATH is left intact apart from prepending $APPDIR/usr/bin, so host
  tools (xrandr, 7z, wine, flatpak) still win — that's the whole
  point of choosing AppImage over Flatpak here.

Output is dist/Lutris-<version>-x86_64.AppImage at ~83 MB. Smoke-tested
end-to-end on Fedora 43 Nobara: boots GUI, GPU detection, DB load,
service auth, GTK theme, and host subprocess invocations all work.

Refs #6724.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 13:29:57 -07:00
Mathieu Comandon
338e6f5980 docs: document gogdl_setup and merge's remove_source option
Add an installer reference section for the gogdl_setup depot-download
directive (parameters, game_id defaulting to the installer gogid, and the
typical merge-to-flatten pattern), a worked Quake III Arena + Quake3e
example, and document the new remove_source parameter on merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 02:42:07 -07:00
Mathieu Comandon
8ce4e90a6e Let installer scripts run their own engine over a gogdl depot download
The gogdl_setup depot path was only usable by the auto-generated GOG
installers. Three issues blocked community scripts that supply their own
engine (e.g. a native source port over GOG game data):

- _apply_gogdl_post_install() used dict.update() to apply the config
  detected from goggame-*.info, clobbering an `exe` the script set
  explicitly with the Windows executable. Use setdefault() so detection
  only fills gaps and never overrides script-provided config. The
  auto-generated installers leave these keys unset, so they are
  unaffected.

- merge gained an opt-in `remove_source` parameter that deletes the
  source tree once merged, so the install-directory subfolder a depot
  download creates can be flattened into $GAMEDIR without duplicating
  the (often large) game data.

- gogdl_setup's game_id now defaults to the installer's resolved gogid,
  so scripts don't have to repeat the product id. An explicit game_id
  still wins (e.g. for downloading a DLC).

Also resolve a relative entry point against the install directory in
_finish_install() before the existence check, matching how the runner
resolves it at launch, so scripts using a relative exe no longer trigger
a spurious "No executable found" warning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 02:42:07 -07:00
Mathieu Comandon
47fcc65c88 Filter noisy Vulkan loader warnings out of GPU detection output
vulkaninfo lets the Vulkan loader print warnings to stderr for every ICD
that fails to initialize on the host (e.g. Mesa's dzn/Direct3D12 driver,
which has no D3D12 backend outside of WSL), and these were passing straight
through to the terminal during GPU detection.

Capture vulkaninfo's stderr via a new optional stderr_handler on
read_process_output and route it through log_vulkan_loader_messages, which
surfaces genuine errors and drops the benign loader warnings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 23:48:28 -07:00
Mathieu Comandon
ab78b8a1e9 Make the Lutris.net login sync banner dismissable
The first-launch banner prompting users to log in to Lutris.net to sync
their library had no close button and reappeared on every restart. Add a
close button (mirroring the unsupported-version banner) that persists a
"dismissed_login_notification" setting so it stays hidden across restarts.
Logging in remains available from the preferences window.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 23:03:23 -07:00
Daniel Johnson
b9527a5205 Adjust hack for supplying the platform when GOG fails to supply it. Use get() for better tolerance of changing JSON shape, and log errors correctly.
For issue #6755
2026-06-29 16:41:08 -04:00
Daniel Johnson
d2b9baca4d Merge pull request #6767 from gnarz/workaround_for_#6755
created a workaround for issue #6755
2026-06-29 16:31:46 -04:00
Gunnar Zötl
cc1494e89f the ruff pre commit hook had a remark, fixed 2026-06-29 18:01:04 +02:00
Gunnar Zötl
94c4db0fb8 created a workaround for issue #6755
after reading the entire product list from gog, for all products that
have all worksOn entries set to False, we check with the url
https://api.gog.com/v2/games/<id> which OSes are actually supported.
This URL is from the gogdb website. It will not work for games that
are in the inventory, but are no longer sold by gog. But until there is
a proper solution, this will allow installation of games again.
2026-06-29 17:54:57 +02:00
Daniel Johnson
2517a2dbc2 Merge pull request #6761 from ghostrunner42/fix/tray-exits-on-external-game-stop
Fix/tray exits on external game stop
2026-06-28 17:58:07 -04:00
Ghost Runner 42
fbb223d654 Fix tray icon exit when externally-launched game stops
When a game is launched via a lutris: URI (e.g. from a .desktop file),
quit_on_game_exit was unconditionally set to True. This caused Lutris to
quit when the game stopped — even if Lutris was already running with a
tray icon and an open window.

The flag should only trigger when Lutris has no persistent presence (no
tray icon, no visible window), i.e. it was launched purely to run one
game and should clean up after itself.

Fixes #6749

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:35:33 -04:00
Daniel Johnson
e037577178 Merge pull request #6747 from KogasaPls/master
Fix filesystem detection for Btrfs subvolume paths
2026-06-28 07:28:21 -04:00