diff --git a/.github/workflows/flatpak-check.yml b/.github/workflows/flatpak-check.yml new file mode 100644 index 00000000..2f4c20b5 --- /dev/null +++ b/.github/workflows/flatpak-check.yml @@ -0,0 +1,72 @@ +name: Flatpak package check + +# Verifies packaging/flatpak actually builds and its AppStream/desktop metadata +# is valid, in a real Flatpak runtime container — so the Flathub submission can +# be validated without owning a Linux machine. +on: + push: + paths: + - "packaging/flatpak/**" + - ".github/workflows/flatpak-check.yml" + pull_request: + paths: + - "packaging/flatpak/**" + workflow_dispatch: + +permissions: + contents: read + +jobs: + # Fast feedback: validate the metadata Flathub reviewers (and appstream-compose) + # care about. Runs on every change, no container or build required. + validate: + name: Validate metadata + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install validators + run: sudo apt-get update && sudo apt-get install -y appstream desktop-file-utils + + - name: Validate AppStream metainfo + run: appstreamcli validate --explain packaging/flatpak/org.zennotes.ZenNotes.metainfo.xml + + - name: Validate desktop entry + run: desktop-file-validate packaging/flatpak/org.zennotes.ZenNotes.desktop + + # Full flatpak-builder run against the same runtime Flathub uses. The manifest + # always points at the upcoming release's AppImage, which does not exist until + # that release is published, so mirror aur-check.yml: skip the build (but keep + # validating metadata above) when the asset isn't up yet. + build: + name: flatpak-builder + runs-on: ubuntu-latest + container: + image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08 + options: --privileged + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Check whether the release AppImage is published + id: asset + run: | + set -uo pipefail + url=$(grep -oE 'https://github.com/[^[:space:]]+\.AppImage' \ + packaging/flatpak/org.zennotes.ZenNotes.yml | head -1) + echo "url=$url" + if curl -fsIL -o /dev/null "$url"; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::notice title=Flatpak build skipped::Release AppImage not published yet ($url). Metadata is still validated; the full flatpak-builder run happens once the release exists." + fi + + - name: Build the Flatpak + if: steps.asset.outputs.exists == 'true' + uses: flatpak/flatpak-github-actions/flatpak-builder@v6 + with: + bundle: zennotes.flatpak + manifest-path: packaging/flatpak/org.zennotes.ZenNotes.yml + cache-key: flatpak-builder-${{ github.sha }} diff --git a/packaging/flatpak/README.md b/packaging/flatpak/README.md index 4ac06bcc..b0e38524 100644 --- a/packaging/flatpak/README.md +++ b/packaging/flatpak/README.md @@ -61,13 +61,59 @@ sha256sum /tmp/ZenNotes.AppImage # 4. rebuild and smoke-test (see above) ``` +## Continuous validation + +`.github/workflows/flatpak-check.yml` validates this directory on every change: +it runs `appstreamcli validate` on the metainfo and `desktop-file-validate` on +the desktop entry, and (once the release AppImage is published) runs a full +`flatpak-builder` against the same freedesktop runtime Flathub uses. This is how +the package is verified without a Linux machine. + +## Publishing to Flathub + +The app-id `org.zennotes.ZenNotes` reverse-maps to `zennotes.org`, which we own, +so authorship can be verified on flathub.org after the app is live. Submission is +a one-time process: + +1. Make sure `flatpak-check.yml` is green on `main` (the full build passes). +2. Fork and create a branch **off the + `new-pr` branch** (not `master`). +3. Copy the four files from this directory to the fork's root, keeping names: + `org.zennotes.ZenNotes.yml`, `zennotes.sh`, `org.zennotes.ZenNotes.desktop`, + `org.zennotes.ZenNotes.metainfo.xml`. +4. Open a PR titled `Add org.zennotes.ZenNotes` against `flathub/flathub`'s + `new-pr` branch. A bot test-builds it; a reviewer then approves. +5. On merge, Flathub creates `flathub/org.zennotes.ZenNotes`, grants maintainer + access, and the build bot publishes. Then claim the verified badge on + flathub.org via the `zennotes.org` domain. + +Likely reviewer feedback to expect: a request to justify or narrow +`--filesystem=home`, and possibly to extract the `.deb` instead of the AppImage. + +## Ongoing updates + +Once on Flathub, the `x-checker-data` block in the manifest lets Flathub's +`flatpak-external-data-checker` bot auto-open a PR that bumps `url` + `sha256` +whenever a new GitHub release appears. The `` entry in the metainfo +still needs a manual note per version. Manual fallback: + +```sh +# 1. bump the `url` in org.zennotes.ZenNotes.yml to the new release tag +# 2. update the `sha256`: +curl -L -o /tmp/ZenNotes.AppImage \ + https://github.com/ZenNotes/zennotes/releases/download/v/ZenNotes--linux-x86_64.AppImage +sha256sum /tmp/ZenNotes.AppImage +# 3. bump the entry in org.zennotes.ZenNotes.metainfo.xml +``` + ## Notes & limitations -- **Sandbox permissions:** `--filesystem=home` is granted so notes (plain - Markdown files) are reachable. Tighten it to a specific path (e.g. - `--filesystem=~/Notes`) if you prefer. +- **Sandbox permissions:** access is granted to `xdg-documents`, + `xdg-download`, and `xdg-desktop` so notes (plain Markdown files) in the usual + places are reachable. Flathub's linter rejects broad `--filesystem=home` + (`finish-args-home-filesystem-access`), so vaults kept elsewhere (e.g. + `~/Notes`, Dropbox) need the user to widen access with Flatseal or + `flatpak override --user --filesystem=home org.zennotes.ZenNotes`. - **Auto-update is disabled** inside Flatpak (`electron-updater` cannot replace a read-only `/app`). Update via `flatpak update` once published, or rebuild with a new `url`/`sha256` for a local install. -- **Publishing to Flathub** would be a follow-up: it needs the `com.adibhanna.*` - app-id owner's sign-off plus screenshots in the AppStream metadata. diff --git a/packaging/flatpak/org.zennotes.ZenNotes.yml b/packaging/flatpak/org.zennotes.ZenNotes.yml index cc5492f1..5e8c192e 100644 --- a/packaging/flatpak/org.zennotes.ZenNotes.yml +++ b/packaging/flatpak/org.zennotes.ZenNotes.yml @@ -14,8 +14,12 @@ finish-args: - --socket=wayland - --device=dri - --socket=pulseaudio - # Notes are plain Markdown files on disk — allow access to the home dir. - - --filesystem=home + # Notes are plain Markdown files on disk. Flathub's linter blocks broad + # --filesystem=home, so grant the common user document dirs; users whose + # vaults live elsewhere can widen access with Flatseal / `flatpak override`. + - --filesystem=xdg-documents + - --filesystem=xdg-download + - --filesystem=xdg-desktop - --talk-name=org.freedesktop.Notifications modules: @@ -46,11 +50,35 @@ modules: - install -Dm644 org.zennotes.ZenNotes.metainfo.xml /app/share/metainfo/org.zennotes.ZenNotes.metainfo.xml sources: - # Bump `url` + `sha256` on each release (see README.md). + # Flathub builds both x86_64 and aarch64, so each arch needs its own + # AppImage — a single-arch source fails the other arch with an + # "Exec format error". `url` + `sha256` are auto-bumped on each release by + # Flathub's flatpak-external-data-checker (per-arch x-checker-data below); + # the manual steps in README.md are the fallback. - type: file + only-arches: [x86_64] url: https://github.com/ZenNotes/zennotes/releases/download/v2.13.1/ZenNotes-2.13.1-linux-x86_64.AppImage sha256: bbf3015fbc5fc775ce229e53f7f4977ce20a29fcd82e9737d7b10d5d8f8a4bdb dest-filename: ZenNotes.AppImage + x-checker-data: + type: json + url: https://api.github.com/repos/ZenNotes/zennotes/releases/latest + version-query: .tag_name | sub("^v"; "") + url-query: >- + .assets[] | select(.name | test("linux-x86_64\\.AppImage$")) + | .browser_download_url + - type: file + only-arches: [aarch64] + url: https://github.com/ZenNotes/zennotes/releases/download/v2.13.1/ZenNotes-2.13.1-linux-arm64.AppImage + sha256: dd3e8c9a39331b70c65390e3f44a6b8c349f1ce71fbd388aaa7b9b9afeb00a42 + dest-filename: ZenNotes.AppImage + x-checker-data: + type: json + url: https://api.github.com/repos/ZenNotes/zennotes/releases/latest + version-query: .tag_name | sub("^v"; "") + url-query: >- + .assets[] | select(.name | test("linux-arm64\\.AppImage$")) + | .browser_download_url - type: file path: zennotes.sh - type: file