The Windows installer and the Flatpak carried no ONNX Runtime, so they ran every model on tract's one core; the Arch package left it to an optional dependency. Each now installs two builds under runtimes/ — Intel's OpenVINO build and the generic WebGPU one, both with the CPU provider — fetched by tools/fetch-bundled-runtimes.sh from PyPI wheels pinned by SHA-256, pruned to the native libraries (81 + 31 MB on Linux, 67 + 42 MB on Windows), licence texts beside them. darkroom-desktop searches runtimes/openvino and runtimes/webgpu under each place a package installs to; the engine opens all it finds and keeps the one that fits the GPU, so a CUDA or ROCm runtime installed beside them still wins on its vendor's card. On Windows the chosen runtime's directory goes on PATH, because Intel's build leaves OpenVINO's DLLs for the loader to find there. The Windows image gains unzip; the installer smoke test checks both runtimes landed.
216 lines
11 KiB
YAML
216 lines
11 KiB
YAML
# Flatpak manifest — FR-PLAT-LIN-3 (sandboxed distribution), NFR-COMPAT-2.
|
||
#
|
||
# YAML rather than JSON because this file has more to explain than to declare,
|
||
# and JSON cannot hold a comment. flatpak-builder reads both.
|
||
#
|
||
# Build it, from the repository root:
|
||
#
|
||
# flatpak-builder --user --install --force-clean \
|
||
# build/flatpak packaging/flatpak/paris.tourolle.darkroom.yml
|
||
#
|
||
# Read docs/dev/distribution.md before changing any permission below. Every line in
|
||
# `finish-args` is a hole in the sandbox, and the one that is conspicuously
|
||
# absent — `--filesystem=` — is absent on purpose and is explained there.
|
||
id: paris.tourolle.darkroom
|
||
|
||
# 25.08 is the current freedesktop runtime, and the choice is made by what the
|
||
# binary needs rather than by what is newest. `ldd` on a release build names
|
||
# fontconfig, freetype, expat, libpng, zlib, brotli and bzip2 — all in the
|
||
# Platform — and nothing else: Vulkan, libxkbcommon and the two display-server
|
||
# protocols are reached without a link-time dependency (wgpu dlopens
|
||
# libvulkan.so.1, and x11rb and wayland-client speak the wire protocols in Rust
|
||
# rather than binding libxcb or libwayland). So the runtime has to supply a
|
||
# Vulkan loader and an ICD at *runtime*, which is the GL extension's job, and
|
||
# not much else.
|
||
runtime: org.freedesktop.Platform
|
||
runtime-version: '25.08'
|
||
sdk: org.freedesktop.Sdk
|
||
|
||
# The Rust toolchain is an SDK extension rather than something this manifest
|
||
# installs, so the build is offline-capable in the part that matters and the
|
||
# compiler is the one freedesktop tested against its own glibc.
|
||
#
|
||
# Note what this quietly overrides: `rust-toolchain.toml` pins 1.92.0, and that
|
||
# pin is honoured by *rustup*, which is not what the extension provides. The
|
||
# extension's cargo therefore ignores the file and builds with its own stable
|
||
# (1.98.0 on 25.08). That is fine here and deliberately different from
|
||
# CONTRIBUTING.md's "do not override the toolchain": the pin exists so `cargo
|
||
# fmt --check` and `clippy -D warnings` agree between a laptop and CI, and
|
||
# neither runs in this build. A *release binary* only needs a compiler at or
|
||
# above the workspace's `rust-version`.
|
||
sdk-extensions:
|
||
- org.freedesktop.Sdk.Extension.rust-stable
|
||
|
||
command: darkroom-desktop
|
||
|
||
finish-args:
|
||
# FR-PLAT-LIN-2 asks for both display servers. `fallback-x11` rather than
|
||
# `x11`: it grants the X socket only when Wayland is unavailable, so a
|
||
# Wayland session does not leave an X11 hole open beside the socket actually
|
||
# in use. `--share=ipc` goes with it — without it X11 cannot use shared
|
||
# memory and every frame is pushed through the socket instead.
|
||
- --socket=wayland
|
||
- --socket=fallback-x11
|
||
- --share=ipc
|
||
|
||
# The GPU. The develop pipeline is compute shaders through wgpu and there is
|
||
# no CPU renderer behind it, so this is not an optimisation: without
|
||
# /dev/dri the application starts and cannot develop anything.
|
||
#
|
||
# `dri` rather than `all`: it covers the render nodes and the NVIDIA device
|
||
# nodes, which is the whole of what a Vulkan ICD opens. `all` would add every
|
||
# other device on the machine for no gain.
|
||
- --device=dri
|
||
|
||
# Nextcloud (FR-NC-*). Nothing else here reaches the network — face grouping,
|
||
# segmentation and lens correction are all local and stay local (NFR-SEC-5).
|
||
- --share=network
|
||
|
||
# Credential storage (FR-NC-2). The keyring crate speaks the Secret Service
|
||
# D-Bus interface directly, which GNOME Keyring and KWallet's `ksecretd` both
|
||
# implement, so what it needs is a talk hole to that well-known name.
|
||
#
|
||
# Worth being precise, because FR-PLAT-LIN-3 says "Secret Service portal" and
|
||
# these are two different things: xdg-desktop-portal's `org.freedesktop.
|
||
# portal.Secret` hands an application a master key for a store it keeps
|
||
# itself, whereas this talks to the session's secret daemon. Only the latter
|
||
# puts the app password where `secret-tool` and Seahorse can see it, which is
|
||
# what makes a credential individually revocable by the user rather than
|
||
# opaque inside our own data directory. If no daemon answers, FR-NC-2's
|
||
# degraded mode is what the user gets — the same behaviour as outside a
|
||
# sandbox, which is the point.
|
||
- --talk-name=org.freedesktop.secrets
|
||
|
||
# No `--filesystem=` line of any kind, and this is the substance of
|
||
# FR-PLAT-LIN-3 rather than an omission.
|
||
#
|
||
# What that leaves working: /run/user/$UID/doc is mounted in every sandbox, so
|
||
# a photograph opened from a file manager — the .desktop entry declares the
|
||
# RAW MIME types and `Exec=darkroom-desktop %F` — arrives as a document-portal
|
||
# path in argv and opens. That path is genuinely portal-mediated and needs no
|
||
# code change.
|
||
#
|
||
# What that leaves to the portal: choosing a *library root*, an import's
|
||
# source and every other folder the desktop asks for. Since 0.17.0 they are
|
||
# chosen in `ui/dr-ui/src/folder_dialog.rs`, which asks the FileChooser
|
||
# portal through rfd (no talk hole needed: portals are always reachable),
|
||
# and a folder chosen there arrives as a document-portal path under
|
||
# /run/user/$UID/doc. That is the design; no Flatpak has been built from
|
||
# this manifest yet, so it has not been seen to work in the sandbox.
|
||
# Removable cards are still invisible to `dr_plat::volumes()`, which reads
|
||
# the sandbox's own mount table; the import page's Browse… reaches one
|
||
# through the same portal.
|
||
#
|
||
# `--filesystem=host` would make all of it work without the portal and is
|
||
# exactly what the requirement forbids, so it is not here.
|
||
# docs/dev/distribution.md §4 records what is left to prove and how to run a
|
||
# Flatpak build in the meantime.
|
||
|
||
modules:
|
||
- name: darkroom
|
||
buildsystem: simple
|
||
|
||
build-options:
|
||
append-path: /usr/lib/sdk/rust-stable/bin
|
||
env:
|
||
# Inside the build sandbox rather than in $HOME, so a rebuild starts
|
||
# from the state flatpak-builder is managing and not from whatever the
|
||
# host's cargo cache happens to hold.
|
||
CARGO_HOME: /run/build/darkroom/cargo
|
||
# Cargo fetches over 800 crates, and Flathub's builders forbid this — a
|
||
# submission there needs `cargo-sources.json` generated by
|
||
# flatpak-builder-tools' `flatpak-cargo-generator.py` from Cargo.lock,
|
||
# listing every crate as its own source, plus a vendored-registry
|
||
# `.cargo/config.toml`. That file is ~30k lines, has to be regenerated on
|
||
# every dependency change, and buys nothing for a build from a local
|
||
# checkout, which is what this manifest is for and what packaging/PKGBUILD
|
||
# is for as well. Add it when there is a Flathub submission, not before.
|
||
build-args:
|
||
- --share=network
|
||
|
||
build-commands:
|
||
# `--locked` for the reason CI uses it: a lockfile that resolves
|
||
# differently in the packaging build than in the tree is a release whose
|
||
# dependency versions nobody chose.
|
||
- cargo build --release --locked -p darkroom-desktop
|
||
|
||
- install -Dm755 target/release/darkroom-desktop /app/bin/darkroom-desktop
|
||
|
||
- install -Dm644 packaging/paris.tourolle.darkroom.desktop
|
||
/app/share/applications/paris.tourolle.darkroom.desktop
|
||
|
||
- install -Dm644 packaging/paris.tourolle.darkroom.metainfo.xml
|
||
/app/share/metainfo/paris.tourolle.darkroom.metainfo.xml
|
||
|
||
# The icon's name is the contract, not its path — the desktop entry says
|
||
# `Icon=paris.tourolle.darkroom` and the shell resolves that through the
|
||
# hicolor theme. 256x256 because that is the source's actual size;
|
||
# installing it under a size it is not makes scaled icons look wrong.
|
||
- install -Dm644 ui/dr-ui/ui/app-icon.png
|
||
/app/share/icons/hicolor/256x256/apps/paris.tourolle.darkroom.png
|
||
|
||
# The face models, where `system_face_models_dirs()` looks: it reads
|
||
# $XDG_DATA_DIRS, which includes /app/share inside a Flatpak, so this is
|
||
# the same lookup that finds /usr/share/darkroom/models from the Arch
|
||
# package. Last in the search order, so a pair the user dropped in their
|
||
# own data directory still outranks these.
|
||
#
|
||
# These live in Git LFS. A checkout made without `git lfs pull` has
|
||
# ~130-byte pointers here, and `type: dir` below would copy the pointers
|
||
# in without complaint — producing a Flatpak whose face indexing fails
|
||
# inside the graph loader on the user's machine. Refuse instead, with the
|
||
# command that fixes it.
|
||
- |
|
||
for m in scrfd_500m_640.onnx scrfd_2.5g_640.onnx scrfd_10g_640.onnx arcface_mbf_b1.onnx \
|
||
2d106det_b1.onnx ocec_s_b1.onnx sgc_l_48_b1.onnx; do
|
||
if [ "$(stat -c%s "models/face/$m")" -lt 100000 ]; then
|
||
echo "error: $m is an LFS pointer, not a model — run: git lfs pull" >&2
|
||
exit 1
|
||
fi
|
||
install -Dm644 "models/face/$m" "/app/share/darkroom/models/$m"
|
||
done
|
||
|
||
# The scene model, for the per-category grades. In the repository, unlike
|
||
# the face weights — AGPL, and GPLv3 §13 permits the combination
|
||
# (models/LICENCE.md) — so it installs unconditionally. Three files: the
|
||
# graph, its vocabulary, and the category descriptor; dr_ui reports the
|
||
# tab unavailable without any one of them. The pointer check is on the
|
||
# graph alone, the other two being legitimately small.
|
||
- |
|
||
if [ "$(stat -c%s models/scene/yolo26s-sem-ade20k.onnx)" -lt 100000 ]; then
|
||
echo "error: the scene model is an LFS pointer — run: git lfs pull" >&2
|
||
exit 1
|
||
fi
|
||
for m in yolo26s-sem-ade20k.onnx yolo26s-sem-ade20k.classes.json categories.txt; do
|
||
install -Dm644 "models/scene/$m" "/app/share/darkroom/models/$m"
|
||
done
|
||
|
||
# The two runtimes the app chooses between at launch
|
||
# (docs/dev/inference.md §3.2): Intel's OpenVINO build and the generic
|
||
# WebGPU one, each with ONNX Runtime's CPU provider — 8–10× the
|
||
# built-in tract on any machine. Pinned wheels, fetched over the
|
||
# build's network. In the sandbox the OpenVINO rung finds no Intel
|
||
# OpenCL driver and the probe settles on the CPU; WebGPU reaches the
|
||
# GPU through the runtime's Vulkan.
|
||
- ./tools/fetch-bundled-runtimes.sh linux /app/lib/darkroom/runtimes
|
||
|
||
- install -Dm644 README.md /app/share/doc/darkroom/README.md
|
||
|
||
sources:
|
||
# The local checkout, for the same reason packaging/PKGBUILD builds from
|
||
# one: this makes a Flatpak of what you are actually working on. Swap it
|
||
# for an `archive` or `git` source with a tag when there is a release to
|
||
# point at.
|
||
#
|
||
# `skip` is not tidiness. `target/` is tens of gigabytes and `.git` with
|
||
# LFS objects is not small; flatpak-builder copies a `dir` source
|
||
# wholesale, so without these two lines the copy is the slowest part of
|
||
# the build by a wide margin.
|
||
- type: dir
|
||
path: ../..
|
||
skip:
|
||
- target
|
||
- target-android
|
||
- .git
|
||
- build
|