From a4f9a67da1bcc3fef9fe8bb6fd9bbc84f0cc642b Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 21 Jul 2026 15:01:11 +0900 Subject: [PATCH 01/10] fix: use drop-in mixin to set page progress state --- dzgui/views/components/entry.py | 3 ++- dzgui/views/dialogs/wizard.py | 16 +++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dzgui/views/components/entry.py b/dzgui/views/components/entry.py index 88fd7e2..ee652a0 100644 --- a/dzgui/views/components/entry.py +++ b/dzgui/views/components/entry.py @@ -162,7 +162,8 @@ class APIEntry(Gtk.Box): self.entry.connect("icon-release", self._on_icon_release) self.entry.connect("activate", self._on_field_activated) - self.submit = Gtk.Button(label="Submit") + # TODO: strings + self.submit = Gtk.Button(label="Validate") self.submit.set_sensitive(False) self.submit.connect("clicked", self._on_submit) diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index 651ec7a..9a5b2cd 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -49,6 +49,11 @@ class PageNum(Enum): SHORTCUTS = 7 FINAL = 8 +class OptionalPageMixin: + """Marks optional pages as advanceable""" + def _on_map(self, page: "ScrolledWizardPage") -> None: + EMITTER.emit("step_complete") + class DescriptionArea(Gtk.Box): def __init__(self, text: str): @@ -91,6 +96,7 @@ class ScrolledWizardPage(Gtk.ScrolledWindow): margin_top=50, spacing=20, ) + self.add(self.box) self.prog = Progress() self.box.pack_end(self.prog, expand=False, fill=False, padding=0) @@ -215,7 +221,7 @@ class APIValidationPage(ScrolledWizardPage): self.spinner.stop() -class BMValidationPage(APIValidationPage): +class BMValidationPage(OptionalPageMixin, APIValidationPage): def __init__(self) -> None: super().__init__( enum=PageNum.BM_API, @@ -224,6 +230,7 @@ class BMValidationPage(APIValidationPage): link=BM_API_SETUP, func=self._validate, ) + self.connect("map", self._on_map) @call_on_thread("", show_dialog=False) def _validate(self, key: str) -> None: @@ -570,8 +577,6 @@ class Assistant(Gtk.Assistant): # NOTE: disable forward action # TODO: use page enums - if page == self.page5: - return if page != self.page1: EMITTER.emit("step_pending") @@ -599,7 +604,7 @@ class CheckboxWithLabel(Gtk.Box): self.button.set_active(state) -class ShortcutCreationPage(ScrolledWizardPage): +class ShortcutCreationPage(OptionalPageMixin, ScrolledWizardPage): def __init__(self, shortcut: Path) -> None: super().__init__( enum=PageNum.SHORTCUTS, @@ -642,9 +647,6 @@ class ShortcutCreationPage(ScrolledWizardPage): self.desktop_checkbox.set_active(state) self.desktop_checkbox.set_sensitive(state) - def _on_map(self, page: "ScrolledWizardPage") -> None: - EMITTER.emit("step_complete") - def set_steam_path(self, path: Path) -> None: self.steam_path = path From 0c6421fa6325747051a371209ad3b90954837427 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:30:07 +0900 Subject: [PATCH 02/10] chore: update freedesktop comment --- dzgui/config/freedesktop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui/config/freedesktop.py b/dzgui/config/freedesktop.py index 4cebe98..6e4504e 100644 --- a/dzgui/config/freedesktop.py +++ b/dzgui/config/freedesktop.py @@ -21,7 +21,7 @@ def write_desktop_file(exe_path: Path) -> Path: Terminal=false Exec={exe_path} Name=DZGUI - Comment=dzgui + Comment=DayZ server browser and mod manager Icon={icon} Categories=Game""" From 1dc20b838da4b66399d52ba084105961e36eb838 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:45:27 +0900 Subject: [PATCH 03/10] fix: support source version of DZGUI --- dzgui/views/dialogs/wizard.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index 9a5b2cd..b5d8908 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -451,6 +451,7 @@ class Assistant(Gtk.Assistant): else: self.set_default_size(1500, 900) + self.is_binary = False if os.getenv("PYAPP") is None else True self.config_path = XDG.config self.config_values: dict[str, Any] = config_boilerplate @@ -491,7 +492,7 @@ class Assistant(Gtk.Assistant): ): continue # NOTE: disabled for now on system-provided packages - if isinstance(page, ShortcutCreationPage) and os.getenv("PYAPP") is None: + if isinstance(page, ShortcutCreationPage) and not self.is_binary: continue self._add_page(page, page.get_page_type()) @@ -506,32 +507,33 @@ class Assistant(Gtk.Assistant): def _advance_page(self, index: int) -> int: page = self.get_nth_page(index) - # TODO: use enums/isinstance match page: - case self.page1: + case IntroductionPage(): pass - case self.page2: - if self.page2.is_migrated(): + case ConfigMigrationPage(): + if page.is_migrated(): steam_path = lookup(self.config_path, Preferences.DEFAULT) self.page7.set_steam_path(steam_path) - return self.get_n_pages() - 2 - case self.page3: + offset = 1 if not self.is_binary else 2 + self.setup_complete = True + return self.get_n_pages() - offset + case SteamPathPage(): self.config_values["default_steam_path"] = page.get_path_from_radio() - case self.page4: + case SteamValidationPage(): self.config_values["steam_api"] = page.get_api_key() - case self.page5: + case BMValidationPage(): self.config_values["bm_api"] = page.get_api_key() - # NOTE: collects config values before advancing to last page - case self.page6: + case PreferencesPage(): + # NOTE: collects config values before advancing to last page name, use_miles, client = self.page6.get_prefs() self.config_values["name"] = name self.config_values["use_miles"] = use_miles self.config_values["client"] = client self.write_config() self.page7.set_steam_path(self.config_values["default_steam_path"]) - case self.page7: - self.page7.create_shortcuts() self.setup_complete = True + case ShortcutCreationPage(): + page.create_shortcuts() case _: raise AttributeError("Trying to advance a non-canonical page") return index + 1 @@ -575,9 +577,7 @@ class Assistant(Gtk.Assistant): bar.set_fraction(fraction) bar.set_text(f"{page_num}/{total}") - # NOTE: disable forward action - # TODO: use page enums - if page != self.page1: + if not isinstance(page, IntroductionPage): EMITTER.emit("step_pending") From ba1b980a7ff40a82c5d5f1a7f41396ca049f6ef0 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:47:08 +0900 Subject: [PATCH 04/10] chore: rename test namespace --- pyproject.toml | 2 +- tests/test_remote_api.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c9f5ac5..37b6408 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,11 +107,11 @@ config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } } [tool.pytest.ini_options] markers = [ + "apitest: checks remote endpoints" "config: config file keys/values", "mods: tests mod metadata/link creation", "pefile: validate PE files", "post_install: requires a completed installation", "redact: log redaction mechanisms", "slow: long-running tests", - "webtest: checks remote endpoints" ] diff --git a/tests/test_remote_api.py b/tests/test_remote_api.py index e47a4d6..27cc066 100644 --- a/tests/test_remote_api.py +++ b/tests/test_remote_api.py @@ -4,7 +4,7 @@ from dzgui.config.query import get_config from dzgui.config.xdg import get_xdg_paths, parse_filepaths from dzgui.api import probe -pytestmark = pytest.mark.webtest +pytestmark = pytest.mark.apitest @pytest.fixture From 71a0481600a5e5ad73a5b47b08739b12d388ea84 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:47:28 +0900 Subject: [PATCH 05/10] fix: add comma --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 37b6408..956d4b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,7 +107,7 @@ config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } } [tool.pytest.ini_options] markers = [ - "apitest: checks remote endpoints" + "apitest: checks remote endpoints", "config: config file keys/values", "mods: tests mod metadata/link creation", "pefile: validate PE files", From daa4aa00598046056c1e2b685b1526a76f5a8abd Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:48:45 +0900 Subject: [PATCH 06/10] chore: clear typehinting errors resulting from mixins --- dzgui/views/dialogs/wizard.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index b5d8908..ca49375 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -49,8 +49,10 @@ class PageNum(Enum): SHORTCUTS = 7 FINAL = 8 + class OptionalPageMixin: """Marks optional pages as advanceable""" + def _on_map(self, page: "ScrolledWizardPage") -> None: EMITTER.emit("step_complete") @@ -221,7 +223,7 @@ class APIValidationPage(ScrolledWizardPage): self.spinner.stop() -class BMValidationPage(OptionalPageMixin, APIValidationPage): +class BMValidationPage(OptionalPageMixin, APIValidationPage): # type: ignore def __init__(self) -> None: super().__init__( enum=PageNum.BM_API, @@ -604,7 +606,7 @@ class CheckboxWithLabel(Gtk.Box): self.button.set_active(state) -class ShortcutCreationPage(OptionalPageMixin, ScrolledWizardPage): +class ShortcutCreationPage(OptionalPageMixin, ScrolledWizardPage): # type: ignore def __init__(self, shortcut: Path) -> None: super().__init__( enum=PageNum.SHORTCUTS, From 852830085159807545ac4d4ea1f37b46bf389957 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:01:39 +0900 Subject: [PATCH 07/10] chore: update crc tests --- tests/test_shortcuts.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_shortcuts.py b/tests/test_shortcuts.py index 470b5c8..a473d03 100644 --- a/tests/test_shortcuts.py +++ b/tests/test_shortcuts.py @@ -65,3 +65,27 @@ def test_save_shortcut(dummy_app) -> None: s.save_shortcuts() s._load_shortcuts(tmp) assert len(s.shortcuts["shortcuts"]) == 1 + + +def test_shortcut_crc(dummy_app) -> None: + s = Shortcuts(Path("")) + s.add_shortcut(*dummy_app.values()) + for key in s.shortcuts["shortcuts"].keys(): + entry = s.shortcuts["shortcuts"][key] + name = entry["AppName"] + exe = entry["Exe"] + uid = name + exe + bpid = s.gen_bpid(uid) + assert entry["appid"] & 0xFFFFFFFF == bpid + + +def test_reverse_crc(dummy_app) -> None: + s = Shortcuts(Path("")) + s.add_shortcut(*dummy_app.values()) + for key in s.shortcuts["shortcuts"].keys(): + entry = s.shortcuts["shortcuts"][key] + name = entry["AppName"] + exe = entry["Exe"] + uid = name + exe + bpid = s.gen_bpid(uid) + assert s.find_appname_by_unsigned_id(bpid) == name From dc2a635d2f9a49b666d56813391bc7e09abee98d Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:04:41 +0900 Subject: [PATCH 08/10] chore: add typehints to test --- tests/test_shortcuts.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_shortcuts.py b/tests/test_shortcuts.py index a473d03..eea75e4 100644 --- a/tests/test_shortcuts.py +++ b/tests/test_shortcuts.py @@ -38,14 +38,14 @@ def dummy_app() -> None: return d -def test_wrap_exe(dummy_app) -> None: +def test_wrap_exe(dummy_app: dict[str, str]) -> None: s = Shortcuts(Path("")) s.add_shortcut(*dummy_app.values()) assert s.shortcuts["shortcuts"]["0"]["Exe"][0] == '"' assert s.shortcuts["shortcuts"]["0"]["Exe"][-1] == '"' -def test_add_shortcut(dummy_app) -> None: +def test_add_shortcut(dummy_app: dict[str, str]) -> None: s = Shortcuts(Path("")) s.add_shortcut(*dummy_app.values()) new = s.shortcuts["shortcuts"] @@ -56,7 +56,7 @@ def test_add_shortcut(dummy_app) -> None: assert new[ind][k] == v -def test_save_shortcut(dummy_app) -> None: +def test_save_shortcut(dummy_app: dict[str, str]) -> None: s = Shortcuts(Path("")) s.add_shortcut(*dummy_app.values()) with tempfile.NamedTemporaryFile() as f: @@ -67,7 +67,7 @@ def test_save_shortcut(dummy_app) -> None: assert len(s.shortcuts["shortcuts"]) == 1 -def test_shortcut_crc(dummy_app) -> None: +def test_shortcut_crc(dummy_app: dict[str, str]) -> None: s = Shortcuts(Path("")) s.add_shortcut(*dummy_app.values()) for key in s.shortcuts["shortcuts"].keys(): @@ -79,7 +79,7 @@ def test_shortcut_crc(dummy_app) -> None: assert entry["appid"] & 0xFFFFFFFF == bpid -def test_reverse_crc(dummy_app) -> None: +def test_reverse_crc(dummy_app: dict[str, str]) -> None: s = Shortcuts(Path("")) s.add_shortcut(*dummy_app.values()) for key in s.shortcuts["shortcuts"].keys(): From 7edb66c3114402a0089caa82ce6f4143687f5da9 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:05:01 +0900 Subject: [PATCH 09/10] chore: distinguish between api and web tests --- pyproject.toml | 1 + tests/test_remote_api.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 956d4b7..8ded0ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,4 +114,5 @@ markers = [ "post_install: requires a completed installation", "redact: log redaction mechanisms", "slow: long-running tests", + "webtest: depends on remote endpoint", ] diff --git a/tests/test_remote_api.py b/tests/test_remote_api.py index 27cc066..e47a4d6 100644 --- a/tests/test_remote_api.py +++ b/tests/test_remote_api.py @@ -4,7 +4,7 @@ from dzgui.config.query import get_config from dzgui.config.xdg import get_xdg_paths, parse_filepaths from dzgui.api import probe -pytestmark = pytest.mark.apitest +pytestmark = pytest.mark.webtest @pytest.fixture From 525de56036319f91c44c0e680a44cc0294208248 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:13:00 +0900 Subject: [PATCH 10/10] feat: redact user id in logs --- dzgui/util/redact.py | 3 ++- tests/test_log_redaction.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/dzgui/util/redact.py b/dzgui/util/redact.py index 2cc57af..5d7254c 100644 --- a/dzgui/util/redact.py +++ b/dzgui/util/redact.py @@ -4,8 +4,9 @@ from typing import Literal api_filter = r"(.*&key=)([^&]*)(.*)" home_filter = r"(/home/)([^\s'\/]*)(.*)" +user_filter = r"(.*Steam/userdata/)([^/]*)(.*)" REDACTED = r"\1REDACTED\3" -REDACTION_PATTERNS = [api_filter, home_filter] +REDACTION_PATTERNS = [api_filter, home_filter, user_filter] def redact_home(text: str) -> str: diff --git a/tests/test_log_redaction.py b/tests/test_log_redaction.py index a96058c..438a195 100644 --- a/tests/test_log_redaction.py +++ b/tests/test_log_redaction.py @@ -37,6 +37,14 @@ class RecordsListHandler(logging.Handler): "Error in directory: '/home/SENSITIVE_USERNAME'", "Error in directory: '/home/REDACTED'", ), + ( + "User directory: /home/user/.local/share/Steam/userdata/999999/grid", + "User directory: /home/REDACTED/.local/share/Steam/userdata/REDACTED/grid", + ), + ( + "User directory: /drive/.local/share/Steam/userdata/999999", + "User directory: /drive/.local/share/Steam/userdata/REDACTED", + ), ], ) def test_log_redaction(log_error: str, expect: str) -> None: