A Flatpak manifest sat in the repo looking entirely reasonable. It had never been built
once. Every problem below appeared within minutes of actually running flatpak-builder, and not
one of them was visible by re-reading the file. A manifest that looks right and a manifest that
builds are unrelated states. Here is what broke, in the order it broke.
Flathub build workers have no network. That is the single fact driving most of what follows. Your
package-lock.json is not enough, because nothing is allowed to go and fetch what it describes.
You pre-generate every source as JSON:
python3 -m venv /tmp/fng && /tmp/fng/bin/pip install flatpak-node-generator
/tmp/fng/bin/flatpak-node-generator npm -o generated-sources.json package-lock.json
Generate it from the lockfiles as they stand at the tag you are building, not from your
working tree. Building v1.2.3 from sources generated against uncommitted changes gives you a
build that cannot be reproduced by anyone, including you.
On Ubuntu that tool needs a venv, because PEP 668 marks the system Python as externally managed.
npm ci failed ENOTCACHED on the very first package, while the populated cache sat
in the build directory a few paths away.
NPM_CONFIG_OFFLINE=true only says "do not use the network". It does not say where to
look instead, so npm consults its default cache location, which in the sandbox is empty. Set both:
build-options:
env:
NPM_CONFIG_OFFLINE: "true"
NPM_CONFIG_CACHE: /run/build/<module-name>/flatpak-node/npm-cache
The module name in that path is the name of your manifest module. Get it wrong and you get the identical error, which makes this feel like an offline-mode bug rather than a path typo.
org.electronjs.Electron2.BaseApp supplies zypak, the wrapper that makes
Chromium's sandbox work inside Flatpak, plus supporting libraries. It does not supply an
Electron runtime. Assume it does, set ELECTRON_SKIP_BINARY_DOWNLOAD, and the build succeeds
while producing a 2.7MB app with no runtime in it. The launcher then hands a
.js file to zypak with nothing capable of executing it. A correct build here is 327MB.
Take Electron from the zip that flatpak-node-generator has already cached for you, and unzip
it directly:
unzip -q flatpak-node/cache/electron/electron-v*-linux-x64.zip -d /app/lib/myapp/electron
Do not rely on the electron npm package's postinstall to place a binary. It does not
run in this sandbox at all. After npm ci there is no dist/ and no
path.txt, so anything expecting those finds nothing.
Then the launcher has to pass two different things: the binary to zypak, and the app
directory to Electron, so package.json resolves main, the name and the
version:
#!/bin/sh
exec zypak-wrapper /app/lib/myapp/electron/electron /app/lib/myapp "$@"
Anything main.js reaches for via __dirname has to be installed explicitly. The
bundler output is not the whole app:
preload.js, which is the context bridge. If your desktop sign-in uses it, it would have had
nothing to load.package.json, because Electron reads main, name and version from it.node_modules, if main.js does a runtime
require() of anything. An auto-updater is the usual one.Flatpak refuses to export a setuid binary, so the obvious move is to drop the setuid bit and move on. That is worse than deleting it.
Electron finds the helper, decides it is misconfigured, and aborts at startup with "The SUID sandbox helper binary was found, but is not configured correctly". zypak is providing the sandbox, so the helper has no job:
rm -f /app/lib/myapp/electron/chrome-sandbox
sources, the build cannot find it.gdk-pixbuf-thumbnailer -s 512 in.png out.png.flatpak remote-ls flathub --runtime | grep Platform takes a second and stops you shipping
against a stale one.Worth the price of admission on its own. The desktop entry had no
MimeType=x-scheme-handler/myapp; line, which means the custom URL scheme was never
registered with the desktop. Single sign-on returns to the app through that scheme, so desktop SSO could not
have completed under any Linux packaging, Flatpak or otherwise. Packaging exercises paths your dev
machine never touches, because your dev machine has the scheme registered by some other means or never used
it.
flatpak run --command=/app/lib/myapp/electron/electron bypasses zypak entirely, which means it
skips the exact wrapper the whole packaging exists to set up. It can pass while the real launch path is
broken. Run the app the way a user does, and if you are headless, run it under a virtual display rather than
not at all.