Flatpak and Flathub Packaging
This directory contains the upstream packaging files for the Flathub-style Goose Flatpak.
Files in this workflow:
io.github.block.Goose.yaml:flatpak-buildermanifest used for Flathub submission and local validationflatpak/io.github.block.Goose.desktop: desktop entry installed by the manifestflatpak/io.github.block.Goose.metainfo.xml: AppStream metadataflatpak/goose-desktop.sh: launcher wrapper used inside the sandboxflatpak/cargo-sources.json: offline Cargo dependency sources generated fromCargo.lockflatpak/generated-sources.json: offline pnpm dependency sources generated fromui/pnpm-lock.yamlflatpak/forge-post-package.js: local hook used duringelectron-forge packageto copy the packaged app into/app/lib/goose
Important distinction
This repo currently has two different Flatpak-related build paths.
- The long-standing GitHub Actions release workflow builds a
.flatpakartifact with Electron Forge fromui/desktop/forge.config.ts. - The files in this directory plus
io.github.block.Goose.yamlare the separateflatpak-builderinputs intended for Flathub submission.
This README is about the second workflow.
What the manifest does
The manifest in io.github.block.Goose.yaml does not reuse the Electron Forge Flatpak maker.
Instead it:
- Builds
goose-serverfrom source inside the Flatpak SDK. - Copies the resulting
goosedbinary intoui/desktop/src/bin/goosed. - Bootstraps a pinned
pnpm@10.30.3frompnpm-10.30.3.tgzand exposes realpnpmandpnpxshims onPATH. - Installs the
ui/pnpm workspace offline fromflatpak/generated-sources.json. - Patches
ui/desktop/forge.config.tsduring the build to point Electron Forge at the staged offline Electron ZIP cache and add apostPackagehook. - Runs
electron-forge package --platform=linuxinside the Flatpak build sandbox. - Copies the packaged app into
/app/lib/goosefrom Forge's actualoutputPaths. - Installs the desktop file, metainfo, icons, and wrapper script.
The manifest builds from the Goose release tarball, not from the working tree. Because that tarball does not include the repo's flatpak/ directory, the local packaging files in flatpak/ are added as explicit type: file sources in io.github.block.Goose.yaml.
Runtime behavior also differs from the Electron Forge .flatpak artifact:
- the Flathub manifest grants
--filesystem=home - it grants
--talk-name=org.freedesktop.Flatpak - Goose detects
/.flatpak-infoand disables its built-in updater in Flatpak installs - the wrapper uses
zypak-wrapperto launch Electron in the sandbox
Local validation
Baseline host tools
The most portable local path is to use host flatpak and flatpak-builder directly.
Required tools:
flatpakflatpak-builderappstreamclidesktop-file-validate
If you build with --user, you need the user-scoped Flathub remote configured:
flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo
On this host, flatpak-builder also required disabling rofiles FUSE because rofiles-fuse failed with:
fusermount3: failed to access mountpoint ... Permission denied
Error: Failure spawning rofiles-fuse, exit_status: 1024
The local workaround was --disable-rofiles-fuse.
Build the manifest locally:
flatpak-builder \
--user \
--install \
--install-deps-from=flathub \
--force-clean \
--disable-rofiles-fuse \
builddir \
io.github.block.Goose.yaml
Run the installed app:
flatpak run io.github.block.Goose
If you want an exported local repo for linting or bundle inspection:
flatpak-builder \
--user \
--install-deps-from=flathub \
--force-clean \
--disable-rofiles-fuse \
--repo=repo \
builddir \
io.github.block.Goose.yaml
Using org.flatpak.Builder
If you already use the Flathub helper app, the equivalent commands also work through org.flatpak.Builder:
flatpak install -y flathub org.flatpak.Builder
flatpak run --command=flathub-build org.flatpak.Builder --install io.github.block.Goose.yaml
flatpak run io.github.block.Goose
Validation commands
Validate AppStream and desktop metadata:
appstreamcli validate flatpak/io.github.block.Goose.metainfo.xml
desktop-file-validate flatpak/io.github.block.Goose.desktop
If you have org.flatpak.Builder installed, run the Flathub linter too:
flatpak run --command=flatpak-builder-lint org.flatpak.Builder manifest io.github.block.Goose.yaml
flatpak run --command=flatpak-builder-lint org.flatpak.Builder repo repo
The current manifest is expected to trigger these lints:
finish-args-home-filesystem-accessfinish-args-flatpak-spawn-accessappid-url-not-reachable
Those are expected for the current packaging model:
--filesystem=homeis intentional because Goose works against real project directories--talk-name=org.freedesktop.Flatpakis intentional because Goose needsflatpak-spawn --hostfor host-side shell executionappid-url-not-reachablecurrently points at the legacy app ID owner URL and should be fixed before or during Flathub review
Proven local result
The following command was run locally and completed successfully:
flatpak-builder \
--user \
--install \
--install-deps-from=flathub \
--force-clean \
--disable-rofiles-fuse \
builddir \
io.github.block.Goose.yaml
That run:
- built
goose-server - installed the pnpm workspace fully offline
- packaged the Electron app fully offline
- exported and installed
app/io.github.block.Goose/x86_64/master
When to regenerate generated sources
Regenerate the dependency manifests whenever any of these change:
Cargo.lockui/pnpm-lock.yaml- manifest build steps that change how dependencies are resolved offline
Do not hand-edit flatpak/cargo-sources.json or flatpak/generated-sources.json.
Regenerating Cargo sources
Use the upstream flatpak-builder-tools cargo generator.
One reproducible approach:
git clone https://github.com/flatpak/flatpak-builder-tools.git /tmp/flatpak-builder-tools
python3 -m venv /tmp/flatpak-builder-tools-cargo-venv
. /tmp/flatpak-builder-tools-cargo-venv/bin/activate
python3 -m pip install -r /tmp/flatpak-builder-tools/cargo/requirements.txt
python3 /tmp/flatpak-builder-tools/cargo/flatpak-cargo-generator.py \
--git-tarballs \
Cargo.lock \
-o flatpak/cargo-sources.json
If the generator instructions change upstream, prefer upstream over this README.
Regenerating pnpm sources
Use the upstream node generator from flatpak-builder-tools.
One reproducible approach with pipx:
pipx install git+https://github.com/flatpak/flatpak-builder-tools.git#subdirectory=node
flatpak-node-generator pnpm ui/pnpm-lock.yaml -o flatpak/generated-sources.json
If pipx is not available, install the node generator from the same upstream repo in another isolated Python environment.
Updating the manifest for a new Goose release
At minimum, update:
io.github.block.Goose.yamlflatpak/io.github.block.Goose.metainfo.xmlflatpak/cargo-sources.jsonifCargo.lockchangedflatpak/generated-sources.jsonifui/pnpm-lock.yamlchanged
Checklist:
- Update the source tarball URL and SHA256 in
io.github.block.Goose.yaml. - Update release entries in
flatpak/io.github.block.Goose.metainfo.xml. - Regenerate offline dependency manifests if locks changed.
- Rebuild locally with
flatpak-builder. - Re-run
appstreamcli,desktop-file-validate, and the Flathub linter.
Flathub submission flow
These files are intended to be copied into a new app directory in flathub/flathub.
Typical flow:
- Merge the upstream packaging files into Goose.
- Cut a stable Goose release that matches the tarball referenced by
io.github.block.Goose.yaml. - Fork
flathub/flathub. - Create a branch from
new-pr. - Create
io.github.block.Goose/in that branch. - Copy
io.github.block.Goose.yaml,flatpak/cargo-sources.json, andflatpak/generated-sources.jsoninto that directory. - Open a PR to
flathub/flathub:new-prtitledAdd io.github.block.Goose.
Expect review questions about permissions.
Review notes for Flathub
Goose is a developer tool, so the current manifest intentionally requests broad access compared with a typical consumer desktop app.
The two permissions reviewers will ask about first are:
--filesystem=home--talk-name=org.freedesktop.Flatpak
Current justification:
- Goose needs direct access to local project directories selected by the user.
- Goose uses
flatpak-spawn --hostto execute host-side shell commands when running sandboxed.
If the app security model changes later, revisit these permissions instead of cargo-culting them forward.