From 3ddfb7353c8fa2b5a7dbf343718482ea9d653565 Mon Sep 17 00:00:00 2001 From: Aaddrick Date: Tue, 5 May 2026 06:45:42 -0400 Subject: [PATCH] launcher: log session/IME env block at startup (#570) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * launcher: log session/IME env block at startup (#548) Adds log_session_env, called once per launch from each packaging target (deb, rpm, AppImage, Nix). Emits a single env={ ... } block covering display (XDG_SESSION_TYPE, WAYLAND_DISPLAY, DISPLAY, XDG_CURRENT_DESKTOP), IME (GTK_IM_MODULE, XMODIFIERS, QT_IM_MODULE), and Claude-specific overrides (CLAUDE_USE_WAYLAND, CLAUDE_TITLEBAR_STYLE, CLAUDE_GTK_IM_MODULE). Empty/unset values are emitted as `KEY=` (rather than omitted) so absence is unambiguous in bug reports. Pure observability — no behavior change. Closes #548 Co-Authored-By: Claude * test: consolidate log_session_env BATS coverage (#548) Collapse the four log_session_env cases into two, and tighten the assertions in both: - Old test 1 (substring match per key) + old test 4 (block braces on their own lines) → one test using exact-line equality on the `lines` array. Locks block structure and per-key formatting in a single pass; substring matching could not catch a regression that re-ordered keys, dropped indentation, or merged lines. - Old test 2 (unset values are KEY=) + old test 3 (empty-string is KEY=) → one test covering both code paths. Exact-line match proves the value after `=` is truly empty; the previous `*'KEY='*` substring would have matched `KEY=value` and the old test-3 regex was fragile (depended on trailing newline being literal `$'\n'` vs end-of-string `$`). Net: 77 → 42 lines, 4 → 2 cases, stronger guarantees. No change to the helper itself or the call sites — issue #548 acceptance criteria still hold. --------- Co-authored-by: Claude --- nix/claude-desktop.nix | 1 + scripts/launcher-common.sh | 33 +++++++++++++++++++ scripts/packaging/appimage.sh | 1 + scripts/packaging/deb.sh | 1 + scripts/packaging/rpm.sh | 1 + tests/launcher-common.bats | 62 +++++++++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+) diff --git a/nix/claude-desktop.nix b/nix/claude-desktop.nix index 54ae4c1..dd7fd3a 100644 --- a/nix/claude-desktop.nix +++ b/nix/claude-desktop.nix @@ -245,6 +245,7 @@ cleanup_stale_cowork_socket log_message '--- Claude Desktop Launcher Start (NixOS) ---' log_message "Timestamp: $(date)" log_message "Arguments: $@" +log_session_env # Check for display if ! check_display; then diff --git a/scripts/launcher-common.sh b/scripts/launcher-common.sh index 05bb183..86a9567 100644 --- a/scripts/launcher-common.sh +++ b/scripts/launcher-common.sh @@ -16,6 +16,39 @@ log_message() { echo "$1" >> "$log_file" } +# Log the session/IME environment vars that drive display and input +# decisions, so bug reports include enough context to reason about +# them without round-trip env-dump requests (#548). +# +# Emits one block: +# env={ +# KEY=value +# ... +# } +# +# Empty or unset values are emitted as `KEY=` so absence is +# unambiguous (vs. silently omitted). Caller must run setup_logging +# first. +log_session_env() { + local key + log_message 'env={' + for key in \ + XDG_SESSION_TYPE \ + WAYLAND_DISPLAY \ + DISPLAY \ + XDG_CURRENT_DESKTOP \ + GTK_IM_MODULE \ + XMODIFIERS \ + QT_IM_MODULE \ + CLAUDE_USE_WAYLAND \ + CLAUDE_TITLEBAR_STYLE \ + CLAUDE_GTK_IM_MODULE + do + log_message " $key=${!key:-}" + done + log_message '}' +} + # Detect display backend (Wayland vs X11) # Sets: is_wayland, use_x11_on_wayland detect_display_backend() { diff --git a/scripts/packaging/appimage.sh b/scripts/packaging/appimage.sh index fbf4ba3..c6f6f47 100755 --- a/scripts/packaging/appimage.sh +++ b/scripts/packaging/appimage.sh @@ -98,6 +98,7 @@ log_message '--- Claude Desktop AppImage Start ---' log_message "Timestamp: $(date)" log_message "Arguments: $@" log_message "APPDIR: $appdir" +log_session_env # Path to the bundled Electron executable and app electron_exec="$appdir/usr/lib/node_modules/electron/dist/electron" diff --git a/scripts/packaging/deb.sh b/scripts/packaging/deb.sh index f5d510e..c3513dc 100755 --- a/scripts/packaging/deb.sh +++ b/scripts/packaging/deb.sh @@ -114,6 +114,7 @@ cleanup_stale_cowork_socket log_message '--- Claude Desktop Launcher Start ---' log_message "Timestamp: \$(date)" log_message "Arguments: \$@" +log_session_env # Check for display if ! check_display; then diff --git a/scripts/packaging/rpm.sh b/scripts/packaging/rpm.sh index 3ce7bb7..b4685b6 100755 --- a/scripts/packaging/rpm.sh +++ b/scripts/packaging/rpm.sh @@ -97,6 +97,7 @@ cleanup_stale_cowork_socket log_message '--- Claude Desktop Launcher Start ---' log_message "Timestamp: \$(date)" log_message "Arguments: \$@" +log_session_env # Check for display if ! check_display; then diff --git a/tests/launcher-common.bats b/tests/launcher-common.bats index 4c7a03f..d692a8c 100644 --- a/tests/launcher-common.bats +++ b/tests/launcher-common.bats @@ -35,10 +35,15 @@ setup() { unset CLAUDE_USE_WAYLAND unset NIRI_SOCKET unset XDG_CURRENT_DESKTOP + unset XDG_SESSION_TYPE unset CLAUDE_MENU_BAR unset CLAUDE_TITLEBAR_STYLE unset COWORK_VM_BACKEND unset ELECTRON_USE_SYSTEM_TITLE_BAR + unset GTK_IM_MODULE + unset XMODIFIERS + unset QT_IM_MODULE + unset CLAUDE_GTK_IM_MODULE # shellcheck source=scripts/launcher-common.sh source "$SCRIPT_DIR/../scripts/launcher-common.sh" @@ -86,6 +91,63 @@ teardown() { [[ "${lines[1]}" == "test message two" ]] } +# ============================================================================= +# log_session_env +# ============================================================================= + +@test "log_session_env: emits env={ ... } block with all required keys" { + setup_logging + XDG_SESSION_TYPE='wayland' + WAYLAND_DISPLAY='wayland-0' + DISPLAY=':0' + XDG_CURRENT_DESKTOP='KDE' + GTK_IM_MODULE='ibus' + XMODIFIERS='@im=ibus' + QT_IM_MODULE='ibus' + CLAUDE_USE_WAYLAND='1' + CLAUDE_TITLEBAR_STYLE='hybrid' + CLAUDE_GTK_IM_MODULE='xim' + log_session_env + + run cat "$log_file" + # Exact-line match locks block structure (open/close braces on + # their own lines) and per-key formatting in one pass. + [[ "${lines[0]}" == 'env={' ]] + [[ "${lines[1]}" == ' XDG_SESSION_TYPE=wayland' ]] + [[ "${lines[2]}" == ' WAYLAND_DISPLAY=wayland-0' ]] + [[ "${lines[3]}" == ' DISPLAY=:0' ]] + [[ "${lines[4]}" == ' XDG_CURRENT_DESKTOP=KDE' ]] + [[ "${lines[5]}" == ' GTK_IM_MODULE=ibus' ]] + [[ "${lines[6]}" == ' XMODIFIERS=@im=ibus' ]] + [[ "${lines[7]}" == ' QT_IM_MODULE=ibus' ]] + [[ "${lines[8]}" == ' CLAUDE_USE_WAYLAND=1' ]] + [[ "${lines[9]}" == ' CLAUDE_TITLEBAR_STYLE=hybrid' ]] + [[ "${lines[10]}" == ' CLAUDE_GTK_IM_MODULE=xim' ]] + [[ "${lines[11]}" == '}' ]] +} + +@test "log_session_env: unset/empty values render as 'KEY=' (no value)" { + setup_logging + # All vars unset by setup() except this one, which exercises the + # empty-string branch (must be indistinguishable from unset). + GTK_IM_MODULE='' + log_session_env + + run cat "$log_file" + # Exact-line match proves the line ends right after '=' — a + # substring like *'KEY='* would also match 'KEY=value'. + [[ "${lines[1]}" == ' XDG_SESSION_TYPE=' ]] + [[ "${lines[2]}" == ' WAYLAND_DISPLAY=' ]] + [[ "${lines[3]}" == ' DISPLAY=' ]] + [[ "${lines[4]}" == ' XDG_CURRENT_DESKTOP=' ]] + [[ "${lines[5]}" == ' GTK_IM_MODULE=' ]] + [[ "${lines[6]}" == ' XMODIFIERS=' ]] + [[ "${lines[7]}" == ' QT_IM_MODULE=' ]] + [[ "${lines[8]}" == ' CLAUDE_USE_WAYLAND=' ]] + [[ "${lines[9]}" == ' CLAUDE_TITLEBAR_STYLE=' ]] + [[ "${lines[10]}" == ' CLAUDE_GTK_IM_MODULE=' ]] +} + # ============================================================================= # check_display # =============================================================================