fix(bundler): inline linuxdeploy plugin scripts#15
Conversation
WalkthroughThe changes implement inline bundling of linuxdeploy plugins (GStreamer and GTK) as embedded bash scripts within tauri-bundler to replace external downloads, avoiding HTTP rate-limiting errors. The bundler's Rust code is updated to use Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In
`@crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gstreamer.sh`:
- Around line 141-160: The 0.10 branch in the GSTREAMER_VERSION conditional
writes apprun hook exports that incorrectly reference gstreamer-1.0 paths;
update the linuxdeploy-plugin-gstreamer.sh content generated in the elif [
"$GSTREAMER_VERSION" == "0.10" ] block so the exported variables
GST_PLUGIN_SYSTEM_PATH_0_10, GST_PLUGIN_SCANNER_0_10 and GST_PTP_HELPER_0_10
point to the 0.10 layout (use gstreamer-0.10 and the corresponding
gstreamer0.10/gstreamer-0.10 scanner/helper locations) instead of gstreamer-1.0
paths.
In `@crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh`:
- Around line 184-185: The APPDIR fallback currently uses
dirname(realpath("$0")) which resolves to the apprun-hooks directory; change the
fallback for APPDIR so it ascends one additional directory (use dirname around
the existing dirname(realpath("$0")) expression) so APPDIR points to the AppDir
root, then leave the export GTK_DATA_PREFIX="$APPDIR" as-is; update the export
that sets APPDIR (the line exporting APPDIR and referencing realpath and
dirname) to use the double-dirname approach to correct path resolution.
- Around line 227-231: The sed -i calls are not guarded and will fail under set
-e if the cache files are missing; update the blocks that reference APPDIR and
the cache variables (gtk3_immodules_cache_file and gdk_pixbuf_cache_file) to
only run sed -i when the target file exists (i.e., wrap the sed invocation in an
if [ -f "$APPDIR/$gtk3_immodules_cache_file" ] ... fi and similarly for
gdk_pixbuf_cache_file), keeping the existing warning messages intact so the
script won't abort when caches are absent.
In `@crates/tauri-bundler/src/bundle/linux/appimage/mod.rs`:
- Around line 242-246: The guard for the GTK plugin is inverted: change the
condition on the gtk Path (variable gtk) to check non-existence (if
!gtk.exists()) so the embedded bytes from
include_bytes!("./linuxdeploy-plugin-gtk.sh") are written for fresh tool
directories; keep using write_and_make_executable(>k, data)? and the same
include_bytes! reference so behavior matches the other tool blocks (apprun,
linuxdeploy, gstreamer, appimage).
- Around line 284-309: prepare_tools propagates FileWriteError from
write_and_make_executable into crate::Result<PathBuf> but there's no conversion
to the crate Error type; either implement impl From<FileWriteError> for Error
that converts the FileWriteError into Error::BundlerError (wrapping the display
of the FileWriteError via anyhow) or change each call site in prepare_tools to
map_err into the crate Error explicitly (e.g., .map_err(|e|
Error::BundlerError(anyhow::anyhow!("{}", e))) before using ?). Ensure you
update FileWriteError, write_and_make_executable, or the prepare_tools call
sites accordingly so the ? operator can convert FileWriteError into the crate
Error.
- Around line 309-313: In write_and_make_executable, fs::set_permissions is
called before fs::write which fails for non-existent files; change the order so
write_and_make_executable writes the file first using fs::write (preserving the
existing FileWriteError::Write mapping) and then calls fs::set_permissions to
set 0o770 (preserving FileWriteError::Permissions mapping); keep the same error
mapping semantics and the use of PermissionsExt when adjusting the order.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.changes/bundler-inline-linuxdeploy-plugins.mdcrates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gstreamer.shcrates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.shcrates/tauri-bundler/src/bundle/linux/appimage/mod.rs
🧰 Additional context used
🧬 Code graph analysis (2)
crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gstreamer.sh (1)
crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh (1)
show_usage(16-26)
crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh (3)
crates/tauri/src/state.rs (1)
set(118-133)crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gstreamer.sh (1)
show_usage(12-25)crates/tauri-cli/src/helpers/flock.rs (2)
read(53-55)file(32-34)
🔇 Additional comments (3)
crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gstreamer.sh (1)
27-68: Argument parsing and prerequisite checks are clear.The flow is easy to follow and the error messaging is direct.
crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh (1)
126-143: Tool discovery and prerequisite checks look solid.The pkg-config/pkgconf fallback and LINUXDEPLOY guard are straightforward and user-friendly.
.changes/bundler-inline-linuxdeploy-plugins.md (1)
1-5: Changelog entry is clear and aligned with the change.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| if [ "$GSTREAMER_VERSION" == "1.0" ]; then | ||
| cat > "$APPDIR"/apprun-hooks/linuxdeploy-plugin-gstreamer.sh <<\EOF | ||
| #! /bin/bash | ||
|
|
||
| export GST_REGISTRY_REUSE_PLUGIN_SCANNER="no" | ||
| export GST_PLUGIN_SYSTEM_PATH_1_0="${APPDIR}/usr/lib/gstreamer-1.0" | ||
| export GST_PLUGIN_PATH_1_0="${APPDIR}/usr/lib/gstreamer-1.0" | ||
|
|
||
| export GST_PLUGIN_SCANNER_1_0="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" | ||
| export GST_PTP_HELPER_1_0="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" | ||
| EOF | ||
| elif [ "$GSTREAMER_VERSION" == "0.10" ]; then | ||
| cat > "$APPDIR"/apprun-hooks/linuxdeploy-plugin-gstreamer.sh <<\EOF | ||
| #! /bin/bash | ||
|
|
||
| export GST_REGISTRY_REUSE_PLUGIN_SCANNER="no" | ||
| export GST_PLUGIN_SYSTEM_PATH_0_10="${APPDIR}/usr/lib/gstreamer-1.0" | ||
|
|
||
| export GST_PLUGIN_SCANNER_0_10="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" | ||
| export GST_PTP_HELPER_0_10="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find crates/tauri-bundler -name "linuxdeploy-plugin-gstreamer.sh" -type fRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 224
🌐 Web query:
GStreamer 0.10 vs 1.0 directory structure gstreamer-0.10 paths Linux plugin scanner
💡 Result:
Short summary — main differences between GStreamer 0.10 and 1.0 directory/plugin paths (Linux)
-
System plugin directory
- 0.10: /usr/lib/gstreamer-0.10 (or /usr/local/lib/gstreamer-0.10). [1]
- 1.0: /usr/lib/gstreamer-1.0 (or /usr/local/lib/gstreamer-1.0). [2]
-
Per-user plugin directory / XDG data
- 0.10: ~/.gstreamer-0.10/plugins (older XDG) or XDG_DATA_HOME/.gstreamer-0.10/plugins. [1]
- 1.0: $XDG_DATA_HOME/gstreamer-1.0/plugins (usually ~/.local/share/gstreamer-1.0/plugins). [2]
-
Environment variables (versioned variants)
- 0.10: GST_PLUGIN_PATH and GST_PLUGIN_SYSTEM_PATH (and used for 0.10 if no _1_0 variant). [1][3]
- 1.0: GST_PLUGIN_PATH_1_0 and GST_PLUGIN_SYSTEM_PATH_1_0 (these override the unversioned ones for 1.0). [2][3]
-
Plugin scanner / registry
- Both versions use a plugin scanner/registry mechanism that caches plugin metadata (GstRegistry) and scan order follows: explicit --gst-plugin-path, GST_PLUGIN_PATH(_1_0), GST_PLUGIN_SYSTEM_PATH(_1_0). The registry/cache location/format is internal but differs by API_VERSION (so separate registries for 0.10 vs 1.0). [4][2]
-
Practical notes
- You can run both runtimes side-by-side because of versioned directories and environment variables; use the _1_0 variants to point only 1.0 to specific paths. [2][3]
- On some systems packages install into /usr/lib vs /usr/lib64 or /usr/local/lib — check your distro’s package layout. [2]
Sources
- GStreamer 0.10 running/debugging docs (system & user plugin paths). [1]
- GStreamer 1.0 running/debugging docs (system & user plugin paths, _1_0 env vars). [2]
- Explanation of GST_PLUGIN_PATH / GST_PLUGIN_SYSTEM_PATH and _1_0 variants. [3]
- GstRegistry / plugin scanning order and cache behavior. [4]
References:
[1] Maemo / gstreamer-0.10 running docs.
[2] GStreamer official “Running GStreamer Applications” / gstreamer-1.0.
[3] GStreamer environment/FAQ pages describing GST_PLUGIN_PATH/_1_0 behavior.
[4] GstRegistry documentation (plugin scanning and cache).
🌐 Web query:
GStreamer gst-plugin-scanner path location gstreamer-1.0 directory structure /usr/lib/gstreamer1.0
💡 Result:
Short answer: gst-plugin-scanner’s binary location depends on distro/prefix. Common locations:
- Debian/Ubuntu (multiarch): /usr/lib//gstreamer1.0/gstreamer-1.0/gst-plugin-scanner (e.g. /usr/lib/x86_64-linux-gnu/... ). [1]
- Fedora/RHEL: /usr/libexec/gstreamer-1.0/gst-plugin-scanner. [2]
- Upstream docs: GStreamer also searches $prefix/libs/gstreamer-/ (system plugin dirs) and per-user XDG paths. [3]
To find it on your system:
- which gst-plugin-scanner
- or: sudo find /usr -type f -name gst-plugin-scanner 2>/dev/null
Sources: Ubuntu/Launchpad crash reports showing Debian path. [1] Fedora koji file listing showing /usr/libexec path. [2] GStreamer docs on plugin search paths. [3]
0.10 AppRun hook points to 1.0 paths.
When GSTREAMER_VERSION=0.10, the hook exports _0_10 variables pointing at gstreamer-1.0 directories, which won't exist for 0.10 builds. GStreamer 0.10 uses separate directory paths from 1.0; use gstreamer-0.10 and gstreamer0.10/gstreamer-0.10 to match the versioned plugin layout.
🛠️ Proposed fix
- export GST_PLUGIN_SYSTEM_PATH_0_10="${APPDIR}/usr/lib/gstreamer-1.0"
+ export GST_PLUGIN_SYSTEM_PATH_0_10="${APPDIR}/usr/lib/gstreamer-0.10"
- export GST_PLUGIN_SCANNER_0_10="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner"
- export GST_PTP_HELPER_0_10="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper"
+ export GST_PLUGIN_SCANNER_0_10="${APPDIR}/usr/lib/gstreamer0.10/gstreamer-0.10/gst-plugin-scanner"
+ export GST_PTP_HELPER_0_10="${APPDIR}/usr/lib/gstreamer0.10/gstreamer-0.10/gst-ptp-helper"🤖 Prompt for AI Agents
In
`@crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gstreamer.sh`
around lines 141 - 160, The 0.10 branch in the GSTREAMER_VERSION conditional
writes apprun hook exports that incorrectly reference gstreamer-1.0 paths;
update the linuxdeploy-plugin-gstreamer.sh content generated in the elif [
"$GSTREAMER_VERSION" == "0.10" ] block so the exported variables
GST_PLUGIN_SYSTEM_PATH_0_10, GST_PLUGIN_SCANNER_0_10 and GST_PTP_HELPER_0_10
point to the 0.10 layout (use gstreamer-0.10 and the corresponding
gstreamer0.10/gstreamer-0.10 scanner/helper locations) instead of gstreamer-1.0
paths.
| export APPDIR="${APPDIR:-"$(dirname "$(realpath "$0")")"}" # Workaround to run extracted AppImage | ||
| export GTK_DATA_PREFIX="$APPDIR" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate the file
find . -name "linuxdeploy-plugin-gtk.sh" -type f 2>/dev/nullRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 224
🏁 Script executed:
# Check the directory structure around this file
ls -la crates/tauri-bundler/src/bundle/linux/appimage/ 2>/dev/null || echo "Directory not found"Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 412
🏁 Script executed:
# Read the problematic lines in context
cat -n crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh | sed -n '180,190p'Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 887
🏁 Script executed:
# Search for references to this script to understand how it's invoked
rg "linuxdeploy-plugin-gtk" --type sh --type rust -B 2 -A 2Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 2522
🏁 Script executed:
# Check if there's an apprun-hooks directory
find . -type d -name "*apprun*" -o -type d -name "*hooks*" 2>/dev/null | head -20Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 96
APPDIR fallback resolves to the hooks directory, not the AppDir root.
This script is installed at $APPDIR/apprun-hooks/linuxdeploy-plugin-gtk.sh and executed from there. When APPDIR is unset, dirname "$(realpath "$0")" points to .../apprun-hooks instead of the AppImage root, breaking GTK data path resolution. An additional dirname is needed to navigate up one more level.
🛠️ Proposed fix
-export APPDIR="${APPDIR:-"$(dirname "$(realpath "$0")")"}" # Workaround to run extracted AppImage
+export APPDIR="${APPDIR:-"$(dirname "$(dirname "$(realpath "$0")")")"}" # Workaround to run extracted AppImage📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export APPDIR="${APPDIR:-"$(dirname "$(realpath "$0")")"}" # Workaround to run extracted AppImage | |
| export GTK_DATA_PREFIX="$APPDIR" | |
| export APPDIR="${APPDIR:-"$(dirname "$(dirname "$(realpath "$0")")")"}" # Workaround to run extracted AppImage | |
| export GTK_DATA_PREFIX="$APPDIR" |
🤖 Prompt for AI Agents
In `@crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh`
around lines 184 - 185, The APPDIR fallback currently uses
dirname(realpath("$0")) which resolves to the apprun-hooks directory; change the
fallback for APPDIR so it ascends one additional directory (use dirname around
the existing dirname(realpath("$0")) expression) so APPDIR points to the AppDir
root, then leave the export GTK_DATA_PREFIX="$APPDIR" as-is; update the export
that sets APPDIR (the line exporting APPDIR and referencing realpath and
dirname) to use the double-dirname approach to correct path resolution.
| if [ ! -f "$APPDIR/$gtk3_immodules_cache_file" ]; then | ||
| echo "WARNING: immodules.cache file is missing" | ||
| fi | ||
| sed -i "s|$gtk3_libdir/3.0.0/immodules/||g" "$APPDIR/$gtk3_immodules_cache_file" | ||
| ;; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the file to check its size
wc -l crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.shRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 224
🏁 Script executed:
# Check if set -e is used at the beginning of the script
head -20 crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.shRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 224
🏁 Script executed:
# Examine the code around lines 227-231
sed -n '220,235p' crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.shRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 810
🏁 Script executed:
# Examine the code around lines 266-270
sed -n '260,275p' crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.shRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 753
🏁 Script executed:
head -50 crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh | grep -E "^#!/|set -e"Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 90
🏁 Script executed:
# Search for set -e anywhere in the file
grep "set -e" crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.shRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 90
Guard sed -i operations when cache files may be missing.
The script uses set -e, so sed will abort the script when cache files don't exist—even though you already warn about missing files. Guard the edits to keep the script resilient.
This applies to the GTK immodules cache (lines 227-231) and the GdkPixbuf cache (lines 266-270).
🛠️ Proposed fix
- sed -i "s|$gtk3_libdir/3.0.0/immodules/||g" "$APPDIR/$gtk3_immodules_cache_file"
+ if [ -f "$APPDIR/$gtk3_immodules_cache_file" ]; then
+ sed -i "s|$gtk3_libdir/3.0.0/immodules/||g" "$APPDIR/$gtk3_immodules_cache_file"
+ fi-sed -i "s|$gdk_pixbuf_moduledir/||g" "$APPDIR/$gdk_pixbuf_cache_file"
+if [ -f "$APPDIR/$gdk_pixbuf_cache_file" ]; then
+ sed -i "s|$gdk_pixbuf_moduledir/||g" "$APPDIR/$gdk_pixbuf_cache_file"
+fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [ ! -f "$APPDIR/$gtk3_immodules_cache_file" ]; then | |
| echo "WARNING: immodules.cache file is missing" | |
| fi | |
| sed -i "s|$gtk3_libdir/3.0.0/immodules/||g" "$APPDIR/$gtk3_immodules_cache_file" | |
| ;; | |
| if [ ! -f "$APPDIR/$gtk3_immodules_cache_file" ]; then | |
| echo "WARNING: immodules.cache file is missing" | |
| fi | |
| if [ -f "$APPDIR/$gtk3_immodules_cache_file" ]; then | |
| sed -i "s|$gtk3_libdir/3.0.0/immodules/||g" "$APPDIR/$gtk3_immodules_cache_file" | |
| fi | |
| ;; |
🤖 Prompt for AI Agents
In `@crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh`
around lines 227 - 231, The sed -i calls are not guarded and will fail under set
-e if the cache files are missing; update the blocks that reference APPDIR and
the cache variables (gtk3_immodules_cache_file and gdk_pixbuf_cache_file) to
only run sed -i when the target file exists (i.e., wrap the sed invocation in an
if [ -f "$APPDIR/$gtk3_immodules_cache_file" ] ... fi and similarly for
gdk_pixbuf_cache_file), keeping the existing warning messages intact so the
script won't abort when caches are absent.
| let gtk = tools_path.join("linuxdeploy-plugin-gtk.sh"); | ||
| if !gtk.exists() { | ||
| let data = download("https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh")?; | ||
| if gtk.exists() { | ||
| let data = include_bytes!("./linuxdeploy-plugin-gtk.sh"); | ||
| write_and_make_executable(>k, data)?; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate and examine the target file
fd "appimage/mod.rs" crates/tauri-bundler/src/bundle/linux/Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 224
🏁 Script executed:
# Read the appimage/mod.rs file around the target lines
sed -n '230,260p' crates/tauri-bundler/src/bundle/linux/appimage/mod.rsRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 1510
🏁 Script executed:
# Search for similar tool setup patterns
rg "tools_path\.join\(" crates/tauri-bundler/src/bundle/linux/appimage/mod.rs -A 3Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 1498
gtk plugin guard is inverted.
The gtk condition should be if !gtk.exists() to match all other tools (apprun, linuxdeploy, gstreamer, appimage). Currently it only writes the embedded script when the file already exists, which prevents fresh tool directories from receiving the gtk plugin.
🛠️ Proposed fix
- if gtk.exists() {
+ if !gtk.exists() {
let data = include_bytes!("./linuxdeploy-plugin-gtk.sh");
write_and_make_executable(>k, data)?;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let gtk = tools_path.join("linuxdeploy-plugin-gtk.sh"); | |
| if !gtk.exists() { | |
| let data = download("https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh")?; | |
| if gtk.exists() { | |
| let data = include_bytes!("./linuxdeploy-plugin-gtk.sh"); | |
| write_and_make_executable(>k, data)?; | |
| } | |
| let gtk = tools_path.join("linuxdeploy-plugin-gtk.sh"); | |
| if !gtk.exists() { | |
| let data = include_bytes!("./linuxdeploy-plugin-gtk.sh"); | |
| write_and_make_executable(>k, data)?; | |
| } |
🤖 Prompt for AI Agents
In `@crates/tauri-bundler/src/bundle/linux/appimage/mod.rs` around lines 242 -
246, The guard for the GTK plugin is inverted: change the condition on the gtk
Path (variable gtk) to check non-existence (if !gtk.exists()) so the embedded
bytes from include_bytes!("./linuxdeploy-plugin-gtk.sh") are written for fresh
tool directories; keep using write_and_make_executable(>k, data)? and the same
include_bytes! reference so behavior matches the other tool blocks (apprun,
linuxdeploy, gstreamer, appimage).
| // Custom error type for file operations | ||
| #[derive(Debug)] | ||
| enum FileWriteError { | ||
| Write(std::io::Error), | ||
| Permissions(std::io::Error), | ||
| } | ||
|
|
||
| impl std::fmt::Display for FileWriteError { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| match self { | ||
| FileWriteError::Write(e) => write!(f, "Failed to write file: {}", e), | ||
| FileWriteError::Permissions(e) => write!(f, "Failed to set permissions: {}", e), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl std::error::Error for FileWriteError { | ||
| fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
| match self { | ||
| FileWriteError::Write(e) => Some(e), | ||
| FileWriteError::Permissions(e) => Some(e), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn write_and_make_executable(path: &Path, data: &[u8]) -> Result<(), FileWriteError> { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for FileWriteError usage and conversion implementations
echo "=== FileWriteError usages ==="
rg -n "FileWriteError" --max-count=20
echo -e "\n=== From<FileWriteError> implementations ==="
rg -n "impl\s+From<\s*FileWriteError" -g '*.rs'
echo -e "\n=== crate::Error or crate::Result definitions ==="
rg -n "type Result|enum Error" crates/tauri-bundler/src/lib.rs | head -20Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 1752
🏁 Script executed:
#!/bin/bash
# Find calls to write_and_make_executable
echo "=== Calls to write_and_make_executable ==="
rg -n "write_and_make_executable" -B2 -A2
echo -e "\n=== crate::Result and crate::Error definitions ==="
find crates/tauri-bundler/src -name "lib.rs" -o -name "error.rs" | head -5
xargs grep -n "type Result\|pub enum Error" 2>/dev/null | head -30Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 3354
🏁 Script executed:
#!/bin/bash
# Check the error definitions
echo "=== crates/tauri-bundler/src/error.rs (first 100 lines) ==="
head -100 crates/tauri-bundler/src/error.rs
echo -e "\n=== crate::Result definition in lib.rs ==="
rg -n "type Result|pub enum Error" crates/tauri-bundler/src/lib.rs -A 10 | head -30Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 3127
🏁 Script executed:
#!/bin/bash
# Find Result type definition
echo "=== Result type in lib.rs ==="
rg -n "pub type Result|type Result" crates/tauri-bundler/src/lib.rs
echo -e "\n=== Function containing write_and_make_executable calls ==="
rg -B 15 "write_and_make_executable" crates/tauri-bundler/src/bundle/linux/appimage/mod.rs | head -60 | tail -30
echo -e "\n=== Function signature of prepare_tools ==="
rg -n "fn prepare_tools" crates/tauri-bundler/src/bundle/linux/appimage/mod.rs -A 2Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 1575
🏁 Script executed:
#!/bin/bash
# Check if code compiles or if there are any error conversions
echo "=== Full error.rs to see all From implementations ==="
wc -l crates/tauri-bundler/src/error.rs
echo -e "\n=== All From implementations in error.rs ==="
rg -n "#\[from\]|impl.*From" crates/tauri-bundler/src/error.rs
echo -e "\n=== Check if Result type is defined elsewhere ==="
rg -n "type Result" crates/tauri-bundler/src/ -A 1
echo -e "\n=== Check for any impl From<FileWriteError> anywhere ==="
rg -r "impl.*From.*FileWriteError" crates/Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 27328
🏁 Script executed:
#!/bin/bash
# Read the full prepare_tools function to see error handling
echo "=== Full prepare_tools function ==="
sed -n '226,315p' crates/tauri-bundler/src/bundle/linux/appimage/mod.rsRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 3219
Add From<FileWriteError> impl or use explicit error mapping for write_and_make_executable calls.
The function prepare_tools returns crate::Result<PathBuf> (which resolves to Result<PathBuf, Error>), but calls to write_and_make_executable at lines 232, 239, 245, 251, and 259 use ? to propagate FileWriteError. Since no From<FileWriteError> impl exists in the Error enum, this code will not compile. Either add:
impl From<FileWriteError> for Error {
fn from(err: FileWriteError) -> Self {
Error::BundlerError(anyhow::anyhow!("{}", err))
}
}Or map errors explicitly at each call site: .map_err(|e| /* convert to Error */)?
🤖 Prompt for AI Agents
In `@crates/tauri-bundler/src/bundle/linux/appimage/mod.rs` around lines 284 -
309, prepare_tools propagates FileWriteError from write_and_make_executable into
crate::Result<PathBuf> but there's no conversion to the crate Error type; either
implement impl From<FileWriteError> for Error that converts the FileWriteError
into Error::BundlerError (wrapping the display of the FileWriteError via anyhow)
or change each call site in prepare_tools to map_err into the crate Error
explicitly (e.g., .map_err(|e| Error::BundlerError(anyhow::anyhow!("{}", e)))
before using ?). Ensure you update FileWriteError, write_and_make_executable, or
the prepare_tools call sites accordingly so the ? operator can convert
FileWriteError into the crate Error.
| fn write_and_make_executable(path: &Path, data: &[u8]) -> Result<(), FileWriteError> { | ||
| use std::os::unix::fs::PermissionsExt; | ||
|
|
||
| fs::write(path, data)?; | ||
| fs::set_permissions(path, fs::Permissions::from_mode(0o770))?; | ||
| fs::set_permissions(path, fs::Permissions::from_mode(0o770)).map_err(FileWriteError::Permissions)?; | ||
| fs::write(path, data).map_err(FileWriteError::Write)?; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n crates/tauri-bundler/src/bundle/linux/appimage/mod.rs | sed -n '300,320p'Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 735
🏁 Script executed:
# Check the Rust standard library documentation for fs::set_permissions behavior
# Let's also look at the broader context of the function
cat -n crates/tauri-bundler/src/bundle/linux/appimage/mod.rs | sed -n '305,330p'Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 480
🏁 Script executed:
# Search for calls to write_and_make_executable to understand context
rg "write_and_make_executable" crates/tauri-bundler/src/bundle/linux/appimage/Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 753
🏁 Script executed:
# Also check if there's any documentation or test code about this function
rg -A 5 "write_and_make_executable" crates/tauri-bundler/src/bundle/linux/appimage/mod.rsRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 1535
🏁 Script executed:
# Let me check the broader context of the first call to understand if files might not exist
rg -B 10 "write_and_make_executable(&apprun" crates/tauri-bundler/src/bundle/linux/appimage/mod.rsRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 193
🏁 Script executed:
# Let me check the broader context - fixing the regex
rg -B 10 "write_and_make_executable.*apprun" crates/tauri-bundler/src/bundle/linux/appimage/mod.rsRepository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 540
🏁 Script executed:
# Let me see a few more examples to confirm the pattern
rg -B 5 "write_and_make_executable" crates/tauri-bundler/src/bundle/linux/appimage/mod.rs | head -50Repository: qodo-benchmark/tauri-combined-coderabbit
Length of output: 1755
Swap file write and permissions order to avoid NotFound error.
fs::set_permissions fails when called on non-existent files. Since this function is called primarily when files don't yet exist (after download), the write operation must happen first. The current order causes immediate failure in the common path.
Proposed fix
- fs::set_permissions(path, fs::Permissions::from_mode(0o770)).map_err(FileWriteError::Permissions)?;
- fs::write(path, data).map_err(FileWriteError::Write)?;
+ fs::write(path, data).map_err(FileWriteError::Write)?;
+ fs::set_permissions(path, fs::Permissions::from_mode(0o770)).map_err(FileWriteError::Permissions)?;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn write_and_make_executable(path: &Path, data: &[u8]) -> Result<(), FileWriteError> { | |
| use std::os::unix::fs::PermissionsExt; | |
| fs::write(path, data)?; | |
| fs::set_permissions(path, fs::Permissions::from_mode(0o770))?; | |
| fs::set_permissions(path, fs::Permissions::from_mode(0o770)).map_err(FileWriteError::Permissions)?; | |
| fs::write(path, data).map_err(FileWriteError::Write)?; | |
| fn write_and_make_executable(path: &Path, data: &[u8]) -> Result<(), FileWriteError> { | |
| use std::os::unix::fs::PermissionsExt; | |
| fs::write(path, data).map_err(FileWriteError::Write)?; | |
| fs::set_permissions(path, fs::Permissions::from_mode(0o770)).map_err(FileWriteError::Permissions)?; |
🤖 Prompt for AI Agents
In `@crates/tauri-bundler/src/bundle/linux/appimage/mod.rs` around lines 309 -
313, In write_and_make_executable, fs::set_permissions is called before
fs::write which fails for non-existent files; change the order so
write_and_make_executable writes the file first using fs::write (preserving the
existing FileWriteError::Write mapping) and then calls fs::set_permissions to
set 0o770 (preserving FileWriteError::Permissions mapping); keep the same error
mapping semantics and the use of PermissionsExt when adjusting the order.
Benchmark PR from qodo-benchmark#154
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.