Skip to content

feat(linux): package OpenLogi as a Flatpak with a signed repository - #767

Open
AalmanSadath wants to merge 3 commits into
AprilNEA:masterfrom
AalmanSadath:feat/linux-flatpak
Open

feat(linux): package OpenLogi as a Flatpak with a signed repository#767
AalmanSadath wants to merge 3 commits into
AprilNEA:masterfrom
AalmanSadath:feat/linux-flatpak

Conversation

@AalmanSadath

Copy link
Copy Markdown

Summary

Linux is the only platform that cannot update itself. The .deb, .rpm and .pkg.tar.zst packages ship with no repository behind them, and the in-app updater has no Linux artifact to offer: xtask release latest-json classifies only .dmg, .msi and .zip while the updater asks for tar.gz on Linux. Every Linux upgrade today is a manual download, which is what #605 runs into.

A Flatpak remote closes that. Install once, and flatpak update, or GNOME Software and KDE Discover in the background, carries the user forward. It also installs on atomic distributions, where layering an rpm is the current answer (#681).

Publishing is gated on a FLATPAK_GPG_PRIVATE_KEY secret. Without it the build still runs and uploads artifacts, so merging this changes nothing until you decide to turn publishing on and supply a key. Hosting and signing are yours to decide: the repository would live on this project's Pages.

Changes

packaging/linux/flatpak/

  • org.openlogi.OpenLogi.yml, the manifest. Source is type: dir over the checkout, so a release build is exactly the tagged tree and a local build is exactly the working tree.
  • .desktop and AppStream metadata. Flatpak requires both to be named for the application ID, which the packaged .desktop is not, and there is no AppStream data in the tree, which software centres need to list the app.
  • index.html.in, the landing page for the published repository.

Only the build environment lives in the manifest, because that part is genuinely packaging's problem:

  • rustup rather than org.freedesktop.Sdk.Extension.rust-stable. The workspace sets rust-version to current stable and the extension trails it by weeks (1.97.1 against a workspace asking for 1.98), which cargo refuses outright. rustup installs what rust-toolchain.toml names.
  • llvm20, since openlogi-camera pulls v4l2-sys-mit, whose build script runs bindgen and dlopens libclang. The base SDK ships none.
  • Icons rescaled, because flatpak build-export rejects the 1024x1024 source ("Image too large. Max. size 512x512"). The SDK has no ImageMagick; ffmpeg is present and scales PNG fine.

.github/workflows/flatpak.yml

Builds x86_64 and aarch64 on release: published, merges them into one OSTree repository, signs it, deploys to Pages. workflow_dispatch is build-only unless publishing is explicitly requested. Signing covers the commits as well as the summary: clients verify the commit they pull, and a summary-only signature fails every install with "GPG verification enabled, but no signatures found". The repository is rebuilt per release rather than appended to, since Pages caps a site at 1 GB.

docs/INSTALL-linux.md

A Flatpak section covering install, updates, and the host setup below.

.gitignore

flatpak-builder output and scratch state.

Testing

  • cargo xtask ci: 7 passed, 0 failed, 2 skipped. Not run: tests (macos) (wrong host OS), cargo-deny (not installed locally).
  • Full local flatpak-builder run on x86_64: completed and exported, 75.4 MB. rustup installed 1.98.0 inside the sandbox and the workspace compiled clean.
  • Bundle contents verified with ostree ls against the exported commit: all four binaries in /files/bin, 70-openlogi.rules under /files/share/openlogi/udev/, four icon sizes, desktop entry, AppStream metadata, both licenses.
  • desktop-file-validate clean. appstreamcli validate --no-net successful. Both YAML files parse.
  • The workflow itself ran green on a fork, dispatch path, both architectures building successfully, with the publish job correctly skipped.
  • Not tested: the publish job (needs a signing key and Pages), and the release: published trigger. The publish job is unchanged from one I have running in a separate repository of my own, where the signing, merge and Pages deploy work end to end, but it has not run in this repository's context.

Also worth stating plainly, and documented in docs/INSTALL-linux.md: a Flatpak cannot write to /etc, so the udev rules must be installed on the host or no devices are detected at all. The app opens to an empty device list, which reads as a broken package rather than a missing setup step. The rules ship inside the app so that step needs no checkout.

Fixes #371

Linux is the only platform that cannot update itself. The .deb/.rpm/.pkg.tar.zst
packages ship with no repository behind them, and the in-app updater has no Linux
artifact to offer: `xtask release latest-json` classifies only .dmg/.msi/.zip
while the updater asks for tar.gz on Linux. So every Linux upgrade today is a
manual download, which is what AprilNEA#605 runs into.

A Flatpak remote closes that. Install once, and `flatpak update` — or GNOME
Software and KDE Discover in the background — carries the user forward. It also
installs on atomic distributions, where layering an rpm is the current answer
(AprilNEA#681).

Nothing about the application changes. Only the build environment lives in the
manifest, because that part is genuinely packaging's problem:

- rustup rather than org.freedesktop.Sdk.Extension.rust-stable. The workspace
  sets `rust-version` to current stable and the extension trails it by weeks
  (1.97.1 against a workspace asking for 1.98), which cargo refuses outright.
  rustup installs what rust-toolchain.toml names.
- llvm20, since openlogi-camera pulls v4l2-sys-mit, whose build script runs
  bindgen and dlopens libclang; the base SDK ships none.
- A .desktop file and AppStream metadata, because Flatpak requires both to be
  named for the application ID and there is no AppStream data in the tree.
- Icons rescaled: `flatpak build-export` rejects the 1024x1024 source.

The workflow builds both architectures on a published release, merges them into
one OSTree repository, signs it, and deploys to Pages. Signing covers the commits
as well as the summary, because clients verify the commit they pull and a
summary-only signature fails every install with "GPG verification enabled, but no
signatures found".

Publishing is gated on FLATPAK_GPG_PRIVATE_KEY. Without the secret the build
still runs and uploads artifacts, so this is inert rather than red until someone
decides to turn it on and supply a key.

One caveat, documented in docs/INSTALL-linux.md: a Flatpak cannot write to /etc,
so the udev rules must be installed on the host or no devices are detected at
all. The rules ship inside the app so no checkout is needed for that step.
@AalmanSadath
AalmanSadath requested a review from AprilNEA as a code owner August 21, 2026 18:41
@davidbudnick davidbudnick added type: feature New feature request platform: linux Linux-specific issue labels Aug 21, 2026
@greptile-apps

greptile-apps Bot commented Aug 21, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a self-updating Linux Flatpak distribution backed by a signed GitHub Pages OSTree repository.

  • Builds x86_64 and aarch64 Flatpak repositories from a shared resolved source ref.
  • Merges and signs architecture-specific commits before deployment.
  • Adds Flatpak desktop metadata, AppStream metadata, installation guidance, and local build documentation.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the source ref and advertised version are now resolved together, and the previously raised first-party action-pinning concern was explicitly settled.

Important Files Changed

Filename Overview
.github/workflows/flatpak.yml Adds the multi-architecture build, signing, and Pages publication workflow; the previously reported version/ref mismatch is fixed.
packaging/linux/flatpak/org.openlogi.OpenLogi.yml Defines the Flatpak runtime permissions, build environment, binaries, desktop integration, icons, udev rules, and licenses.
packaging/linux/flatpak/org.openlogi.OpenLogi.metainfo.xml Adds AppStream metadata for software-center discovery and release presentation.
docs/INSTALL-linux.md Documents Flatpak installation, updates, and required host-side udev setup.
.claude/rules/ci.md Documents that Flatpak builds are outside the PR workflow and provides the local reproduction command.

Sequence Diagram

sequenceDiagram
  participant Trigger as Release or manual dispatch
  participant Resolve as Resolve job
  participant Build as Architecture builds
  participant Publish as Publish job
  participant Pages as GitHub Pages
  Trigger->>Resolve: Tag, requested version, or selected ref
  Resolve-->>Build: Paired source ref and version
  Build->>Build: Build x86_64 and aarch64 Flatpak repos
  Build-->>Publish: Upload architecture repositories
  Publish->>Publish: Merge OSTree refs
  Publish->>Publish: Sign commits and repository summary
  Publish->>Pages: Deploy repository and descriptors
Loading

Reviews (3): Last reviewed commit: "docs(ci): record why the publish job kee..." | Re-trigger Greptile

Comment thread .github/workflows/flatpak.yml
Comment thread .github/workflows/flatpak.yml
A manual dispatch took `version` as a label only: `resolve` fed it to the
AppStream stamp and the landing page, while both jobs checked out whatever ref
the dispatch ran on. Dispatching v0.7.0 from master therefore built master and
published it, signed, as 0.7.0 — a repository asserting a release identity its
binaries were never built from.

`resolve` now emits the ref alongside the version and the two move together: a
release builds its tag, a dispatch naming a version builds that tag, and a
dispatch naming none builds the ref it ran on and labels it with the workspace
version, which is the honest name for that tree.

Also records the workflow in .claude/rules/ci.md, including that it never runs
on a PR, so a change to the manifest is known to arrive unverified unless
someone builds it locally.
The job holds Pages and OIDC permissions and imports the signing key, so the
mutable tags read like an oversight to anyone auditing it. They are not.

Every action in the job is first-party `actions/*`, on the same tags release.yml
and windows-sign-dryrun.yml already use while handling Apple codesign certs and
Azure signing credentials. Pinning this one workflow would make it the only
pinned one in the tree, and with no Dependabot or Renovate config to bump them,
hand-pinned SHAs go stale unnoticed — trading a hypothetical risk for a certain
one. The comment says to follow a repo-wide policy here if one is adopted.
@AalmanSadath

AalmanSadath commented Aug 21, 2026

Copy link
Copy Markdown
Author

Both review findings are resolved.

Version/ref mismatch (P1). Fixed in 754a2e5. resolve now emits the ref alongside the version and the two move together: a release builds its tag, a dispatch naming a version builds that tag, and a dispatch naming none builds the ref it ran on and labels it with the workspace version. Previously the version was a label only, so a dispatch could have published one tree signed under another tree's release identity.

Action pinning (P2). Settled as a non-issue for this workflow: every action in the publish job is first-party actions/*, on the same mutable tags release.yml and windows-sign-dryrun.yml already use while handling Apple codesign certs and Azure signing credentials. Pinning here alone would make it the only pinned workflow in the tree, and with no Dependabot or Renovate config the SHAs would go stale unnoticed. 12ec9e1 records that reasoning next to the job so it does not have to be rediscovered, and says to follow a repo-wide pinning policy here if one is ever adopted. Happy to pin if you would prefer it, or to do the repo-wide version as a separate PR.

CI status. Green on 12ec9e1, including the macOS test matrix. Locally all three commits also pass cargo xtask ci: 8 passed, 0 failed, 1 skipped, the skip being tests (macos) for want of a Mac.

@davidbudnick davidbudnick added this to the v0.7.5 milestone Aug 21, 2026
@AalmanSadath

Copy link
Copy Markdown
Author

Thanks for the approval. One thing to flag before v0.7.5 ships: publishing is gated on a FLATPAK_GPG_PRIVATE_KEY repository secret. Without it the workflow still builds both architectures and uploads artifacts, but the publish job skips and no repository is deployed, so the remote that users would add based on the docs wouldn't exist yet.

Whoever sets the key will also need to set GitHub Pages to deploy from GitHub Actions. Happy to write up the key generation steps if that's useful.

@davidbudnick

Copy link
Copy Markdown
Collaborator

FLATPAK_GPG_PRIVATE_KEY

Great call out @AprilNEA would have the update the ENV variable before going live :)

@AalmanSadath

Copy link
Copy Markdown
Author

Just to clarify where it goes, it's a repository secret rather than an env variable, under
Settings -> Secrets and variables -> Actions,
since the workflow reads it as secrets.FLATPAK_GPG_PRIVATE_KEY.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: linux Linux-specific issue type: feature New feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Flatpak distribution / Publish on Flathub

2 participants