Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .github/workflows/flatpak-check.yml
Original file line number Diff line number Diff line change
@@ -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 }}
56 changes: 51 additions & 5 deletions packaging/flatpak/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/flathub/flathub> 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 `<release>` 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<version>/ZenNotes-<version>-linux-x86_64.AppImage
sha256sum /tmp/ZenNotes.AppImage
# 3. bump the <release> 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.
34 changes: 31 additions & 3 deletions packaging/flatpak/org.zennotes.ZenNotes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
Loading