mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-10-18 13:41:52 +02:00
feat: flatpak and snap support
This commit is contained in:
1 parent
feef010f55
commit
276d5b56f7
17 files changed
+1143
No files matched your search
@@ -0,0 +1,328 @@
|
||||
# Builds the Linux .deb once, repackages it as a Flatpak and as a Snap, and
|
||||
# starts each package headless to check that it runs inside its sandbox.
|
||||
|
||||
name: Linux Packages
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "packaging/**"
|
||||
- "snap/**"
|
||||
- ".github/workflows/linux-packages.yml"
|
||||
- "src-tauri/tauri.conf.json"
|
||||
- "src-tauri/Cargo.toml"
|
||||
- "src-tauri/donutbrowser.desktop"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "packaging/**"
|
||||
- "snap/**"
|
||||
- ".github/workflows/linux-packages.yml"
|
||||
- "src-tauri/tauri.conf.json"
|
||||
- "src-tauri/Cargo.toml"
|
||||
- "src-tauri/donutbrowser.desktop"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
check-wayfern:
|
||||
description: "Also download Wayfern (about 1 GB) and start it inside each sandbox"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: linux-packages-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
APP_ID: com.donutbrowser.DonutBrowser
|
||||
FLATPAK_MANIFEST: packaging/flatpak/com.donutbrowser.DonutBrowser.yml
|
||||
FLATPAK_LINT_EXCEPTIONS: packaging/flatpak/flatpak-builder-lint-exceptions.json
|
||||
METAINFO: packaging/linux/com.donutbrowser.DonutBrowser.metainfo.xml
|
||||
|
||||
jobs:
|
||||
deb:
|
||||
name: Build .deb
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 #v6.1.0
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 #v7.0.0
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 #master
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libxdo-dev pkg-config unzip xdg-utils
|
||||
|
||||
- name: Rust cache
|
||||
uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 #v2.9.2
|
||||
with:
|
||||
workdir: ./src-tauri
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# beforeBuildCommand builds the donut-proxy sidecar, downloads the
|
||||
# verified Xray-core sidecar and builds the frontend.
|
||||
- name: Build the .deb
|
||||
env:
|
||||
NEXT_PUBLIC_TURNSTILE: ${{ secrets.NEXT_PUBLIC_TURNSTILE }}
|
||||
run: |
|
||||
BUILD_TAG="nightly-$(git show -s --format=%cs HEAD)-${GITHUB_SHA::7}"
|
||||
export BUILD_TAG
|
||||
pnpm tauri build --bundles deb
|
||||
|
||||
- name: Collect the .deb
|
||||
run: |
|
||||
mkdir -p linux-deb
|
||||
cp src-tauri/target/release/bundle/deb/*.deb linux-deb/
|
||||
ls -l linux-deb
|
||||
test "$(find linux-deb -name '*.deb' | wc -l)" -eq 1
|
||||
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
||||
with:
|
||||
name: donut-deb
|
||||
path: linux-deb/*.deb
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
flatpak:
|
||||
name: Build Flatpak
|
||||
needs: deb
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 45
|
||||
container:
|
||||
# flatpak, flatpak-builder, flatpak-builder-lint and appstreamcli, with
|
||||
# org.gnome.Platform//50 and org.gnome.Sdk//50 already installed.
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-50
|
||||
options: --privileged
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
|
||||
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||
with:
|
||||
name: donut-deb
|
||||
path: linux-deb
|
||||
|
||||
- name: Put the .deb where the manifest expects it
|
||||
run: mv linux-deb/*.deb packaging/donut.deb
|
||||
|
||||
- name: Validate the AppStream metadata
|
||||
run: appstreamcli validate --no-net --explain "$METAINFO"
|
||||
|
||||
- name: Lint the manifest
|
||||
run: flatpak-builder-lint --exceptions --user-exceptions "$FLATPAK_LINT_EXCEPTIONS" manifest "$FLATPAK_MANIFEST"
|
||||
|
||||
- name: Restore the flatpak-builder cache
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0
|
||||
with:
|
||||
path: .flatpak-builder
|
||||
key: flatpak-builder-${{ hashFiles('packaging/flatpak/**') }}
|
||||
restore-keys: flatpak-builder-
|
||||
|
||||
# No --install-deps-from: the image already has the runtime and the SDK,
|
||||
# and updating a runtime in place fails on the container's overlay
|
||||
# filesystem ("Invalid cross-device link").
|
||||
- name: Build the Flatpak
|
||||
run: |
|
||||
flatpak-builder --repo=repo --disable-rofiles-fuse --force-clean \
|
||||
--default-branch=master build-dir "$FLATPAK_MANIFEST"
|
||||
|
||||
- name: Lint the build
|
||||
run: flatpak-builder-lint --exceptions --user-exceptions "$FLATPAK_LINT_EXCEPTIONS" repo repo
|
||||
|
||||
- name: Create the bundle
|
||||
run: |
|
||||
flatpak build-bundle --runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo \
|
||||
repo Donut.flatpak "$APP_ID" master
|
||||
ls -l Donut.flatpak
|
||||
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
||||
with:
|
||||
name: donut-flatpak-x86_64
|
||||
path: Donut.flatpak
|
||||
if-no-files-found: error
|
||||
|
||||
flatpak-smoke:
|
||||
name: Test Flatpak
|
||||
needs: flatpak
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
|
||||
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||
with:
|
||||
name: donut-flatpak-x86_64
|
||||
path: flatpak-bundle
|
||||
|
||||
- name: Install Flatpak and the test tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y flatpak xvfb imagemagick dbus
|
||||
# Ubuntu 24.04 can restrict unprivileged user namespaces, which
|
||||
# bubblewrap needs to build the sandbox.
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
|
||||
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
- name: Prepare a user runtime directory
|
||||
run: |
|
||||
dir="/run/user/$(id -u)"
|
||||
sudo install -d -m 700 -o "$(id -u)" -g "$(id -g)" "$dir"
|
||||
echo "XDG_RUNTIME_DIR=$dir" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Install the bundle
|
||||
run: |
|
||||
flatpak install --user -y --noninteractive flatpak-bundle/Donut.flatpak
|
||||
flatpak info --user "$APP_ID"
|
||||
|
||||
- name: Check that every library resolves inside the sandbox
|
||||
run: |
|
||||
# shellcheck disable=SC2016 # expanded by the shell inside the sandbox
|
||||
dbus-run-session -- flatpak run --command=sh "$APP_ID" -c '
|
||||
for f in /app/bin/donutbrowser /app/bin/donut-proxy /app/lib/libayatana-appindicator3.so.1; do
|
||||
echo "== $f"
|
||||
/lib64/ld-linux-x86-64.so.2 --list "$f"
|
||||
done' 2>&1 | tee libraries.txt
|
||||
if grep "not found" libraries.txt || ! grep -q "=>" libraries.txt; then
|
||||
echo "::error::A library does not resolve inside the Flatpak sandbox."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Start the app on a virtual display
|
||||
run: bash packaging/linux/smoke-test.sh smoke 45 flatpak run "$APP_ID"
|
||||
|
||||
- name: Stop the app and collect its logs
|
||||
if: always()
|
||||
run: |
|
||||
flatpak kill "$APP_ID" 2>/dev/null || true
|
||||
mkdir -p smoke
|
||||
cp -r "$HOME/.var/app/$APP_ID/data/com.donutbrowser/logs" smoke/app-logs 2>/dev/null || true
|
||||
cp libraries.txt smoke/ 2>/dev/null || true
|
||||
|
||||
- name: Download Wayfern
|
||||
if: ${{ inputs.check-wayfern }}
|
||||
run: |
|
||||
url="$(curl -fsSL https://donutbrowser.com/wayfern.json | jq -r '.downloads["linux-x64"]')"
|
||||
curl -fsSL --retry 5 -o "$RUNNER_TEMP/wayfern.tar.xz" "$url"
|
||||
|
||||
- name: Start Wayfern inside the sandbox
|
||||
if: ${{ inputs.check-wayfern }}
|
||||
run: bash packaging/linux/wayfern-check.sh flatpak "$RUNNER_TEMP/wayfern.tar.xz" smoke/wayfern
|
||||
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
||||
if: always()
|
||||
with:
|
||||
name: flatpak-smoke-test
|
||||
path: smoke
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
snap:
|
||||
name: Build and test Snap
|
||||
needs: deb
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
|
||||
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||
with:
|
||||
name: donut-deb
|
||||
path: linux-deb
|
||||
|
||||
- name: Put the .deb where snapcraft.yaml expects it
|
||||
run: mv linux-deb/*.deb packaging/donut.deb
|
||||
|
||||
- name: Install Snapcraft
|
||||
run: sudo snap install snapcraft --classic
|
||||
|
||||
# The runner is Ubuntu 24.04, the same system as the core24 base, so
|
||||
# the snap is built on the host rather than in an LXD instance.
|
||||
- name: Build the snap
|
||||
run: |
|
||||
sudo snapcraft pack --destructive-mode --verbosity=brief
|
||||
snap_file="$(ls donutbrowser_*.snap)"
|
||||
echo "SNAP_FILE=$snap_file" >> "$GITHUB_ENV"
|
||||
ls -l "$snap_file"
|
||||
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
||||
with:
|
||||
name: donut-snap-amd64
|
||||
path: ${{ env.SNAP_FILE }}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Prepare a user runtime directory
|
||||
run: |
|
||||
dir="/run/user/$(id -u)"
|
||||
sudo install -d -m 700 -o "$(id -u)" -g "$(id -g)" "$dir"
|
||||
echo "XDG_RUNTIME_DIR=$dir" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Install the snap
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y xvfb imagemagick dbus
|
||||
sudo snap install gnome-46-2404 mesa-2404 gtk-common-themes
|
||||
sudo snap install --dangerous "$SNAP_FILE"
|
||||
snap connections donutbrowser
|
||||
|
||||
- name: Check that every library resolves inside the snap
|
||||
run: |
|
||||
# shellcheck disable=SC2016 # expanded by the shell inside the sandbox
|
||||
snap run --shell donutbrowser -c '
|
||||
for f in "$SNAP/usr/bin/donutbrowser" "$SNAP/usr/bin/donut-proxy"; do
|
||||
echo "== $f"
|
||||
/lib64/ld-linux-x86-64.so.2 --list "$f"
|
||||
done' 2>&1 | tee libraries.txt
|
||||
if grep "not found" libraries.txt || ! grep -q "=>" libraries.txt; then
|
||||
echo "::error::A library does not resolve inside the snap."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Start the app on a virtual display
|
||||
run: bash packaging/linux/smoke-test.sh smoke 45 snap run donutbrowser
|
||||
|
||||
- name: Stop the app and collect its logs
|
||||
if: always()
|
||||
run: |
|
||||
pkill -f /snap/donutbrowser/ 2>/dev/null || true
|
||||
mkdir -p smoke
|
||||
cp -r "$HOME/snap/donutbrowser/current/.local/share/com.donutbrowser/logs" smoke/app-logs 2>/dev/null || true
|
||||
cp libraries.txt smoke/ 2>/dev/null || true
|
||||
sudo journalctl --no-pager -k --grep 'apparmor="DENIED"' 2>/dev/null | grep donutbrowser > smoke/apparmor-denials.txt || true
|
||||
echo "AppArmor denials: $(wc -l < smoke/apparmor-denials.txt)"
|
||||
|
||||
- name: Download Wayfern
|
||||
if: ${{ inputs.check-wayfern }}
|
||||
run: |
|
||||
url="$(curl -fsSL https://donutbrowser.com/wayfern.json | jq -r '.downloads["linux-x64"]')"
|
||||
curl -fsSL --retry 5 -o "$RUNNER_TEMP/wayfern.tar.xz" "$url"
|
||||
|
||||
- name: Start Wayfern inside the snap
|
||||
if: ${{ inputs.check-wayfern }}
|
||||
run: bash packaging/linux/wayfern-check.sh snap "$RUNNER_TEMP/wayfern.tar.xz" smoke/wayfern
|
||||
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
||||
if: always()
|
||||
with:
|
||||
name: snap-smoke-test
|
||||
path: smoke
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
@@ -89,6 +89,8 @@ donutbrowser/
|
||||
│ ├── python/ # `donutbrowser` (stdlib only, pytest)
|
||||
│ └── node/ # `@donutbrowser/sdk` (ESM TypeScript, node --test)
|
||||
├── patches/ # pnpm compatibility patches for secured dependencies
|
||||
├── packaging/ # Flatpak manifest, shared AppStream metainfo, Linux package smoke tests
|
||||
├── snap/ # snapcraft.yaml (strict confinement, repackages the .deb)
|
||||
├── flake.nix # Nix development environment
|
||||
└── .github/workflows/ # CI/CD pipelines
|
||||
```
|
||||
|
||||
@@ -86,6 +86,17 @@ If that gives an EGL display error, add `WEBKIT_DISABLE_DMABUF_RENDERER=1` or `G
|
||||
|
||||
</details>
|
||||
|
||||
### Flatpak and Snap
|
||||
|
||||
The [Linux Packages](https://github.com/zhom/donutbrowser/actions/workflows/linux-packages.yml) workflow builds an x86_64 Flatpak bundle and Snap from the `.deb`. Download `donut-flatpak-x86_64` or `donut-snap-amd64` from a successful run, unzip it, and install it:
|
||||
|
||||
```bash
|
||||
flatpak install --user Donut.flatpak
|
||||
sudo snap install --dangerous donutbrowser_*.snap
|
||||
```
|
||||
|
||||
The Snap leaves the microphone, the camera and the keyring (used by profile import) disconnected until you run `sudo snap connect donutbrowser:audio-record`, `donutbrowser:camera` or `donutbrowser:password-manager-service`. The Flatpak cannot change the default browser, so choose Donut in your system settings instead.
|
||||
|
||||
### Nix
|
||||
|
||||
```bash
|
||||
|
||||
@@ -7,6 +7,8 @@ extend-exclude = [
|
||||
# Auto-generated from commit subjects by release.yml; typos here originate
|
||||
# in commit messages, which are immutable, so don't spell-check it.
|
||||
"CHANGELOG.md",
|
||||
# Upstream patches for the Flatpak's tray libraries, applied verbatim.
|
||||
"packaging/flatpak/patches/",
|
||||
]
|
||||
|
||||
[default.extend-words]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# The .deb that the Flatpak manifest and snap/snapcraft.yaml repackage.
|
||||
/donut.deb
|
||||
@@ -0,0 +1,166 @@
|
||||
# Flatpak manifest for Donut Browser.
|
||||
#
|
||||
# This repackages the .deb that `pnpm tauri build --bundles deb` produces. It
|
||||
# takes only Donut's own files from it (the app, the donut-proxy and xray
|
||||
# sidecars, the desktop entry and the icons) and links against the WebKitGTK,
|
||||
# GTK and OpenSSL of the GNOME runtime. Nothing from the build host's /usr/lib
|
||||
# is copied: those libraries would shadow the runtime's and break it.
|
||||
#
|
||||
# Build it locally (x86_64) after `pnpm tauri build --bundles deb`:
|
||||
# cp src-tauri/target/release/bundle/deb/*.deb packaging/donut.deb
|
||||
# flatpak-builder --user --install --install-deps-from=flathub --force-clean \
|
||||
# build-dir packaging/flatpak/com.donutbrowser.DonutBrowser.yml
|
||||
id: com.donutbrowser.DonutBrowser
|
||||
runtime: org.gnome.Platform
|
||||
runtime-version: "50"
|
||||
sdk: org.gnome.Sdk
|
||||
command: donutbrowser
|
||||
rename-desktop-file: Donut.desktop
|
||||
rename-icon: donutbrowser
|
||||
|
||||
finish-args:
|
||||
# Proxies, VPNs, sync and Wayfern downloads. The local API, the MCP server and
|
||||
# the per-profile proxy workers listen on loopback ports.
|
||||
- --share=network
|
||||
# X11 shared memory, for the app and for Wayfern under XWayland.
|
||||
- --share=ipc
|
||||
- --socket=wayland
|
||||
- --socket=fallback-x11
|
||||
- --device=dri
|
||||
# Audio playback and microphone for the websites Wayfern opens.
|
||||
- --socket=pulseaudio
|
||||
# Wayfern saves downloads here, and cookie, profile and proxy exports go here.
|
||||
- --filesystem=xdg-download
|
||||
# The tray icon is written to $XDG_RUNTIME_DIR/tray-icon, and the desktop's
|
||||
# tray host reads it from the same path outside the sandbox.
|
||||
- --filesystem=xdg-run/tray-icon:create
|
||||
- --talk-name=org.kde.StatusNotifierWatcher
|
||||
# Profile import reads the source browser's "Safe Storage" secret.
|
||||
- --talk-name=org.freedesktop.secrets
|
||||
# Web notifications from the sites Wayfern opens.
|
||||
- --talk-name=org.freedesktop.Notifications
|
||||
# tauri-plugin-single-instance hands the second launch (a clicked link) to the
|
||||
# first one over this name, which is derived from the Tauri identifier.
|
||||
- --own-name=com.donutbrowser.SingleInstance
|
||||
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/pkgconfig
|
||||
- /share/man
|
||||
- "*.la"
|
||||
- "*.a"
|
||||
|
||||
modules:
|
||||
# The tray (tray-icon, through libappindicator-sys) dlopens
|
||||
# libayatana-appindicator3 at startup and panics when it is missing. The GNOME
|
||||
# runtime does not ship it. These five modules are Flathub's
|
||||
# shared-modules/libayatana-appindicator (commit cb9ec602), flattened.
|
||||
- name: intltool
|
||||
cleanup:
|
||||
- "*"
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://launchpad.net/intltool/trunk/0.51.0/+download/intltool-0.51.0.tar.gz
|
||||
mirror-urls:
|
||||
- https://gstreamer.freedesktop.org/src/mirror/intltool-0.51.0.tar.gz
|
||||
- https://mirror-hk.koddos.net/lfs/lfs-packages/12.0/intltool-0.51.0.tar.gz
|
||||
sha256: 67c74d94196b153b774ab9f89b2fa6c6ba79352407037c8c14d5aeb334e959cd
|
||||
- type: patch
|
||||
path: patches/intltool-perl5.26-regex-fixes.patch
|
||||
|
||||
- name: libdbusmenu
|
||||
buildsystem: autotools
|
||||
build-options:
|
||||
cflags: -Wno-error
|
||||
config-opts:
|
||||
- --with-gtk=3
|
||||
- --disable-dumper
|
||||
- --disable-static
|
||||
- --disable-tests
|
||||
- --disable-gtk-doc
|
||||
- --enable-introspection=no
|
||||
- --disable-vala
|
||||
cleanup:
|
||||
- "*.la"
|
||||
- /include
|
||||
- /lib/pkgconfig
|
||||
- /libexec
|
||||
- /share/doc
|
||||
- /share/gtk-doc
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://launchpad.net/libdbusmenu/16.04/16.04.0/+download/libdbusmenu-16.04.0.tar.gz
|
||||
mirror-urls:
|
||||
- https://sources.suse.com/SUSE:SLFO:Main:Build/libdbusmenu/a5b3e4717f09f90f9cd5568a5f933e8d/libdbusmenu-16.04.0.tar.gz
|
||||
- https://mirror.math.princeton.edu/pub/frugalware/frugalware-2.1/source/xlib/libdbusmenu/libdbusmenu-16.04.0.tar.gz
|
||||
sha256: b9cc4a2acd74509435892823607d966d424bd9ad5d0b00938f27240a1bfa878a
|
||||
- type: patch
|
||||
path: patches/libdbusmenu-fix-HAVE_VALGRIND-AM_CONDITIONAL.patch
|
||||
|
||||
- name: ayatana-ido
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DENABLE_INTROSPECTION=OFF
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/pkgconfig
|
||||
sources:
|
||||
- type: git
|
||||
url: https://github.com/AyatanaIndicators/ayatana-ido.git
|
||||
tag: 0.10.4
|
||||
commit: f968079b09e2310fefc3fc307359025f1c74b3eb
|
||||
- type: patch
|
||||
path: patches/ayatana-ido-make-introspection-configurable.patch
|
||||
|
||||
- name: libayatana-indicator
|
||||
buildsystem: cmake-ninja
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/pkgconfig
|
||||
- /libexec
|
||||
- /share
|
||||
sources:
|
||||
- type: git
|
||||
url: https://github.com/AyatanaIndicators/libayatana-indicator.git
|
||||
tag: 0.9.4
|
||||
commit: 611bb384b73fa6311777ba4c41381a06f5b99dad
|
||||
|
||||
- name: libayatana-appindicator
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DENABLE_BINDINGS_MONO=NO
|
||||
- -DENABLE_BINDINGS_VALA=NO
|
||||
- -DENABLE_GTKDOC=NO
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/pkgconfig
|
||||
sources:
|
||||
- type: git
|
||||
url: https://github.com/AyatanaIndicators/libayatana-appindicator.git
|
||||
tag: 0.5.94
|
||||
commit: 31e8bb083b307e1cc96af4874a94707727bd1e79
|
||||
|
||||
- name: donutbrowser
|
||||
buildsystem: simple
|
||||
sources:
|
||||
# CI puts the .deb here; see the note at the top for a local build.
|
||||
- type: file
|
||||
path: ../donut.deb
|
||||
- type: file
|
||||
path: ../linux/com.donutbrowser.DonutBrowser.metainfo.xml
|
||||
- type: file
|
||||
path: ../../LICENSE
|
||||
build-commands:
|
||||
- ar x donut.deb
|
||||
- mkdir deb && tar -xf data.tar.* -C deb
|
||||
- install -Dm755 -t /app/bin deb/usr/bin/donutbrowser deb/usr/bin/donut-proxy deb/usr/bin/xray
|
||||
# Tauri resolves its resource directory to <prefix>/lib/<productName>.
|
||||
- mkdir -p /app/lib && cp -r deb/usr/lib/Donut /app/lib/Donut
|
||||
- install -Dm644 -t /app/share/applications deb/usr/share/applications/Donut.desktop
|
||||
- |
|
||||
for size in 32x32 128x128 512x512; do
|
||||
install -Dm644 "deb/usr/share/icons/hicolor/$size/apps/donutbrowser.png" \
|
||||
"/app/share/icons/hicolor/$size/apps/donutbrowser.png"
|
||||
done
|
||||
- install -Dm644 -t /app/share/metainfo com.donutbrowser.DonutBrowser.metainfo.xml
|
||||
- install -Dm644 -t /app/share/licenses/com.donutbrowser.DonutBrowser LICENSE
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"com.donutbrowser.DonutBrowser": {
|
||||
"finish-args-own-name-com.donutbrowser.SingleInstance": "tauri-plugin-single-instance derives its session bus name from the Tauri identifier com.donutbrowser, which is not under the Flatpak app ID",
|
||||
"appstream-screenshots-not-mirrored-in-ostree": "Only the Flathub build pipeline mirrors screenshots; CI builds keep the upstream URL",
|
||||
"appstream-external-screenshot-url": "Only the Flathub build pipeline mirrors screenshots; CI builds keep the upstream URL"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
From 8a09e6ad33c58c017c0c8fd756da036fc39428ea Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Koskovich <akoskovich@pm.me>
|
||||
Date: Sun, 29 Sep 2024 13:47:54 -0400
|
||||
Subject: [PATCH 1/1] Make introspection configurable
|
||||
|
||||
---
|
||||
CMakeLists.txt | 1 +
|
||||
src/CMakeLists.txt | 4 ++++
|
||||
2 files changed, 5 insertions(+)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 0e13fcd..f3e9ec0 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -12,6 +12,7 @@ endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
option(ENABLE_TESTS "Enable all tests and checks" OFF)
|
||||
option(ENABLE_COVERAGE "Enable coverage reports (includes enabling all tests and checks)" OFF)
|
||||
option(ENABLE_WERROR "Treat all build warnings as errors" OFF)
|
||||
+option(ENABLE_INTROSPECTION "Enable introspection" ON)
|
||||
|
||||
if(ENABLE_COVERAGE)
|
||||
set(ENABLE_TESTS ON)
|
||||
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
|
||||
index 5b3638d..aca9481 100644
|
||||
--- a/src/CMakeLists.txt
|
||||
+++ b/src/CMakeLists.txt
|
||||
@@ -108,6 +108,8 @@ install(TARGETS "ayatana-ido3-0.4" LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIB
|
||||
|
||||
# AyatanaIdo3-0.4.gir
|
||||
|
||||
+if (ENABLE_INTROSPECTION)
|
||||
+
|
||||
find_package(GObjectIntrospection REQUIRED QUIET)
|
||||
|
||||
if (INTROSPECTION_FOUND)
|
||||
@@ -183,3 +185,5 @@ if (INTROSPECTION_FOUND)
|
||||
endif ()
|
||||
|
||||
endif ()
|
||||
+
|
||||
+endif ()
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
Description: Escape "{", to prevent complaints from perl 5.22 and 5.26
|
||||
Author: Roderich Schupp <roderich.schupp@gmail.com>
|
||||
Author: gregor herrmann <gregoa@debian.org>
|
||||
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=788705
|
||||
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=826471
|
||||
Bug-Upstream: https://bugs.launchpad.net/intltool/+bug/1490906
|
||||
|
||||
Index: intltool-0.51.0/intltool-update.in
|
||||
===================================================================
|
||||
--- intltool-0.51.0.orig/intltool-update.in 2017-07-23 17:24:35.113169465 +0200
|
||||
+++ intltool-0.51.0/intltool-update.in 2017-07-23 17:24:35.109169052 +0200
|
||||
@@ -1062,13 +1062,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
- if ($str =~ /^(.*)\${?([A-Z_]+)}?(.*)$/)
|
||||
+ if ($str =~ /^(.*)\$\{?([A-Z_]+)}?(.*)$/)
|
||||
{
|
||||
my $rest = $3;
|
||||
my $untouched = $1;
|
||||
my $sub = "";
|
||||
# Ignore recursive definitions of variables
|
||||
- $sub = $varhash{$2} if defined $varhash{$2} and $varhash{$2} !~ /\${?$2}?/;
|
||||
+ $sub = $varhash{$2} if defined $varhash{$2} and $varhash{$2} !~ /\$\{?$2}?/;
|
||||
|
||||
return SubstituteVariable ("$untouched$sub$rest");
|
||||
}
|
||||
@@ -1190,10 +1190,10 @@
|
||||
$name =~ s/\(+$//g;
|
||||
$version =~ s/\(+$//g;
|
||||
|
||||
- $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/);
|
||||
- $varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/);
|
||||
- $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/);
|
||||
- $varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/);
|
||||
+ $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\$\{?AC_PACKAGE_NAME}?/);
|
||||
+ $varhash{"PACKAGE"} = $name if (not $name =~ /\$\{?PACKAGE}?/);
|
||||
+ $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\$\{?AC_PACKAGE_VERSION}?/);
|
||||
+ $varhash{"VERSION"} = $version if (not $name =~ /\$\{?VERSION}?/);
|
||||
}
|
||||
|
||||
if ($conf_source =~ /^AC_INIT\(([^,\)]+),([^,\)]+)[,]?([^,\)]+)?/m)
|
||||
@@ -1219,11 +1219,11 @@
|
||||
$version =~ s/\(+$//g;
|
||||
$bugurl =~ s/\(+$//g if (defined $bugurl);
|
||||
|
||||
- $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/);
|
||||
- $varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/);
|
||||
- $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/);
|
||||
- $varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/);
|
||||
- $varhash{"PACKAGE_BUGREPORT"} = $bugurl if (defined $bugurl and not $bugurl =~ /\${?\w+}?/);
|
||||
+ $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\$\{?AC_PACKAGE_NAME}?/);
|
||||
+ $varhash{"PACKAGE"} = $name if (not $name =~ /\$\{?PACKAGE}?/);
|
||||
+ $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\$\{?AC_PACKAGE_VERSION}?/);
|
||||
+ $varhash{"VERSION"} = $version if (not $name =~ /\$\{?VERSION}?/);
|
||||
+ $varhash{"PACKAGE_BUGREPORT"} = $bugurl if (defined $bugurl and not $bugurl =~ /\$\{?\w+}?/);
|
||||
}
|
||||
|
||||
# \s makes this not work, why?
|
||||
@@ -0,0 +1,51 @@
|
||||
From 729546c51806a1b3ea6cb6efb7a115b1baa811f1 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Stefan=20Br=C3=BCns?= <stefan.bruens@rwth-aachen.de>
|
||||
Date: Mon, 18 Nov 2019 19:58:53 +0100
|
||||
Subject: [PATCH 1/1] Fix HAVE_VALGRIND AM_CONDITIONAL
|
||||
|
||||
The AM_CONDITIONAL should also be run with --disable-tests, otherwise
|
||||
HAVE_VALGRIND is undefined.
|
||||
---
|
||||
configure | 4 ++--
|
||||
configure.ac | 2 +-
|
||||
2 files changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/configure b/configure
|
||||
index 831a3bb..8913b9b 100644
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -14801,6 +14801,8 @@ else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
have_valgrind=yes
|
||||
+fi
|
||||
+
|
||||
fi
|
||||
if test "x$have_valgrind" = "xyes"; then
|
||||
HAVE_VALGRIND_TRUE=
|
||||
@@ -14811,8 +14813,6 @@ else
|
||||
fi
|
||||
|
||||
|
||||
-fi
|
||||
-
|
||||
|
||||
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index ace54d1..cbd38a6 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -120,8 +120,8 @@ PKG_CHECK_MODULES(DBUSMENUTESTS, json-glib-1.0 >= $JSON_GLIB_REQUIRED_VERSION
|
||||
[have_tests=yes]
|
||||
)
|
||||
PKG_CHECK_MODULES(DBUSMENUTESTSVALGRIND, valgrind, have_valgrind=yes, have_valgrind=no)
|
||||
-AM_CONDITIONAL([HAVE_VALGRIND], [test "x$have_valgrind" = "xyes"])
|
||||
])
|
||||
+AM_CONDITIONAL([HAVE_VALGRIND], [test "x$have_valgrind" = "xyes"])
|
||||
|
||||
AC_SUBST(DBUSMENUTESTS_CFLAGS)
|
||||
AC_SUBST(DBUSMENUTESTS_LIBS)
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2025 zhom@github -->
|
||||
<component type="desktop-application">
|
||||
<id>com.donutbrowser.DonutBrowser</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>AGPL-3.0-only</project_license>
|
||||
|
||||
<name>Donut Browser</name>
|
||||
<summary>Open source anti-detect browser</summary>
|
||||
<developer id="com.donutbrowser">
|
||||
<name>Donut Browser</name>
|
||||
</developer>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
Donut Browser manages any number of isolated browser profiles. Each profile
|
||||
keeps its own fingerprint, cookies, extensions and data, and runs on
|
||||
Wayfern, a Chromium-based engine that Donut downloads on first use.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Per-profile HTTP, HTTPS, SOCKS4 and SOCKS5 proxies, and WireGuard configs</li>
|
||||
<li>Per-profile DNS blocklists for ads and trackers</li>
|
||||
<li>Profile import from Chrome, Edge, Brave and other Chromium browsers</li>
|
||||
<li>Cookie and extension management</li>
|
||||
<li>A local REST API and a Model Context Protocol server for automation</li>
|
||||
<li>Optional end-to-end encrypted sync to Donut cloud or a self-hosted server</li>
|
||||
</ul>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">com.donutbrowser.DonutBrowser.desktop</launchable>
|
||||
<provides>
|
||||
<binary>donutbrowser</binary>
|
||||
</provides>
|
||||
|
||||
<url type="homepage">https://donutbrowser.com</url>
|
||||
<url type="bugtracker">https://github.com/zhom/donutbrowser/issues</url>
|
||||
<url type="vcs-browser">https://github.com/zhom/donutbrowser</url>
|
||||
<url type="contribute">https://github.com/zhom/donutbrowser/blob/main/CONTRIBUTING.md</url>
|
||||
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/zhom/donutbrowser/main/assets/donut-preview.png</image>
|
||||
<caption>The profile list</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
|
||||
<categories>
|
||||
<category>Network</category>
|
||||
<category>WebBrowser</category>
|
||||
</categories>
|
||||
|
||||
<supports>
|
||||
<control>keyboard</control>
|
||||
<control>pointing</control>
|
||||
</supports>
|
||||
<requires>
|
||||
<display_length compare="ge">768</display_length>
|
||||
</requires>
|
||||
|
||||
<content_rating type="oars-1.1" />
|
||||
|
||||
<releases>
|
||||
<release version="0.31.2" date="2026-09-23">
|
||||
<url type="details">https://github.com/zhom/donutbrowser/releases/tag/v0.31.2</url>
|
||||
</release>
|
||||
</releases>
|
||||
</component>
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bash
|
||||
# Start a packaged Donut Browser on a virtual display and check that it stays
|
||||
# up. .github/workflows/linux-packages.yml runs it for the Flatpak and the Snap.
|
||||
#
|
||||
# smoke-test.sh <output-dir> <seconds> <command> [args...]
|
||||
#
|
||||
# XDG_RUNTIME_DIR must point to a directory owned by the current user. The
|
||||
# script starts a private session bus at $XDG_RUNTIME_DIR/bus, the path both
|
||||
# sandboxes let the app reach, and an Xvfb display. It fails when the app exits
|
||||
# before <seconds> have passed or prints a Rust panic. The app's output and a
|
||||
# screenshot are left in <output-dir>. A screenshot with almost no colours
|
||||
# (a blank window) is reported as a warning.
|
||||
set -euo pipefail
|
||||
|
||||
out="$1"
|
||||
seconds="$2"
|
||||
shift 2
|
||||
mkdir -p "$out"
|
||||
|
||||
: "${XDG_RUNTIME_DIR:?XDG_RUNTIME_DIR must be set}"
|
||||
bus="$XDG_RUNTIME_DIR/bus"
|
||||
rm -f "$bus"
|
||||
dbus-daemon --session --address="unix:path=$bus" --nofork --nopidfile &
|
||||
dbus_pid=$!
|
||||
export DBUS_SESSION_BUS_ADDRESS="unix:path=$bus"
|
||||
|
||||
display_number=99
|
||||
Xvfb ":$display_number" -screen 0 1600x1000x24 -nolisten tcp >"$out/xvfb.log" 2>&1 &
|
||||
xvfb_pid=$!
|
||||
export DISPLAY=":$display_number"
|
||||
|
||||
trap 'kill "$xvfb_pid" "$dbus_pid" 2>/dev/null || true' EXIT
|
||||
|
||||
for _ in $(seq 1 100); do
|
||||
if [ -S "$bus" ] && [ -S "/tmp/.X11-unix/X$display_number" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
echo "Starting: $*"
|
||||
"$@" >"$out/app-output.log" 2>&1 &
|
||||
app_pid=$!
|
||||
|
||||
sleep "$seconds"
|
||||
|
||||
failed=0
|
||||
if kill -0 "$app_pid" 2>/dev/null; then
|
||||
echo "The app is still running after ${seconds}s."
|
||||
else
|
||||
code=0
|
||||
wait "$app_pid" || code=$?
|
||||
echo "::error::The app exited within ${seconds}s with exit code $code."
|
||||
failed=1
|
||||
fi
|
||||
|
||||
if command -v import >/dev/null 2>&1; then
|
||||
import -window root "$out/screenshot.png" || true
|
||||
colours="$(identify -format '%k' "$out/screenshot.png" 2>/dev/null || echo 0)"
|
||||
echo "The screenshot has $colours colours."
|
||||
if [ "$colours" -lt 64 ]; then
|
||||
echo "::warning::The screenshot has only $colours colours. The window may be blank."
|
||||
fi
|
||||
fi
|
||||
|
||||
kill "$app_pid" 2>/dev/null || true
|
||||
for _ in $(seq 1 50); do
|
||||
kill -0 "$app_pid" 2>/dev/null || break
|
||||
sleep 0.1
|
||||
done
|
||||
kill -9 "$app_pid" 2>/dev/null || true
|
||||
|
||||
if grep -n "panicked at" "$out/app-output.log"; then
|
||||
echo "::error::The app panicked."
|
||||
failed=1
|
||||
fi
|
||||
|
||||
echo "--- app output (last 100 lines)"
|
||||
tail -n 100 "$out/app-output.log" || true
|
||||
exit "$failed"
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check that the Wayfern build Donut downloads can run inside a package's
|
||||
# sandbox: its libraries resolve there, and it starts headless and answers on
|
||||
# a DevTools port, which is how Donut drives it.
|
||||
#
|
||||
# wayfern-check.sh flatpak|snap <archive> <output-dir>
|
||||
#
|
||||
# <archive> is the Wayfern Linux .tar.xz. It is unpacked into the app's own
|
||||
# data directory, the one place both sandboxes let the app execute files from.
|
||||
# XDG_RUNTIME_DIR must point to a directory owned by the current user.
|
||||
set -euo pipefail
|
||||
|
||||
kind="$1"
|
||||
archive="$2"
|
||||
out="$3"
|
||||
mkdir -p "$out"
|
||||
|
||||
case "$kind" in
|
||||
flatpak)
|
||||
app=com.donutbrowser.DonutBrowser
|
||||
dir="$HOME/.var/app/$app/data/wayfern-check"
|
||||
in_sandbox() { flatpak run --command=sh "$app" -c "$1"; }
|
||||
;;
|
||||
snap)
|
||||
dir="$HOME/snap/donutbrowser/common/wayfern-check"
|
||||
in_sandbox() { snap run --shell donutbrowser -c "$1"; }
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 flatpak|snap <archive> <output-dir>" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
: "${XDG_RUNTIME_DIR:?XDG_RUNTIME_DIR must be set}"
|
||||
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
|
||||
bus="$XDG_RUNTIME_DIR/bus"
|
||||
rm -f "$bus"
|
||||
dbus-daemon --session --address="unix:path=$bus" --nofork --nopidfile &
|
||||
dbus_pid=$!
|
||||
trap 'kill "$dbus_pid" 2>/dev/null || true' EXIT
|
||||
export DBUS_SESSION_BUS_ADDRESS="unix:path=$bus"
|
||||
for _ in $(seq 1 50); do [ -S "$bus" ] && break; sleep 0.1; done
|
||||
fi
|
||||
|
||||
rm -rf "$dir"
|
||||
mkdir -p "$dir"
|
||||
tar -xJf "$archive" -C "$dir"
|
||||
chrome="$(find "$dir" -type f -name chrome -perm -u+x | head -n 1)"
|
||||
if [ -z "$chrome" ]; then
|
||||
echo "::error::The archive has no chrome executable."
|
||||
exit 1
|
||||
fi
|
||||
echo "Wayfern executable: $chrome"
|
||||
|
||||
in_sandbox "/lib64/ld-linux-x86-64.so.2 --list '$chrome'" >"$out/wayfern-libraries.txt" 2>&1 || true
|
||||
if grep "not found" "$out/wayfern-libraries.txt" || ! grep -q "=>" "$out/wayfern-libraries.txt"; then
|
||||
echo "::error::Wayfern's libraries do not all resolve inside the sandbox."
|
||||
cat "$out/wayfern-libraries.txt"
|
||||
exit 1
|
||||
fi
|
||||
echo "Every Wayfern library resolves inside the sandbox."
|
||||
|
||||
# Donut asks the user to accept the Wayfern terms before the first launch and
|
||||
# then runs this same command. This runner is thrown away after the job.
|
||||
in_sandbox "'$chrome' --accept-terms-and-conditions" >"$out/wayfern-terms.log" 2>&1
|
||||
|
||||
port=9333
|
||||
in_sandbox "exec '$chrome' --headless=new --no-sandbox --disable-gpu --disable-dev-shm-usage \
|
||||
--no-first-run --user-data-dir='$dir/profile' --remote-debugging-address=127.0.0.1 \
|
||||
--remote-debugging-port=$port about:blank" >"$out/wayfern-output.log" 2>&1 &
|
||||
wayfern_pid=$!
|
||||
|
||||
answered=0
|
||||
for _ in $(seq 1 60); do
|
||||
if curl -fsS "http://127.0.0.1:$port/json/version" >"$out/wayfern-version.json" 2>/dev/null; then
|
||||
answered=1
|
||||
break
|
||||
fi
|
||||
kill -0 "$wayfern_pid" 2>/dev/null || break
|
||||
sleep 1
|
||||
done
|
||||
|
||||
kill "$wayfern_pid" 2>/dev/null || true
|
||||
pkill -f "$dir/" 2>/dev/null || true
|
||||
|
||||
if [ "$answered" -ne 1 ]; then
|
||||
echo "::error::Wayfern did not answer on its DevTools port."
|
||||
tail -n 40 "$out/wayfern-output.log" || true
|
||||
exit 1
|
||||
fi
|
||||
cat "$out/wayfern-version.json"
|
||||
@@ -0,0 +1,87 @@
|
||||
# Snap for Donut Browser.
|
||||
#
|
||||
# This repackages the .deb that `pnpm tauri build --bundles deb` produces
|
||||
# (the app, the donut-proxy and xray sidecars, the desktop entry and the icons).
|
||||
# WebKitGTK, GTK, libayatana-appindicator and NSS come from the gnome-46-2404
|
||||
# content snap through the gnome extension, not from the build host.
|
||||
#
|
||||
# Build it locally on Ubuntu 24.04 (x86_64) after `pnpm tauri build --bundles deb`:
|
||||
# cp src-tauri/target/release/bundle/deb/*.deb packaging/donut.deb
|
||||
# sudo snapcraft pack --destructive-mode
|
||||
name: donutbrowser
|
||||
title: Donut Browser
|
||||
base: core24
|
||||
adopt-info: donutbrowser
|
||||
license: AGPL-3.0-only
|
||||
grade: stable
|
||||
confinement: strict
|
||||
compression: lzo
|
||||
icon: src-tauri/icons/icon.png
|
||||
contact: mailto:contact@donutbrowser.com
|
||||
|
||||
platforms:
|
||||
amd64:
|
||||
|
||||
slots:
|
||||
# tauri-plugin-single-instance owns this session bus name so that a second
|
||||
# launch (a link clicked in another app) is handed to the running instance.
|
||||
single-instance:
|
||||
interface: dbus
|
||||
bus: session
|
||||
name: com.donutbrowser.SingleInstance
|
||||
|
||||
apps:
|
||||
donutbrowser:
|
||||
command: usr/bin/donutbrowser
|
||||
desktop: usr/share/applications/donutbrowser.desktop
|
||||
extensions: [gnome]
|
||||
environment:
|
||||
# $SNAP_USER_DATA is copied on every refresh so that a revert can restore
|
||||
# it. Downloaded Wayfern builds and profiles run to gigabytes, so they go
|
||||
# to the unversioned $SNAP_USER_COMMON instead, as the Chromium snap does.
|
||||
DONUTBROWSER_DATA_DIR: $SNAP_USER_COMMON/.local/share/DonutBrowser
|
||||
DONUTBROWSER_CACHE_DIR: $SNAP_USER_COMMON/.cache/DonutBrowser
|
||||
plugs:
|
||||
# Proxies, VPNs, sync and Wayfern downloads. The local API, the MCP server
|
||||
# and the per-profile proxy workers listen on loopback ports.
|
||||
- network
|
||||
- network-bind
|
||||
# Wayfern is Chromium based. Donut starts it with --no-sandbox, so the
|
||||
# plug does not need allow-sandbox.
|
||||
- browser-support
|
||||
- audio-playback
|
||||
# Microphone and camera for the websites Wayfern opens. Not connected
|
||||
# automatically: `snap connect donutbrowser:audio-record` / `:camera`.
|
||||
- audio-record
|
||||
- camera
|
||||
# Imports and exports go through the file dialog.
|
||||
- home
|
||||
# Profile import reads the source browser's "Safe Storage" secret. Not
|
||||
# connected automatically: `snap connect donutbrowser:password-manager-service`.
|
||||
- password-manager-service
|
||||
# Tracking and cleaning up the browser and proxy processes Donut starts.
|
||||
- system-observe
|
||||
# The tray icon (StatusNotifierItem / libayatana-appindicator).
|
||||
- unity7
|
||||
slots:
|
||||
- single-instance
|
||||
|
||||
parts:
|
||||
donutbrowser:
|
||||
plugin: dump
|
||||
source: packaging/donut.deb
|
||||
source-type: deb
|
||||
parse-info:
|
||||
- usr/share/metainfo/com.donutbrowser.DonutBrowser.metainfo.xml
|
||||
override-pull: |
|
||||
craftctl default
|
||||
version="$(sed -n 's/^ "version": "\([^"]*\)",$/\1/p' "$CRAFT_PROJECT_DIR/src-tauri/tauri.conf.json")"
|
||||
craftctl set version="$version"
|
||||
override-build: |
|
||||
craftctl default
|
||||
install -Dm644 "$CRAFT_PROJECT_DIR/packaging/linux/com.donutbrowser.DonutBrowser.metainfo.xml" \
|
||||
"$CRAFT_PART_INSTALL/usr/share/metainfo/com.donutbrowser.DonutBrowser.metainfo.xml"
|
||||
desktop="$CRAFT_PART_INSTALL/usr/share/applications"
|
||||
mv "$desktop/Donut.desktop" "$desktop/donutbrowser.desktop"
|
||||
sed -i 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/512x512/apps/donutbrowser.png|' \
|
||||
"$desktop/donutbrowser.desktop"
|
||||
@@ -523,6 +523,12 @@ impl AppAutoUpdater {
|
||||
/// Check if a system package manager repo is configured for this installation.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_repo_configured(&self) -> bool {
|
||||
// flatpak and snapd update these installs. Nothing inside the sandbox can
|
||||
// replace the app, so report the release the way a repository install does
|
||||
// instead of downloading a .deb that cannot be installed from in here.
|
||||
if crate::app_dirs::linux_sandbox().is_some() {
|
||||
return true;
|
||||
}
|
||||
let installation_method = self.detect_linux_installation_method();
|
||||
match installation_method {
|
||||
LinuxInstallationMethod::Deb => Self::is_deb_repo_configured(),
|
||||
|
||||
@@ -26,6 +26,50 @@ pub fn is_portable() -> bool {
|
||||
portable_dir().is_some()
|
||||
}
|
||||
|
||||
/// A Linux app sandbox that Donut can be installed through. In either one the
|
||||
/// app cannot replace its own files, and it does not see the desktop's
|
||||
/// default-application settings the way a .deb or .rpm install does.
|
||||
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LinuxSandbox {
|
||||
Flatpak,
|
||||
Snap,
|
||||
}
|
||||
|
||||
/// The sandbox this process runs in, or `None` for a regular install.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn linux_sandbox() -> Option<LinuxSandbox> {
|
||||
static SANDBOX: OnceLock<Option<LinuxSandbox>> = OnceLock::new();
|
||||
*SANDBOX.get_or_init(|| {
|
||||
detect_linux_sandbox(
|
||||
std::path::Path::new("/.flatpak-info").exists(),
|
||||
std::env::var_os("SNAP").map(PathBuf::from).as_deref(),
|
||||
std::env::current_exe().ok().as_deref(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Flatpak writes `/.flatpak-info` into every sandbox. A snap is recognised by
|
||||
/// the executable living under `$SNAP`, not by the variable alone: a snapped
|
||||
/// terminal or IDE leaks `SNAP` into every program started from it.
|
||||
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
fn detect_linux_sandbox(
|
||||
flatpak_info_exists: bool,
|
||||
snap_dir: Option<&std::path::Path>,
|
||||
exe: Option<&std::path::Path>,
|
||||
) -> Option<LinuxSandbox> {
|
||||
if flatpak_info_exists {
|
||||
return Some(LinuxSandbox::Flatpak);
|
||||
}
|
||||
match (snap_dir, exe) {
|
||||
// An empty `SNAP` is a prefix of every path.
|
||||
(Some(snap), Some(exe)) if !snap.as_os_str().is_empty() && exe.starts_with(snap) => {
|
||||
Some(LinuxSandbox::Snap)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional single-root override for all on-disk state. Set
|
||||
/// `DONUTBROWSER_DATA_ROOT=/path` (e.g. a tmpfs mount) to relocate
|
||||
/// data/cache/logs under `<root>/{data,cache,logs}` without touching the real
|
||||
@@ -460,6 +504,33 @@ mod tests {
|
||||
assert!(portable.join("cache").starts_with(&portable));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linux_sandbox_detection() {
|
||||
let snap = PathBuf::from("/snap/donutbrowser/x1");
|
||||
let snapped_exe = snap.join("usr/bin/donutbrowser");
|
||||
let deb_exe = PathBuf::from("/usr/bin/donutbrowser");
|
||||
|
||||
assert_eq!(
|
||||
detect_linux_sandbox(true, None, Some(&deb_exe)),
|
||||
Some(LinuxSandbox::Flatpak)
|
||||
);
|
||||
assert_eq!(
|
||||
detect_linux_sandbox(false, Some(&snap), Some(&snapped_exe)),
|
||||
Some(LinuxSandbox::Snap)
|
||||
);
|
||||
// A .deb install started from a snapped terminal inherits its `SNAP`.
|
||||
assert_eq!(
|
||||
detect_linux_sandbox(false, Some(&snap), Some(&deb_exe)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
detect_linux_sandbox(false, Some(&PathBuf::new()), Some(&deb_exe)),
|
||||
None
|
||||
);
|
||||
assert_eq!(detect_linux_sandbox(false, None, Some(&deb_exe)), None);
|
||||
assert_eq!(detect_linux_sandbox(false, Some(&snap), None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subdirectory_helpers() {
|
||||
assert!(profiles_dir().ends_with("profiles"));
|
||||
|
||||
@@ -848,11 +848,20 @@ mod windows {
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux {
|
||||
use crate::app_dirs::{linux_sandbox, LinuxSandbox};
|
||||
use std::process::Command;
|
||||
|
||||
const APP_DESKTOP_NAME: &str = "donutbrowser.desktop";
|
||||
|
||||
pub fn is_default_browser() -> Result<bool, String> {
|
||||
match linux_sandbox() {
|
||||
Some(LinuxSandbox::Snap) => {
|
||||
return snap_default_browser("check").map(|answer| answer == "yes")
|
||||
}
|
||||
Some(LinuxSandbox::Flatpak) => return Err(flatpak_cannot_manage_default()),
|
||||
None => {}
|
||||
}
|
||||
|
||||
// Check if xdg-mime is available
|
||||
if !is_xdg_mime_available() {
|
||||
return Err("xdg-mime utility not found. Please install xdg-utils package.".to_string());
|
||||
@@ -886,6 +895,19 @@ mod linux {
|
||||
}
|
||||
|
||||
pub fn set_as_default_browser() -> Result<(), String> {
|
||||
match linux_sandbox() {
|
||||
Some(LinuxSandbox::Snap) => {
|
||||
snap_default_browser("set")?;
|
||||
return if snap_default_browser("check")? == "yes" {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("xdg-settings did not make Donut Browser the default browser".to_string())
|
||||
};
|
||||
}
|
||||
Some(LinuxSandbox::Flatpak) => return Err(flatpak_cannot_manage_default()),
|
||||
None => {}
|
||||
}
|
||||
|
||||
// Check if xdg-mime is available
|
||||
if !is_xdg_mime_available() {
|
||||
return Err("xdg-mime utility not found. Please install xdg-utils package.".to_string());
|
||||
@@ -982,6 +1004,53 @@ mod linux {
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Inside a snap, `xdg-settings` is snapd's client for its
|
||||
/// `io.snapcraft.Settings` service, not the xdg-utils script. It asks the
|
||||
/// user before it changes anything. `xdg-mime` would only edit the
|
||||
/// mimeapps.list in the snap's private home, which the desktop never reads.
|
||||
fn snap_default_browser(action: &str) -> Result<String, String> {
|
||||
let output = Command::new("xdg-settings")
|
||||
.args(snap_settings_args(action))
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run xdg-settings: {e}"))?;
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"xdg-settings {action} failed: {}",
|
||||
String::from_utf8_lossy(&output.stderr).trim()
|
||||
));
|
||||
}
|
||||
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
|
||||
}
|
||||
|
||||
/// snapd installs the snap's desktop file as `<snap>_donutbrowser.desktop`
|
||||
/// and adds that prefix to the name it is given, so the name goes in bare.
|
||||
fn snap_settings_args(action: &str) -> [&str; 3] {
|
||||
[action, "default-web-browser", APP_DESKTOP_NAME]
|
||||
}
|
||||
|
||||
/// No portal lets a Flatpak app read or change the default browser, and
|
||||
/// `xdg-mime` in here would only edit the sandbox's own mimeapps.list.
|
||||
fn flatpak_cannot_manage_default() -> String {
|
||||
let app_id =
|
||||
std::env::var("FLATPAK_ID").unwrap_or_else(|_| "com.donutbrowser.DonutBrowser".to_string());
|
||||
format!(
|
||||
"A Flatpak app cannot change the default browser. Choose Donut Browser in your system settings (GNOME: Settings > Default Applications > Web; KDE: System Settings > Default Applications > Web Browser), or run this outside the sandbox: xdg-settings set default-web-browser {app_id}.desktop"
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn snapd_gets_the_desktop_name_without_its_snap_prefix() {
|
||||
assert_eq!(
|
||||
snap_settings_args("check"),
|
||||
["check", "default-web-browser", "donutbrowser.desktop"]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global singleton instance
|
||||
|
||||
Reference in new issue
Block a user