diff --git a/.gitignore b/.gitignore index 72cc1e3..d4e7ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ env/ *.egg-info/ dist/ build/ +flatpak-build/ +flatpak-repo/ +*.flatpak # Test caches .pytest_cache/ diff --git a/README.md b/README.md index 9add4bb..fef47dc 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Not implemented yet: - [docker-compose.yml](docker-compose.yml) — local deployment - [requirements.txt](requirements.txt) — Python dependencies - [client.py](client.py) — minimal CLI client -- [gtk_client.py](gtk_client.py) — GTK4 desktop client +- [cellar-gtk.py](cellar-gtk.py) — GTK4 desktop client - [app/main.py](app/main.py) — API routes - [app/models.py](app/models.py) — SQLAlchemy model - [app/schemas.py](app/schemas.py) — API response schemas @@ -163,6 +163,56 @@ Interactive API docs are available at: --- +## Flatpak package + +The repository now includes a Flatpak manifest for the GTK client: + +- [net.enne2.Cellar.yaml](net.enne2.Cellar.yaml) + +Flatpak packaging assets are stored in [flatpak/](flatpak/): + +- desktop entry +- AppStream metadata +- launcher script +- icon generated from [cellar.jpg](cellar.jpg) + +Typical local build flow: + +1. install `flatpak-builder` +2. install the required runtimes: + - `org.gnome.Platform//47` + - `org.gnome.Sdk//47` +3. run: + +`flatpak-builder --user --install --force-clean flatpak-build net.enne2.Cellar.yaml` + +Then launch it with: + +`flatpak run net.enne2.Cellar` + +To export a private Flatpak repository and a single-file bundle, use: + +`./scripts/build-flatpak-repo.sh` + +This creates: + +- `flatpak-repo/` — OSTree/Flatpak repository ready to publish +- `dist/net.enne2.Cellar.flatpak` — installable Flatpak bundle + +### Publish on `brain.local` + +If `brain.local` serves static files, copy the generated repository there, for example: + +`./scripts/publish-flatpak-repo.sh brain.local /var/www/html/flatpak/cellar` + +Then clients can add the remote and install the app with: + +`flatpak remote-add --if-not-exists --user --no-gpg-verify brain-local http://brain.local/flatpak/cellar` + +`flatpak install --user brain-local net.enne2.Cellar` + +--- + ## Environment configuration Current environment variables used by the API container: @@ -257,7 +307,7 @@ Use a custom Bottles directory: Run: -`python gtk_client.py` +`python cellar-gtk.py` The GUI lets you: diff --git a/cellar-gtk.py b/cellar-gtk.py index 6d7f2a5..ada6519 100755 --- a/cellar-gtk.py +++ b/cellar-gtk.py @@ -42,6 +42,7 @@ gi.require_version("Adw", "1") from gi.repository import Adw, Gdk, GLib, Gtk # type: ignore[import-not-found] # ── Constants ─────────────────────────────────────────────────── +APP_ID = "net.enne2.Cellar" CONFIG_PATH = Path.home() / ".config" / "cellar" / "gtk_client.json" APP_CSS = """\ @@ -448,6 +449,7 @@ class CellarWindow(Adw.ApplicationWindow): def __init__(self, app: Adw.Application): super().__init__(application=app, title="Cellar") + self.set_icon_name(APP_ID) self.config = load_config() self.settings_window: SettingsWindow | None = None self.rows: list[ArchiveRow] = [] @@ -824,7 +826,7 @@ class CellarWindow(Adw.ApplicationWindow): # ── Application ───────────────────────────────────────────────── class CellarApplication(Adw.Application): def __init__(self) -> None: - super().__init__(application_id="com.cellar.gtkclient") + super().__init__(application_id=APP_ID) def do_activate(self) -> None: _load_css() diff --git a/flatpak/cellar-launcher.sh b/flatpak/cellar-launcher.sh new file mode 100644 index 0000000..ae2e65a --- /dev/null +++ b/flatpak/cellar-launcher.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu +export PYTHONPATH="/app/share/cellar${PYTHONPATH:+:$PYTHONPATH}" +exec python3 /app/share/cellar/cellar-gtk.py "$@" diff --git a/flatpak/net.enne2.Cellar.desktop b/flatpak/net.enne2.Cellar.desktop new file mode 100644 index 0000000..1589be2 --- /dev/null +++ b/flatpak/net.enne2.Cellar.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Type=Application +Name=Cellar +Comment=Browse and restore Bottle archives from a Cellar server +Exec=cellar +Icon=net.enne2.Cellar +Terminal=false +Categories=Utility;Archiving;GTK; +Keywords=bottles;backup;archive;restore;wine; +StartupNotify=true +StartupWMClass=net.enne2.Cellar diff --git a/flatpak/net.enne2.Cellar.metainfo.xml b/flatpak/net.enne2.Cellar.metainfo.xml new file mode 100644 index 0000000..ac38aa0 --- /dev/null +++ b/flatpak/net.enne2.Cellar.metainfo.xml @@ -0,0 +1,39 @@ + + + net.enne2.Cellar + Cellar + Browse and restore Bottle archives from a self-hosted server + CC0-1.0 + LicenseRef-proprietary + + Matteo Benedetto + + net.enne2.Cellar.desktop + https://git.enne2.net/enne2/cellar + https://git.enne2.net/enne2/cellar/issues + + cellar + + +

Cellar is a small GTK application for browsing and restoring archives stored on a self-hosted Bottle archive server.

+

It can refresh the remote catalog, search archives locally, and reinstall archived Bottles environments with progress feedback.

+
+ + Utility + Archiving + + + + Cellar application artwork + https://git.enne2.net/enne2/cellar/raw/branch/master/cellar.jpg + + + + #5b0f11 + #d8b27d + + + + + +
diff --git a/flatpak/net.enne2.Cellar.png b/flatpak/net.enne2.Cellar.png new file mode 100644 index 0000000..a8b38d0 Binary files /dev/null and b/flatpak/net.enne2.Cellar.png differ diff --git a/net.enne2.Cellar.yaml b/net.enne2.Cellar.yaml new file mode 100644 index 0000000..3bdbee3 --- /dev/null +++ b/net.enne2.Cellar.yaml @@ -0,0 +1,27 @@ +app-id: net.enne2.Cellar +branch: stable +runtime: org.gnome.Platform +runtime-version: '47' +sdk: org.gnome.Sdk +command: cellar +finish-args: + - --share=ipc + - --share=network + - --socket=wayland + - --socket=fallback-x11 + - --device=dri + - --filesystem=home +modules: + - name: cellar + buildsystem: simple + build-commands: + - install -Dm755 flatpak/cellar-launcher.sh /app/bin/cellar + - install -Dm644 cellar-gtk.py /app/share/cellar/cellar-gtk.py + - install -Dm644 client.py /app/share/cellar/client.py + - install -Dm644 cellar.jpg /app/share/cellar/cellar.jpg + - install -Dm644 flatpak/net.enne2.Cellar.desktop /app/share/applications/net.enne2.Cellar.desktop + - install -Dm644 flatpak/net.enne2.Cellar.metainfo.xml /app/share/metainfo/net.enne2.Cellar.metainfo.xml + - install -Dm644 flatpak/net.enne2.Cellar.png /app/share/icons/hicolor/512x512/apps/net.enne2.Cellar.png + sources: + - type: dir + path: . diff --git a/scripts/build-flatpak-repo.sh b/scripts/build-flatpak-repo.sh new file mode 100755 index 0000000..9be9c76 --- /dev/null +++ b/scripts/build-flatpak-repo.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +APP_ID="net.enne2.Cellar" +BRANCH="stable" +MANIFEST="${ROOT_DIR}/net.enne2.Cellar.yaml" +BUILD_DIR="${ROOT_DIR}/flatpak-build" +REPO_DIR="${ROOT_DIR}/flatpak-repo" +BUNDLE_PATH="${ROOT_DIR}/dist/${APP_ID}.flatpak" + +if ! command -v flatpak-builder >/dev/null 2>&1; then + echo "flatpak-builder not found. Install it first." >&2 + exit 1 +fi + +if ! command -v flatpak >/dev/null 2>&1; then + echo "flatpak not found. Install it first." >&2 + exit 1 +fi + +mkdir -p "${ROOT_DIR}/dist" +rm -rf "${BUILD_DIR}" +mkdir -p "${REPO_DIR}" + +flatpak-builder \ + --force-clean \ + --repo="${REPO_DIR}" \ + "${BUILD_DIR}" \ + "${MANIFEST}" + +flatpak build-bundle "${REPO_DIR}" "${BUNDLE_PATH}" "${APP_ID}" "${BRANCH}" + +cat < [public-url]" >&2 + exit 1 +fi + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +REPO_DIR="${ROOT_DIR}/flatpak-repo" +HOST="$1" +REMOTE_PATH="$2" +PUBLIC_URL="${3:-}" + +if [[ ! -d "${REPO_DIR}" ]]; then + echo "Repository not found at ${REPO_DIR}. Run ./scripts/build-flatpak-repo.sh first." >&2 + exit 1 +fi + +if ! command -v rsync >/dev/null 2>&1; then + echo "rsync not found. Install it first." >&2 + exit 1 +fi + +rsync -av --delete "${REPO_DIR}/" "${HOST}:${REMOTE_PATH%/}/" + +if [[ -n "${PUBLIC_URL}" ]]; then + cat <