fix(v3/appimage): GTK4 default + relative builddir + output filename (#5475)#5476
Conversation
Since alpha.93, the default Linux build target is GTK4 / WebKitGTK 6.0
(GTK3 / WebKit2GTK 4.1 is now the legacy `-tags gtk3` path). The AppImage
bundler still hard-coded the GTK3 set of runtime files, so on a runner
with only the GTK4 toolchain installed `wails3 generate appimage` aborted
with:
ERROR Unable to locate all required files: libwebkit2gtkinjectedbundle.so
Move the ldd-based version detection in front of `findGTKFiles`, run it
on the source binary (not the post-copy path, which `filepath.Join` mis-
handles when `-binary` includes a path), and pick the injected-bundle
filename to match: `libwebkitgtkinjectedbundle.so` for GTK4, the older
`libwebkit2gtkinjectedbundle.so` for GTK3. `WebKitWebProcess` and
`WebKitNetworkProcess` are unchanged - both stacks ship them.
Also extend `hasRelrDynSections` to probe `libgtk-4.so.1` (plus aarch64
paths) so the strip workaround introduced in #4772 still triggers on
modern toolchains regardless of which stack the user built against.
Verified on Ubuntu 26.04 lin-node1:
- Default (GTK4): `go build` then `wails3 generate appimage` bundles
`libwebkitgtkinjectedbundle.so` under `webkitgtk-6.0/injected-bundle/`
in the AppDir; the resulting AppImage launches under xvfb and logs
`GTK=4.22.2 WebKitGTK=2.52.3`.
- Legacy (`-tags gtk3`): same flow bundles
`libwebkit2gtkinjectedbundle.so` under `webkit2gtk-4.1/injected-bundle/`
and the AppImage launches with `Webkit2Gtk=v2.52.3` and `-tags=gtk3`.
Two pre-existing unrelated issues observed during verification, flagged
here so reviewers don't conflate them with this fix:
- `wails3 generate appimage` with a relative `-builddir` races on the
AppRun download goroutine because `s.CD(BuildDir)` is called between
the goroutine launch and its file write. Workaround: pass absolute
paths.
- The post-bundle `s.MOVE` expects the AppImage to be named after
`normaliseName(filepath.Base(Binary))`, but `linuxdeploy` names it
after the desktop `Name=` field, so the move fails when those don't
match. Workaround: align `Name=` with the binary basename. Will be
addressed in a follow-up.
Refs #5475.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
WalkthroughNormalize CLI paths, determine GTK version from the built binary early to pick the correct WebKit injected-bundle and runtime files, force linuxdeploy to write a deterministic AppImage name, and expand ChangesAppImage generation fixes
Sequence Diagram(s)sequenceDiagram
participant CLI
participant generateAppImage
participant RuntimeSearch
participant linuxdeploy
CLI->>generateAppImage: provide options (Binary, BuildDir, OutputDir, Icon, DesktopFile)
generateAppImage->>generateAppImage: resolve absolute paths
generateAppImage->>RuntimeSearch: run ldd on Binary -> determine DeployGtkVersion
generateAppImage->>RuntimeSearch: select filesNeeded (WebKit/GTK + injected bundle)
generateAppImage->>linuxdeploy: export DEPLOY_GTK_VERSION, set OUTPUT, invoke linuxdeploy
linuxdeploy-->>generateAppImage: writes targetFile
generateAppImage->>CLI: move targetFile -> options.OutputDir, log final AppImage path
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR fixes v3 AppImage generation so it detects whether the target Linux binary uses GTK3 or GTK4 before selecting WebKit runtime files, aligning packaging with the current GTK4 default and legacy -tags gtk3 path.
Changes:
- Moves GTK stack detection earlier and uses
lddoutput to choose the appropriate injected-bundle library. - Keeps
DEPLOY_GTK_VERSIONsetup for linuxdeploy based on detected GTK version. - Expands
.relr.dynprobing to include GTK4 and aarch64 GTK library paths.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
v3/internal/commands/appimage.go |
Detects GTK stack before file discovery, selects GTK3/GTK4 injected bundle, and expands strip-compatibility probing. |
v3/UNRELEASED_CHANGELOG.md |
Documents the AppImage GTK stack detection fix and .relr.dyn probe update. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
v3/internal/commands/appimage.go (1)
159-159: ⚡ Quick winImprove error diagnostics for GTK version detection failure.
When GTK version cannot be determined, include a snippet of the ldd output to help users diagnose whether no GTK library was found or an unexpected version string appeared.
📊 Proposed enhancement for error context
default: - return fmt.Errorf("unable to determine GTK version") + return fmt.Errorf("unable to determine GTK version from ldd output (checked libgtk-4.so, libgtk-3.so, libgtk-x11-2.0.so). ldd output snippet: %.200s...", lddString)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@v3/internal/commands/appimage.go` at line 159, Replace the bare error return at the GTK detection failure (the existing return fmt.Errorf("unable to determine GTK version")) with an error that includes the ldd output used to probe GTK (e.g., the variable holding the command output such as out or lddOutput) so callers see diagnostic info; format the message like "unable to determine GTK version: ldd output: %q" (or include a trimmed prefix if output can be very large) and ensure you propagate the formatted error from the same code path where the ldd invocation and parsing occur.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@v3/internal/commands/appimage.go`:
- Around line 140-160: The code builds a shell command with fmt.Sprintf("ldd
%s", options.Binary) and passes it to s.EXEC, which allows command injection or
failure on paths with spaces; update the call site that produces lddOutput so it
does not interpolate options.Binary into a shell string—either call s.EXEC with
the command and args separately (e.g., pass "ldd" and options.Binary if s.EXEC
supports arg lists) or invoke a safe exec path (exec.Command/Run) to run ldd
directly without shell interpretation; ensure the change is applied where
lddOutput/err are produced and preserve the subsequent lddString parsing and
DeployGtkVersion switch and error handling.
---
Nitpick comments:
In `@v3/internal/commands/appimage.go`:
- Line 159: Replace the bare error return at the GTK detection failure (the
existing return fmt.Errorf("unable to determine GTK version")) with an error
that includes the ldd output used to probe GTK (e.g., the variable holding the
command output such as out or lddOutput) so callers see diagnostic info; format
the message like "unable to determine GTK version: ldd output: %q" (or include a
trimmed prefix if output can be very large) and ensure you propagate the
formatted error from the same code path where the ldd invocation and parsing
occur.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 77dc169e-37d4-4f4a-a165-6dab985571a5
📒 Files selected for processing (2)
v3/UNRELEASED_CHANGELOG.mdv3/internal/commands/appimage.go
Two pre-existing bugs surfaced while verifying #5475: 1. `wails3 generate appimage` with a relative `-builddir` would fail inside the AppRun download goroutine. The flow does `s.CD(BuildDir)` between launching the goroutines and their file writes, so any path computed before the CD (e.g. `appDir = filepath.Join(BuildDir, "...AppDir")`) was being resolved relative to the wrong CWD by the downstream `s.DOWNLOAD(target)` and the `ldd <binary>` shell-out. Fix: resolve `-binary`, `-icon`, `-desktopfile`, `-builddir` and `-outputdir` to absolute paths up-front, alongside the existing `OutputDir` resolution. Now CWD-sensitive operations downstream all see stable absolute paths. 2. The post-bundle `s.MOVE(targetFile, OutputDir)` would fail when the desktop file's `Name=` field didn't match `normaliseName(basename( Binary))`. linuxdeploy's appimage plugin names the output AppImage from the desktop `Name=`, but the Go code expected `<binary>-<arch>.AppImage` and looked it up by that path. Fix: set the `OUTPUT` env var (honored by linuxdeploy-plugin-appimage) to the binary-derived name we already expect, so the produced filename matches what we then move. Verified on Ubuntu 26.04 lin-node1: - Relative `-builddir` + a desktop `Name=Differently Named App` (which used to fail twice over): exit 0, AppImage produced at `out/appimage-smoke-gtk4-x86_64.AppImage`, launches under xvfb, logs `GTK=4.22.2 WebKitGTK=2.52.3`. - The original absolute-paths flow (both GTK4 default and `-tags gtk3`) still produces the matching AppImage in `-outputdir` and exits 0 - no regression of the #5475 fix. Refs #5475.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
v3/internal/commands/appimage.go (1)
146-167:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse argument-safe execution for
lddinvocation.At Line 150,
options.Binaryis interpolated into a command string. This is shell-fragile (spaces/special chars) and potentially unsafe depending ons.EXECinternals.Proposed fix
- lddOutput, err := s.EXEC(fmt.Sprintf("ldd %s", options.Binary)) + lddOutput, err := s.EXEC(fmt.Sprintf("ldd %q", options.Binary))#!/bin/bash # Verify how s.EXEC executes commands and whether it shells out. # Expected: confirm whether command strings are passed through a shell. fd -i 's.go' v3/internal | xargs -r rg -n -C3 'func\s+EXEC\s*\(|exec\.Command|sh -c|bash -c' rg -n -C2 'EXEC\(fmt\.Sprintf\("ldd %s"|EXEC\(fmt\.Sprintf\("readelf -S %s"' v3/internal/commands/appimage.go🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@v3/internal/commands/appimage.go` around lines 146 - 167, The ldd invocation builds a shell-interpolated string with options.Binary and should use argument-safe execution: change the call site that currently uses s.EXEC(fmt.Sprintf("ldd %s", options.Binary)) in v3/internal/commands/appimage.go to a form that passes the command and arguments separately (e.g. s.EXEC("ldd", options.Binary) or an equivalent API provided by s.EXEC) or otherwise safely escape/quote options.Binary before invoking the shell; update the lddOutput/error handling around s.EXEC, keep the subsequent lddString/DeployGtkVersion logic unchanged, and if s.EXEC does not support arg lists implement exec.Command/CommandContext to run ldd with the binary as a separate argument.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@v3/internal/commands/appimage.go`:
- Around line 146-167: The ldd invocation builds a shell-interpolated string
with options.Binary and should use argument-safe execution: change the call site
that currently uses s.EXEC(fmt.Sprintf("ldd %s", options.Binary)) in
v3/internal/commands/appimage.go to a form that passes the command and arguments
separately (e.g. s.EXEC("ldd", options.Binary) or an equivalent API provided by
s.EXEC) or otherwise safely escape/quote options.Binary before invoking the
shell; update the lddOutput/error handling around s.EXEC, keep the subsequent
lddString/DeployGtkVersion logic unchanged, and if s.EXEC does not support arg
lists implement exec.Command/CommandContext to run ldd with the binary as a
separate argument.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5e65e0b5-16c8-4380-b848-e7517c499293
📒 Files selected for processing (2)
v3/UNRELEASED_CHANGELOG.mdv3/internal/commands/appimage.go
✅ Files skipped from review due to trivial changes (1)
- v3/UNRELEASED_CHANGELOG.md
…ailure Addresses CodeRabbit and Copilot review on #5476: - Quote the binary path passed to `ldd` (`%s` -> `%q`). `s.EXEC` splits the command string with shlex; an unquoted path with spaces gets fragmented and the ldd call fails. - Same treatment for the `readelf` probe in `hasRelrDynSections` and for the `linuxdeploy ... --appdir <dir>` command. The linuxdeploy call site was the latent breakage — invoking it with a `-builddir` containing a space produced `fork/exec /tmp/space: no such file or directory` even after the abs-path resolution from the previous commit. - Include a truncated ldd-output snippet in the "unable to determine GTK version" error so users hitting this branch can see which libraries the binary actually links against. Verified on lin-node1 (Ubuntu 26.04): - Paths with spaces (`-binary "/tmp/space test/my app"` ...): now exit 0 and produce `out/my-app-x86_64.AppImage`. Previously failed at the linuxdeploy step. - The original absolute-path GTK4 and `-tags gtk3` flows still pass and the GTK4 AppImage still launches under xvfb with `GTK=4.22.2 WebKitGTK=2.52.3` - no regression. Refs #5475.
Summary
Closes #5475. Bundled with two adjacent fixes for pre-existing AppImage bugs that surfaced while verifying the GTK4 default fix.
1. GTK4 default support (the #5475 fix)
Since alpha.93, the default Linux build stack is GTK4 / WebKitGTK 6.0 (GTK3 / WebKit2GTK 4.1 is now the legacy
-tags gtk3path). Butwails3 generate appimagestill hard-coded the GTK3 set of runtime files, so on a runner with only the GTK4 toolchain installed:ldd-based GTK detection in front offindGTKFiles, so we know which stack the binary links against before searching for runtime files.lddon the sourceoptions.Binarydirectly. The previousfilepath.Join(appDir, "usr", "bin", options.Binary)mishandled-binarywith a path prefix and pointed at a file that may not exist; the source binary is unambiguous and already required to exist.libwebkitgtkinjectedbundle.so(underwebkitgtk-6.0/injected-bundle/)libwebkit2gtkinjectedbundle.so(underwebkit2gtk-4.1/injected-bundle/)WebKitWebProcessandWebKitNetworkProcessare unchanged - both stacks ship them.hasRelrDynSectionsto probelibgtk-4.so.1(and aarch64 paths) so the strip workaround from fix(linux): auto-detect and handle .relr.dyn sections for AppImage builds #4772 still triggers on modern toolchains regardless of which stack the user built against.2. Relative
-builddirracewails3 generate appimagewith a relative-builddirfailed inside the AppRun download goroutine: the flow doess.CD(BuildDir)between launching the goroutines and their file writes, so any path computed before the CD (e.g.appDir = filepath.Join(BuildDir, "...AppDir")) was being resolved relative to the wrong CWD by the downstreams.DOWNLOAD(target)and theldd <binary>shell-out.Fix: resolve
-binary,-icon,-desktopfile,-builddirand-outputdirto absolute paths up-front, alongside the existingOutputDirresolution.3. AppImage output filename mismatch
The post-bundle
s.MOVE(targetFile, OutputDir)failed when the desktopName=field didn't matchnormaliseName(basename(Binary)).linuxdeploy's appimage plugin names the output AppImage from the desktopName=, but the Go code expected<binary>-<arch>.AppImageand looked it up by that path.Fix: set
OUTPUT=<binary>-<arch>.AppImage(honored bylinuxdeploy-plugin-appimage) so the produced filename matches what the move step expects.Test plan
Verified end-to-end on Ubuntu 26.04 (lin-node1) with both GTK3 and GTK4 dev/runtime packages installed:
go builda minimal Wails v3 app;wails3 generate appimagesucceeds, bundlesusr/lib/x86_64-linux-gnu/webkitgtk-6.0/injected-bundle/libwebkitgtkinjectedbundle.sointo the AppDir, output AppImage launches underxvfb-runand logsGTK=4.22.2 WebKitGTK=2.52.3.-tags gtk3bundlesusr/lib/x86_64-linux-gnu/webkit2gtk-4.1/injected-bundle/libwebkit2gtkinjectedbundle.so; AppImage launches under xvfb and logs-tags=gtk3 ... Webkit2Gtk=v2.52.3.-builddir+ Name= mismatch (bugs 2 & 3): desktop file withName=Differently Named App, all paths passed as relative. Exit 0, AppImage produced at the expectedout/<binary>-<arch>.AppImage(notDifferently_Named_App-x86_64.AppImage), launches under xvfb and logsGTK=4.22.2 WebKitGTK=2.52.3.go test ./internal/commands/...passes.go vet ./...clean (only pre-existing warnings ininternal/generator/testcases, unrelated).Summary by CodeRabbit