feat: add Flatpak packaging and private repo helpers
This commit is contained in:
@@ -21,6 +21,9 @@ env/
|
|||||||
*.egg-info/
|
*.egg-info/
|
||||||
dist/
|
dist/
|
||||||
build/
|
build/
|
||||||
|
flatpak-build/
|
||||||
|
flatpak-repo/
|
||||||
|
*.flatpak
|
||||||
|
|
||||||
# Test caches
|
# Test caches
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Not implemented yet:
|
|||||||
- [docker-compose.yml](docker-compose.yml) — local deployment
|
- [docker-compose.yml](docker-compose.yml) — local deployment
|
||||||
- [requirements.txt](requirements.txt) — Python dependencies
|
- [requirements.txt](requirements.txt) — Python dependencies
|
||||||
- [client.py](client.py) — minimal CLI client
|
- [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/main.py](app/main.py) — API routes
|
||||||
- [app/models.py](app/models.py) — SQLAlchemy model
|
- [app/models.py](app/models.py) — SQLAlchemy model
|
||||||
- [app/schemas.py](app/schemas.py) — API response schemas
|
- [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
|
## Environment configuration
|
||||||
|
|
||||||
Current environment variables used by the API container:
|
Current environment variables used by the API container:
|
||||||
@@ -257,7 +307,7 @@ Use a custom Bottles directory:
|
|||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
`python gtk_client.py`
|
`python cellar-gtk.py`
|
||||||
|
|
||||||
The GUI lets you:
|
The GUI lets you:
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -42,6 +42,7 @@ gi.require_version("Adw", "1")
|
|||||||
from gi.repository import Adw, Gdk, GLib, Gtk # type: ignore[import-not-found]
|
from gi.repository import Adw, Gdk, GLib, Gtk # type: ignore[import-not-found]
|
||||||
|
|
||||||
# ── Constants ───────────────────────────────────────────────────
|
# ── Constants ───────────────────────────────────────────────────
|
||||||
|
APP_ID = "net.enne2.Cellar"
|
||||||
CONFIG_PATH = Path.home() / ".config" / "cellar" / "gtk_client.json"
|
CONFIG_PATH = Path.home() / ".config" / "cellar" / "gtk_client.json"
|
||||||
|
|
||||||
APP_CSS = """\
|
APP_CSS = """\
|
||||||
@@ -448,6 +449,7 @@ class CellarWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
def __init__(self, app: Adw.Application):
|
def __init__(self, app: Adw.Application):
|
||||||
super().__init__(application=app, title="Cellar")
|
super().__init__(application=app, title="Cellar")
|
||||||
|
self.set_icon_name(APP_ID)
|
||||||
self.config = load_config()
|
self.config = load_config()
|
||||||
self.settings_window: SettingsWindow | None = None
|
self.settings_window: SettingsWindow | None = None
|
||||||
self.rows: list[ArchiveRow] = []
|
self.rows: list[ArchiveRow] = []
|
||||||
@@ -824,7 +826,7 @@ class CellarWindow(Adw.ApplicationWindow):
|
|||||||
# ── Application ─────────────────────────────────────────────────
|
# ── Application ─────────────────────────────────────────────────
|
||||||
class CellarApplication(Adw.Application):
|
class CellarApplication(Adw.Application):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(application_id="com.cellar.gtkclient")
|
super().__init__(application_id=APP_ID)
|
||||||
|
|
||||||
def do_activate(self) -> None:
|
def do_activate(self) -> None:
|
||||||
_load_css()
|
_load_css()
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
export PYTHONPATH="/app/share/cellar${PYTHONPATH:+:$PYTHONPATH}"
|
||||||
|
exec python3 /app/share/cellar/cellar-gtk.py "$@"
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>net.enne2.Cellar</id>
|
||||||
|
<name>Cellar</name>
|
||||||
|
<summary>Browse and restore Bottle archives from a self-hosted server</summary>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>LicenseRef-proprietary</project_license>
|
||||||
|
<developer id="net.enne2">
|
||||||
|
<name>Matteo Benedetto</name>
|
||||||
|
</developer>
|
||||||
|
<launchable type="desktop-id">net.enne2.Cellar.desktop</launchable>
|
||||||
|
<url type="homepage">https://git.enne2.net/enne2/cellar</url>
|
||||||
|
<url type="bugtracker">https://git.enne2.net/enne2/cellar/issues</url>
|
||||||
|
<provides>
|
||||||
|
<binary>cellar</binary>
|
||||||
|
</provides>
|
||||||
|
<description>
|
||||||
|
<p>Cellar is a small GTK application for browsing and restoring archives stored on a self-hosted Bottle archive server.</p>
|
||||||
|
<p>It can refresh the remote catalog, search archives locally, and reinstall archived Bottles environments with progress feedback.</p>
|
||||||
|
</description>
|
||||||
|
<categories>
|
||||||
|
<category>Utility</category>
|
||||||
|
<category>Archiving</category>
|
||||||
|
</categories>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<caption>Cellar application artwork</caption>
|
||||||
|
<image type="source" width="1024" height="1024">https://git.enne2.net/enne2/cellar/raw/branch/master/cellar.jpg</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<branding>
|
||||||
|
<color type="primary" scheme_preference="light">#5b0f11</color>
|
||||||
|
<color type="primary" scheme_preference="dark">#d8b27d</color>
|
||||||
|
</branding>
|
||||||
|
<content_rating type="oars-1.1" />
|
||||||
|
<releases>
|
||||||
|
<release version="1.0.0" date="2026-03-08" />
|
||||||
|
</releases>
|
||||||
|
</component>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 739 KiB |
@@ -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: .
|
||||||
Executable
+45
@@ -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 <<EOF
|
||||||
|
|
||||||
|
Flatpak repository ready:
|
||||||
|
${REPO_DIR}
|
||||||
|
|
||||||
|
Flatpak bundle ready:
|
||||||
|
${BUNDLE_PATH}
|
||||||
|
|
||||||
|
Example private remote setup:
|
||||||
|
flatpak remote-add --if-not-exists --user --no-gpg-verify brain-local http://brain.local/flatpak/cellar
|
||||||
|
flatpak install --user brain-local ${APP_ID}
|
||||||
|
EOF
|
||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -lt 2 || $# -gt 3 ]]; then
|
||||||
|
echo "Usage: $0 <host> <remote-path> [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 <<EOF
|
||||||
|
|
||||||
|
Published to ${HOST}:${REMOTE_PATH}
|
||||||
|
|
||||||
|
Clients can add the remote with:
|
||||||
|
flatpak remote-add --if-not-exists --user --no-gpg-verify brain-local ${PUBLIC_URL}
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo "Published to ${HOST}:${REMOTE_PATH}"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user