From 1cac0a49bafba391266c881c3959acde0eb90aa4 Mon Sep 17 00:00:00 2001 From: Felitendo <95575686+Felitendo@users.noreply.github.com> Date: Fri, 24 Jul 2026 08:26:27 +0200 Subject: [PATCH] bottles-bin: add package (built and published by CI) --- bottles-bin/.SRCINFO | 0 bottles-bin/PKGBUILD | 34 +++++ bottles-bin/pkg.sh | 49 +++++++ bottles-bin/remove-flatpak-checks.patch | 172 ++++++++++++++++++++++++ 4 files changed, 255 insertions(+) create mode 100644 bottles-bin/.SRCINFO create mode 100644 bottles-bin/PKGBUILD create mode 100644 bottles-bin/pkg.sh create mode 100644 bottles-bin/remove-flatpak-checks.patch diff --git a/bottles-bin/.SRCINFO b/bottles-bin/.SRCINFO new file mode 100644 index 0000000..e69de29 diff --git a/bottles-bin/PKGBUILD b/bottles-bin/PKGBUILD new file mode 100644 index 0000000..4a18e8f --- /dev/null +++ b/bottles-bin/PKGBUILD @@ -0,0 +1,34 @@ +# Maintainer: Felitendo +# This PKGBUILD is updated automatically: +# https://github.com/Felitendo/PKGBUILDS + +pkgname=bottles-bin +pkgver=0 +pkgrel=1 +pkgdesc="Easily manage wine and proton prefixes (prebuilt)" +arch=('any') +url="https://usebottles.com" +license=('GPL-3.0-only') +depends=('fvs2' 'gtk4' 'gtksourceview5' 'hicolor-icon-theme' 'icoextract' + 'libadwaita' 'libportal-gtk4' 'patool' 'python' 'python-cairo' + 'python-certifi' 'python-chardet' 'python-charset-normalizer' + 'python-gobject' 'python-idna' 'python-markdown' 'python-orjson' + 'python-pathvalidate' 'python-pefile' 'python-pycurl' + 'python-requests' 'python-urllib3' 'python-yaml' 'python-yara' + 'vkbasalt-cli') +optdepends=('vulkan-tools: vkcube test / Vulkan info' + 'xorg-xdpyinfo: display info detection' + 'imagemagick: icon/image conversion' + 'vmtouch: preload bottle files into memory' + 'mangohud: performance overlay' + 'gamemode: feral gamemode integration' + 'gamescope: gamescope session integration' + 'xterm: fallback terminal for "Run executable in terminal"') +provides=('bottles') +conflicts=('bottles') +source=("https://github.com/Felitendo/PKGBUILDS/releases/download/${pkgname}-${pkgver}/${pkgname}-${pkgver}.tar.zst") +sha256sums=('SKIP') + +package() { + cp -a "$srcdir/usr" "$pkgdir/" +} diff --git a/bottles-bin/pkg.sh b/bottles-bin/pkg.sh new file mode 100644 index 0000000..761c8f8 --- /dev/null +++ b/bottles-bin/pkg.sh @@ -0,0 +1,49 @@ +# bottles-bin - prebuilt package of https://github.com/bottlesdevs/Bottles +# (GTK4 + LibAdwaita wine/proton prefix manager written in Python). +# +# Upstream only supports Flatpak officially and publishes no usable binary +# assets, so build_artifact() builds from the release tarball, carrying the +# same de-flatpak patch the AUR source package uses (upstream's meson refuses +# to configure outside a Flatpak sandbox and the code hard-requires the +# portal). + +UPSTREAM_REPO="bottlesdevs/Bottles" + +# Installed in CI (pacman) before build_artifact runs. blueprint-compiler +# resolves Gtk/Adw/GtkSource imports via their typelibs, hence the GUI libs. +BUILD_DEPS=(meson ninja blueprint-compiler gobject-introspection + desktop-file-utils appstream-glib gettext glib2-devel + python python-gobject gtk4 libadwaita gtksourceview5 + libportal-gtk4) + +latest_version() { + gh api "repos/$UPSTREAM_REPO/releases/latest" --jq '.tag_name' | sed 's/^v//' +} + +# build_artifact +build_artifact() { + local ver="$1" outfile="$2" + local pkgdir workdir destdir srctree + pkgdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + workdir="$(mktemp -d)" + destdir="$(mktemp -d)" + + curl -sfL "https://github.com/$UPSTREAM_REPO/archive/refs/tags/$ver.tar.gz" | tar -xz -C "$workdir" + srctree="$workdir/Bottles-$ver" + + patch -d "$srctree" -p1 < "$pkgdir/remove-flatpak-checks.patch" + + meson setup "$workdir/build" "$srctree" --prefix=/usr --buildtype=release + meson install -C "$workdir/build" --destdir "$destdir" + + # These caches are generated by pacman hooks; shipping them would cause + # file conflicts on install. + rm -f "$destdir/usr/share/glib-2.0/schemas/gschemas.compiled" \ + "$destdir/usr/share/applications/mimeinfo.cache" \ + "$destdir/usr/share/icons/hicolor/icon-theme.cache" + + tar --zstd --sort=name --owner=0 --group=0 --numeric-owner --mtime='@0' \ + -cf "$outfile" -C "$destdir" usr + + rm -rf "$workdir" "$destdir" +} diff --git a/bottles-bin/remove-flatpak-checks.patch b/bottles-bin/remove-flatpak-checks.patch new file mode 100644 index 0000000..7cee3de --- /dev/null +++ b/bottles-bin/remove-flatpak-checks.patch @@ -0,0 +1,172 @@ +diff -Naur Bottles-64.1-orig/bottles/frontend/meson.build Bottles-64.1/bottles/frontend/meson.build +--- Bottles-64.1-orig/bottles/frontend/meson.build 2026-05-30 13:20:17.000000000 +0200 ++++ Bottles-64.1/bottles/frontend/meson.build 2026-06-25 20:16:08.858675375 +0200 +@@ -23,12 +23,6 @@ + configuration: conf + ) + +-fs = import('fs') +- +-if not fs.is_file('/' + '.flatpak-info') +- error('file does not exist') +-endif +- + bottles_sources = [ + '__init__.py', + 'main.py', +diff -Naur Bottles-64.1-orig/bottles/frontend/views/bottle_details.py Bottles-64.1/bottles/frontend/views/bottle_details.py +--- Bottles-64.1-orig/bottles/frontend/views/bottle_details.py 2026-05-30 13:20:17.000000000 +0200 ++++ Bottles-64.1/bottles/frontend/views/bottle_details.py 2026-06-25 20:17:24.688847866 +0200 +@@ -21,7 +21,7 @@ + from gettext import gettext as _ + from typing import Dict, List, Optional + +-from gi.repository import Adw, Gdk, Gio, GLib, Gtk, Xdp ++from gi.repository import Adw, Gdk, Gio, GLib, Gtk + + from bottles.backend.managers.backup import BackupManager + from bottles.backend.models.config import BottleConfig +@@ -692,20 +692,17 @@ + dialog.connect("response", execute) + dialog.show() + +- if Xdp.Portal.running_under_sandbox(): +- if self.window.settings.get_boolean("show-sandbox-warning"): +- dialog = Adw.MessageDialog.new( +- self.window, +- _("Be Aware of Sandbox"), +- _( +- "Bottles is running in a sandbox, a restricted permission environment needed to keep you safe. If the program won't run, consider moving inside the bottle (3 dots icon on the top), then launch from there." +- ), +- ) +- dialog.add_response("dismiss", _("_Dismiss")) +- dialog.connect("response", lambda *args: show_chooser()) +- dialog.present() +- else: +- show_chooser() ++ if self.window.settings.get_boolean("show-sandbox-warning"): ++ dialog = Adw.MessageDialog.new( ++ self.window, ++ _("Be Aware of Sandbox"), ++ _( ++ "Bottles is running in a sandbox, a restricted permission environment needed to keep you safe. If the program won't run, consider moving inside the bottle (3 dots icon on the top), then launch from there." ++ ), ++ ) ++ dialog.add_response("dismiss", _("_Dismiss")) ++ dialog.connect("response", lambda *args: show_chooser()) ++ dialog.present() + else: + show_chooser() + +diff -Naur Bottles-64.1-orig/bottles/frontend/views/bottle_preferences.py Bottles-64.1/bottles/frontend/views/bottle_preferences.py +--- Bottles-64.1-orig/bottles/frontend/views/bottle_preferences.py 2026-05-30 13:20:17.000000000 +0200 ++++ Bottles-64.1/bottles/frontend/views/bottle_preferences.py 2026-06-25 20:17:51.920386139 +0200 +@@ -20,7 +20,7 @@ + import re + from gettext import gettext as _ + +-from gi.repository import Adw, Gdk, Gtk, Xdp ++from gi.repository import Adw, Gdk, Gtk + + from bottles.backend.globals import ( + gamemode_available, +@@ -141,7 +141,7 @@ + self.queue = details.queue + self.details = details + +- if not gamemode_available or not Xdp.Portal.running_under_sandbox(): ++ if not gamemode_available: + return + + _not_available = _("This feature is unavailable on your system.") +diff -Naur Bottles-64.1-orig/bottles/frontend/views/list.py Bottles-64.1/bottles/frontend/views/list.py +--- Bottles-64.1-orig/bottles/frontend/views/list.py 2026-05-30 13:20:17.000000000 +0200 ++++ Bottles-64.1/bottles/frontend/views/list.py 2026-06-25 20:18:08.351774840 +0200 +@@ -18,7 +18,7 @@ + from datetime import datetime + from gettext import gettext as _, ngettext + +-from gi.repository import Adw, GLib, Gtk, Xdp ++from gi.repository import Adw, GLib, Gtk + + from bottles.backend.models.config import BottleConfig + from bottles.backend.models.result import Result +@@ -83,9 +83,6 @@ + + def run_executable(self, *_args): + """Display file dialog for executable""" +- if not Xdp.Portal.running_under_sandbox(): +- return +- + def set_path(_dialog, response): + if response != Gtk.ResponseType.ACCEPT: + return +diff -Naur Bottles-64.1-orig/bottles/frontend/views/new_bottle_dialog.py Bottles-64.1/bottles/frontend/views/new_bottle_dialog.py +--- Bottles-64.1-orig/bottles/frontend/views/new_bottle_dialog.py 2026-05-30 13:20:17.000000000 +0200 ++++ Bottles-64.1/bottles/frontend/views/new_bottle_dialog.py 2026-06-25 20:18:22.465190406 +0200 +@@ -20,7 +20,7 @@ + from threading import Event + from typing import Any, Optional + +-from gi.repository import Adw, Gio, GLib, GObject, Gtk, Pango, Xdp ++from gi.repository import Adw, Gio, GLib, GObject, Gtk, Pango + from pathvalidate import sanitize_filename + + from bottles.backend.models.config import BottleConfig +@@ -86,7 +86,7 @@ + super().__init__(**kwargs) + # common variables and references + self.window = GtkUtils.get_parent_window() +- if not self.window or not Xdp.Portal.running_under_sandbox(): ++ if not self.window: + return + + self.app = self.window.get_application() +diff -Naur Bottles-64.1-orig/bottles/frontend/windows/window.py Bottles-64.1/bottles/frontend/windows/window.py +--- Bottles-64.1-orig/bottles/frontend/windows/window.py 2026-05-30 13:20:17.000000000 +0200 ++++ Bottles-64.1/bottles/frontend/windows/window.py 2026-06-25 20:18:50.936202160 +0200 +@@ -22,7 +22,7 @@ + from gettext import gettext as _ + from typing import Optional + +-from gi.repository import Adw, Gdk, Gio, GLib, GObject, Gtk, Xdp ++from gi.repository import Adw, Gdk, Gio, GLib, GObject, Gtk + + from bottles.backend.globals import Paths + from bottles.backend.health import HealthChecker +@@ -125,35 +125,6 @@ + manager = Adw.StyleManager.get_default() + manager.set_color_scheme(Adw.ColorScheme.FORCE_DARK) + +- # Be VERY explicit that non-sandboxed environments are unsupported +- if not Xdp.Portal.running_under_sandbox(): +- +- def response(dialog, response, *args): +- if response == "close": +- quit(1) +- +- body = _( +- "Bottles is only supported within a sandboxed environment. Official sources of Bottles are available at" +- ) +- download_url = "usebottles.com/download" +- +- error_dialog = Adw.AlertDialog.new( +- _("Unsupported Environment"), +- f"{body} {download_url}.", +- ) +- +- error_dialog.add_response("close", _("Close")) +- error_dialog.set_body_use_markup(True) +- error_dialog.connect("response", response) +- error_dialog.present(self) +- logging.error( +- _( +- "Bottles is only supported within a sandboxed format. Official sources of Bottles are available at:" +- ) +- ) +- logging.error("https://usebottles.com/download/") +- return +- + # Loading view + self.page_loading = LoadingView() +