diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c7b6a03c..cfcadc01 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -94,12 +94,135 @@ jobs: name: agent-linux-amd64 path: artifacts/agent - - name: Download appimagetool + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libgdk-pixbuf2.0-bin + + - name: Download tools run: | curl -L -o appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage - chmod +x appimagetool + curl -L -o linuxdeploy https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + chmod +x appimagetool linuxdeploy + + - name: Prepare AppDirs + run: | + generate_desktop() { + local binary_name="$1" + local display_name="$2" + local desktop_id="$3" + local output="$4" + cat > "$output" << DESKTOP_EOF + [Desktop Entry] + Name=${display_name} + Comment=Deploy games to Steam Deck and Linux devices + Exec=${binary_name} + Icon=${desktop_id} + Type=Application + Categories=Game;Development; + Terminal=false + DESKTOP_EOF + } + + # Hub AppDir + mkdir -p hub.AppDir/usr/bin + cp artifacts/hub/capydeploy-hub-tauri hub.AppDir/usr/bin/ + chmod +x hub.AppDir/usr/bin/capydeploy-hub-tauri + cp apps/hub-tauri/src-tauri/icons/icon.png hub.AppDir/capydeploy-hub.png + generate_desktop "capydeploy-hub-tauri" "CapyDeploy Hub" "capydeploy-hub" "hub.AppDir/capydeploy-hub.desktop" + + # Agent AppDir + mkdir -p agent.AppDir/usr/bin + cp artifacts/agent/capydeploy-agent-tauri agent.AppDir/usr/bin/ + chmod +x agent.AppDir/usr/bin/capydeploy-agent-tauri + cp apps/agents/agent-tauri/src-tauri/icons/icon.png agent.AppDir/capydeploy-agent.png + generate_desktop "capydeploy-agent-tauri" "CapyDeploy Agent" "capydeploy-agent" "agent.AppDir/capydeploy-agent.desktop" + + - name: Bundle libraries + env: + APPIMAGE_EXTRACT_AND_RUN: "1" + run: | + # Bundle shared libraries with linuxdeploy + ./linuxdeploy --appdir hub.AppDir \ + --executable hub.AppDir/usr/bin/capydeploy-hub-tauri \ + --desktop-file hub.AppDir/capydeploy-hub.desktop \ + --icon-file hub.AppDir/capydeploy-hub.png + + ./linuxdeploy --appdir agent.AppDir \ + --executable agent.AppDir/usr/bin/capydeploy-agent-tauri \ + --desktop-file agent.AppDir/capydeploy-agent.desktop \ + --icon-file agent.AppDir/capydeploy-agent.png + + # Bundle WebKit helpers and patch hardcoded paths in the .so + # WEBKIT_EXEC_PATH only works in developer builds, so we binary-patch + # the .so to use relative paths (././ instead of /usr). + WEBKIT_DIR="" + for candidate in /usr/libexec/webkit2gtk-4.1 /usr/lib/x86_64-linux-gnu/webkit2gtk-4.1; do + if [ -d "$candidate" ]; then + WEBKIT_DIR="$candidate" + break + fi + done + if [ -z "$WEBKIT_DIR" ]; then + WKP=$(find /usr -name "WebKitWebProcess" -path "*webkit2gtk*" 2>/dev/null | head -1) + if [ -n "$WKP" ]; then + WEBKIT_DIR=$(dirname "$WKP") + fi + fi - - name: Generate AppRun script + for appdir in hub.AppDir agent.AppDir; do + WEBKIT_HARDCODED=$(strings "$appdir/usr/lib/libwebkit2gtk-4.1.so.0" 2>/dev/null | grep -m1 '/webkit2gtk-4.1$') + if [ -n "$WEBKIT_DIR" ] && [ -n "$WEBKIT_HARDCODED" ]; then + WEBKIT_RELATIVE="././${WEBKIT_HARDCODED#/usr}" + echo "Patching $appdir WebKit path: $WEBKIT_HARDCODED -> $WEBKIT_RELATIVE" + LC_ALL=C sed -i "s|$WEBKIT_HARDCODED|$WEBKIT_RELATIVE|g" "$appdir/usr/lib/libwebkit2gtk-4.1.so.0" + # Copy helpers to the relative path the patched .so expects + HELPERS_DEST="$appdir${WEBKIT_HARDCODED#/usr}" + mkdir -p "$HELPERS_DEST" + cp "$WEBKIT_DIR/WebKitWebProcess" "$HELPERS_DEST/" + cp "$WEBKIT_DIR/WebKitNetworkProcess" "$HELPERS_DEST/" + if [ -d "$WEBKIT_DIR/injected-bundle" ]; then + cp -r "$WEBKIT_DIR/injected-bundle" "$HELPERS_DEST/" + fi + else + echo "::warning::WebKit helpers or library not found for $appdir" + fi + done + + # Bundle GLib compiled schemas + if [ -f "/usr/share/glib-2.0/schemas/gschemas.compiled" ]; then + echo "Bundling GLib schemas..." + for appdir in hub.AppDir agent.AppDir; do + mkdir -p "$appdir/usr/share/glib-2.0/schemas" + cp /usr/share/glib-2.0/schemas/gschemas.compiled "$appdir/usr/share/glib-2.0/schemas/" + done + fi + + # Bundle GIO modules (TLS support) + GIO_DIR=$(pkg-config --variable=giomoduledir gio-2.0 2>/dev/null || echo "/usr/lib/x86_64-linux-gnu/gio/modules") + if [ -d "$GIO_DIR" ]; then + echo "Bundling GIO modules from $GIO_DIR..." + for appdir in hub.AppDir agent.AppDir; do + mkdir -p "$appdir/usr/lib/gio/modules" + cp "$GIO_DIR"/*.so "$appdir/usr/lib/gio/modules/" + done + fi + + # Bundle GDK pixbuf loaders + PIXBUF_DIR="/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0" + if compgen -G "$PIXBUF_DIR/loaders/*.so" > /dev/null 2>&1; then + echo "Bundling GDK pixbuf loaders..." + for appdir in hub.AppDir agent.AppDir; do + mkdir -p "$appdir/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders" + cp "$PIXBUF_DIR/loaders"/*.so "$appdir/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders/" + if command -v gdk-pixbuf-query-loaders &>/dev/null; then + GDK_PIXBUF_MODULEDIR="$appdir/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders" \ + gdk-pixbuf-query-loaders > "$appdir/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" + fi + done + fi + + - name: Generate AppRun scripts run: | generate_apprun() { local binary_name="$1" @@ -107,17 +230,30 @@ jobs: local display_name="$3" local find_pattern="$4" local output="$5" + # Remove linuxdeploy's AppRun (symlink to binary) to avoid overwriting the binary + rm -f "$output" cat > "$output" << APPRUN_EOF #!/bin/bash SELF=\$(readlink -f "\$0") HERE=\${SELF%/*} APPIMAGE="\${APPIMAGE:-\$SELF}" - APP_NAME="${binary_name}" + APP_NAME="${display_name}" + BINARY_NAME="${binary_name}" DESKTOP_NAME="${desktop_name}" INSTALL_DIR="\$HOME/.local/bin" DESKTOP_DIR="\$HOME/.local/share/applications" ICON_DIR="\$HOME/.local/share/icons" + # Bundled library paths + export LD_LIBRARY_PATH="\${HERE}/usr/lib:\${LD_LIBRARY_PATH}" + export GIO_MODULE_DIR="\${HERE}/usr/lib/gio/modules" + export GDK_PIXBUF_MODULE_FILE="\${HERE}/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" + export GSETTINGS_SCHEMA_DIR="\${HERE}/usr/share/glib-2.0/schemas" + + # WebKit helpers use paths patched to be relative (././ prefix), + # so we must cd to the AppDir root for them to resolve correctly. + cd "\${HERE}" + install_app() { echo "Installing \$APP_NAME..." mkdir -p "\$INSTALL_DIR" "\$DESKTOP_DIR" "\$ICON_DIR" @@ -205,36 +341,7 @@ jobs: chmod +x "$output" } - generate_desktop() { - local binary_name="$1" - local display_name="$2" - local output="$3" - cat > "$output" << DESKTOP_EOF - [Desktop Entry] - Name=${display_name} - Comment=Deploy games to Steam Deck and Linux devices - Exec=${binary_name} - Icon=${binary_name} - Type=Application - Categories=Game;Development; - Terminal=false - DESKTOP_EOF - } - - # Hub AppDir - mkdir -p hub.AppDir/usr/bin - cp artifacts/hub/capydeploy-hub-tauri hub.AppDir/usr/bin/ - chmod +x hub.AppDir/usr/bin/capydeploy-hub-tauri - cp apps/hub-tauri/src-tauri/icons/icon.png hub.AppDir/capydeploy-hub.png - generate_desktop "capydeploy-hub" "CapyDeploy Hub" "hub.AppDir/capydeploy-hub.desktop" generate_apprun "capydeploy-hub-tauri" "capydeploy-hub" "CapyDeploy Hub" "*capydeploy*hub*.AppImage" "hub.AppDir/AppRun" - - # Agent AppDir - mkdir -p agent.AppDir/usr/bin - cp artifacts/agent/capydeploy-agent-tauri agent.AppDir/usr/bin/ - chmod +x agent.AppDir/usr/bin/capydeploy-agent-tauri - cp apps/agents/agent-tauri/src-tauri/icons/icon.png agent.AppDir/capydeploy-agent.png - generate_desktop "capydeploy-agent" "CapyDeploy Agent" "agent.AppDir/capydeploy-agent.desktop" generate_apprun "capydeploy-agent-tauri" "capydeploy-agent" "CapyDeploy Agent" "*capydeploy*agent*.AppImage" "agent.AppDir/AppRun" - name: Build AppImages diff --git a/build_all.sh b/build_all.sh index 519c4d90..836e9aeb 100644 --- a/build_all.sh +++ b/build_all.sh @@ -98,6 +98,17 @@ generate_appimage() { echo -e " ${GREEN}Downloaded${NC}" fi + # Download linuxdeploy if needed (for bundling shared libraries) + local linuxdeploy="$TOOLS_DIR/linuxdeploy-$appimage_arch.AppImage" + if [ ! -f "$linuxdeploy" ]; then + echo " Downloading linuxdeploy..." + mkdir -p "$TOOLS_DIR" + curl -L -o "$linuxdeploy" \ + "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$appimage_arch.AppImage" + chmod +x "$linuxdeploy" + echo -e " ${GREEN}Downloaded${NC}" + fi + # Create AppDir rm -rf "$appdir" mkdir -p "$appdir/usr/bin" @@ -124,7 +135,105 @@ Categories=Game;Development; Terminal=false DESKTOP + # Bundle shared libraries with linuxdeploy + echo " Bundling shared libraries..." + APPIMAGE_EXTRACT_AND_RUN=1 "$linuxdeploy" \ + --appdir "$appdir" \ + --executable "$appdir/usr/bin/$binary_name" \ + --desktop-file "$appdir/$desktop_id.desktop" \ + --icon-file "$appdir/$desktop_id.png" + + # Bundle WebKit helper processes and patch hardcoded paths + # WebKit hardcodes the helper path at compile time and WEBKIT_EXEC_PATH + # only works in developer builds. We must binary-patch the .so to use + # relative paths (././ instead of /usr) and cd to AppDir before exec. + local webkit_hardcoded + webkit_hardcoded=$(strings "$appdir/usr/lib/libwebkit2gtk-4.1.so.0" 2>/dev/null | grep -m1 '/webkit2gtk-4.1$') + local webkit_dir="" + for candidate in /usr/libexec/webkit2gtk-4.1 /usr/lib/x86_64-linux-gnu/webkit2gtk-4.1 /usr/lib64/webkit2gtk-4.1; do + if [ -d "$candidate" ]; then + webkit_dir="$candidate" + break + fi + done + if [ -z "$webkit_dir" ]; then + local wkp + wkp=$(find /usr -name "WebKitWebProcess" -path "*webkit2gtk*" 2>/dev/null | head -1) + if [ -n "$wkp" ]; then + webkit_dir=$(dirname "$wkp") + fi + fi + if [ -n "$webkit_dir" ] && [ -n "$webkit_hardcoded" ]; then + local webkit_relative="././${webkit_hardcoded#/usr}" + echo " Patching WebKit path: $webkit_hardcoded -> $webkit_relative" + LC_ALL=C sed -i "s|$webkit_hardcoded|$webkit_relative|g" "$appdir/usr/lib/libwebkit2gtk-4.1.so.0" + # Copy helpers to the relative path that the patched .so expects + local helpers_dest="$appdir${webkit_hardcoded#/usr}" + mkdir -p "$helpers_dest" + cp "$webkit_dir/WebKitWebProcess" "$helpers_dest/" + cp "$webkit_dir/WebKitNetworkProcess" "$helpers_dest/" + # Copy injected bundle if present + if [ -d "$webkit_dir/injected-bundle" ]; then + cp -r "$webkit_dir/injected-bundle" "$helpers_dest/" + fi + echo " Bundling WebKit helpers from $webkit_dir" + else + echo -e " ${YELLOW}[WARN]${NC} WebKit helpers or library not found" + fi + + # Bundle GLib compiled schemas + if [ -f "/usr/share/glib-2.0/schemas/gschemas.compiled" ]; then + echo " Bundling GLib schemas..." + mkdir -p "$appdir/usr/share/glib-2.0/schemas" + cp /usr/share/glib-2.0/schemas/gschemas.compiled "$appdir/usr/share/glib-2.0/schemas/" + fi + + # Bundle GIO modules (TLS support) + local gio_dir + gio_dir=$(pkg-config --variable=giomoduledir gio-2.0 2>/dev/null) + if [ -z "$gio_dir" ] || [ ! -d "$gio_dir" ]; then + for candidate in /usr/lib/x86_64-linux-gnu/gio/modules /usr/lib64/gio/modules; do + if [ -d "$candidate" ]; then + gio_dir="$candidate" + break + fi + done + fi + if [ -n "$gio_dir" ] && [ -d "$gio_dir" ]; then + echo " Bundling GIO modules from $gio_dir..." + mkdir -p "$appdir/usr/lib/gio/modules" + cp "$gio_dir"/*.so "$appdir/usr/lib/gio/modules/" + fi + + # Bundle GDK pixbuf loaders + local pixbuf_module_dir + pixbuf_module_dir=$(pkg-config --variable=gdk_pixbuf_moduledir gdk-pixbuf-2.0 2>/dev/null) + local pixbuf_dir="" + if [ -n "$pixbuf_module_dir" ] && [ -d "$pixbuf_module_dir" ]; then + pixbuf_dir=$(dirname "$pixbuf_module_dir") + else + for candidate in /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0 /usr/lib64/gdk-pixbuf-2.0/2.10.0; do + if [ -d "$candidate" ]; then + pixbuf_dir="$candidate" + break + fi + done + fi + if [ -n "$pixbuf_dir" ] && compgen -G "$pixbuf_dir/loaders/*.so" > /dev/null 2>&1; then + echo " Bundling GDK pixbuf loaders..." + mkdir -p "$appdir/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders" + cp "$pixbuf_dir/loaders"/*.so "$appdir/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders/" + if command -v gdk-pixbuf-query-loaders &>/dev/null; then + GDK_PIXBUF_MODULEDIR="$appdir/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders" \ + gdk-pixbuf-query-loaders > "$appdir/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" + fi + fi + + echo -e " ${GREEN}Libraries bundled${NC}" + # Create AppRun with install/uninstall support + # Remove linuxdeploy's AppRun (symlink to binary) to avoid overwriting the binary + rm -f "$appdir/AppRun" cat > "$appdir/AppRun" << 'APPRUN' #!/bin/bash SELF=$(readlink -f "$0") @@ -137,6 +246,16 @@ INSTALL_DIR="$HOME/.local/bin" DESKTOP_DIR="$HOME/.local/share/applications" ICON_DIR="$HOME/.local/share/icons" +# Bundled library paths +export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}" +export GIO_MODULE_DIR="${HERE}/usr/lib/gio/modules" +export GDK_PIXBUF_MODULE_FILE="${HERE}/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" +export GSETTINGS_SCHEMA_DIR="${HERE}/usr/share/glib-2.0/schemas" + +# WebKit helpers use paths patched to be relative (././ prefix), +# so we must cd to the AppDir root for them to resolve correctly. +cd "${HERE}" + install_app() { echo "Installing $APP_NAME..."