Files
o3de-flatpak/README.md
T
pc-heini a2a52f83ea docs: add DESIGN.md engineering notes and link from README
Captures the why behind the build: the read-only /app core tension, the
packaging architecture (no flatpak-builder/bwrap, Sdk-not-Platform, OSTree on
pages), the full read-only-/app saga (X11, Python sdist hook, wrapper venv
bootstrap, SNAP/SNAP_BUILD engine path, the unrelocatable user folder, the
icon), the CI flow, and a maintenance/debugging guide for future O3DE releases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 03:52:36 +02:00

195 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# O3DE Flatpak
Repackage the official [Open 3D Engine](https://o3de.org/) Linux release as a
**Flatpak** so it can be installed on any distribution — not only Debian/Ubuntu.
A Gitea Actions workflow checks daily for a new O3DE release, builds the Flatpak,
and publishes it as a static [OSTree](https://ostreedev.github.io/ostree/) Flatpak
repository on the `pages` branch of this repo. You add that as a Flatpak remote and
`install` / `update` like any other app.
> **Status:** community / unofficial. O3DE is a large application (~1518 GB
> installed); building and hosting it is heavy. Treat this as best-effort.
---
## Installing (end users)
```sh
# Flathub provides the runtime O3DE needs
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
# Add this repo (replace <owner> with the Gitea account that owns the repo)
flatpak remote-add --if-not-exists o3de \
https://gitea.pc-heini.de/<owner>/o3de-flatpak/raw/branch/pages/o3de.flatpakrepo
flatpak install o3de org.o3de.O3DE
flatpak run org.o3de.O3DE
```
Later updates:
```sh
flatpak update org.o3de.O3DE
```
> The repo is currently **unsigned** (no GPG). Flatpak will add it with GPG
> verification disabled. See *Signing* below to harden this.
---
## How it works
| File | Purpose |
| --- | --- |
| `scripts/make-flatpak.sh` | **The build.** Unpacks the official `o3de_*.deb` into `/app`, bakes in Python, and exports an OSTree repo using `flatpak build-init`/`build-finish`/`build-export` — no `flatpak-builder`, no bubblewrap, no privileged container. |
| `o3de-wrapper.sh` | Entry point. Finds the versioned `o3de` Project Manager binary inside the sandbox and sets `LD_LIBRARY_PATH`. |
| `org.o3de.O3DE.desktop` | Desktop entry under the Flatpak app-id. |
| `org.o3de.O3DE.metainfo.xml` | AppStream metadata (version stamped at build time). |
| `scripts/get-latest-version.sh` | Resolves the latest `.deb` URL, version, and SHA-256 from o3debinaries.org. |
| `scripts/build.sh` | Download + build + test the Flatpak locally (wraps `make-flatpak.sh`). |
| `org.o3de.O3DE.yaml` | Equivalent `flatpak-builder` manifest — kept as an **alternative** for builders that have a privileged/bwrap-capable environment. Not used by CI. |
| `.gitea/workflows/build-flatpak.yml` | CI: detect new version → build → publish to `pages` → tag `vX.Y.Z`. |
The engine ships as a Debian package at a predictable URL
(`https://o3debinaries.org/main/Latest/Linux/o3de_<ver>.deb`). The build extracts
it (`ar` + `tar`) and copies the payload into the Flatpak's `/app`. The version
directory inside the `.deb` changes every release, so the wrapper discovers the
executable at runtime rather than hard-coding a path.
> **Want the full story?** O3DE is an engine + SDK that compiles code and writes
> into its own install tree at runtime, while a Flatpak gives it a **read-only
> `/app`**. Reconciling those two facts is most of the work here.
> **[`docs/DESIGN.md`](docs/DESIGN.md)** documents every non-obvious decision, the
> read-only-`/app` problems and their fixes, and how to debug/maintain this across
> O3DE releases. Read it before changing the build.
---
## CI requirements (Gitea Actions)
The workflow targets a **self-hosted `act_runner`**. Because O3DE is large:
- **Disk:** budget **60 GB+** free. The build needs roughly 23× the installed
size (extracted payload in `build-dir` + a copy committed into the OSTree
`repo/`). The job deletes `build-dir` before publishing to cut peak usage, but
it can still be tight. If builds fail on space, that's the first thing to check.
- **No privileged container required.** The build avoids `flatpak-builder`/bwrap
and uses `flatpak build-init`/`build-finish`/`build-export`, which only touch
files and the OSTree repo. A plain unprivileged job container works.
- **Runner label:** the job uses `runs-on: ubuntu-latest`. Change it if your
runner is registered with a different label.
- **Token:** publishing force-pushes the `pages` branch and creates a `vX.Y.Z`
tag. The auto-provided `GITHUB_TOKEN` (with `contents: write`) usually suffices.
If your instance restricts it, create a Personal Access Token with repo write
access and add it as a secret named **`PUBLISH_TOKEN`** — the workflow prefers
it automatically.
Trigger it manually from the Gitea Actions UI (`workflow_dispatch`, with an
optional **force** rebuild), or let the daily `cron` run it. It only rebuilds when
the upstream version has no matching `vX.Y.Z` tag yet, so reruns are cheap no-ops.
---
## Building locally
```sh
# flatpak + curl + python3/setuptools (to pre-build the o3de sdist)
sudo apt install flatpak python3-setuptools # or your distro's equivalent
./scripts/build.sh
```
Then install from the local `repo/` and run:
```sh
flatpak remote-add --user --no-gpg-verify o3de-local repo
flatpak install --user o3de-local org.o3de.O3DE
flatpak run org.o3de.O3DE
```
---
## Signing (recommended hardening)
The first iteration publishes an unsigned repo for simplicity. To sign:
1. Generate a key: `gpg --quick-gen-key "O3DE Flatpak" default default never`
2. Export the public key and add `GPGKey=<base64>` to the generated `.flatpakrepo`.
3. Pass `--gpg-sign=<KEYID>` to both `flatpak-builder` and `build-update-repo`,
and provide the private key to CI via a secret. Until then, users add the
remote with GPG verification disabled.
---
## Caveats & things to verify
These were confirmed by inspecting the v26.05 package (`opt/O3DE/26.05/…`):
- **Layout is confirmed for now**: the package installs everything under
`/opt/O3DE/<ver>/`, with the launcher at `bin/Linux/profile/Default/o3de` and
~270 `.so` files beside it (hence the wrapper's `LD_LIBRARY_PATH`). If a future
release moves things, adjust `o3de-wrapper.sh` and the manifest.
- **Runs against `org.freedesktop.Sdk`, not `Platform`.** O3DE's package
dependencies are a *build toolchain* (clang/ninja/cmake/pkg-config) because the
engine compiles project code at runtime. Those live in the SDK. Users therefore
pull the SDK runtime (larger than Platform) on install — Flatpak does this
automatically from Flathub.
- **Python is per-user, not baked in.** O3DE needs a Python venv under `~/.o3de`
(writable), keyed to the engine path, so it can only be created at runtime — the
build can't bundle it. The Project Manager GUI does **not** create it itself, so
the wrapper (`o3de-wrapper.sh`) runs `python/get_python.sh` on launch when the
venv is missing or stale. The **first launch downloads Python and can take
several minutes with no window on screen** — that's expected; subsequent launches
are instant. (cmake, which `get_python.sh` needs, comes from the Sdk runtime.)
- **The o3de CLI install (read-only `/app`).** On first launch O3DE installs its
own `o3de` CLI from `scripts/o3de` into the per-user `~/.o3de` venv. By default
`python/get_python.sh` does an *editable* install (`pip install -e`), which
writes an `egg-info` next to the source under read-only `/app` and fails.
`get_python.sh` has a built-in immutable-install hook (the Snap path): if a
prebuilt sdist exists at `scripts/o3de/dist/o3de-1.0.0.tar.gz` it installs that
(non-editable) instead. The build pre-creates that tarball (`python3 setup.py
sdist`) while the tree is still writable, so at runtime the CLI lands in the
writable venv and nothing is written to `/app`. (The build also patches every
`cmake/LYPython.cmake` to a non-editable install, covering the separate
project-build CMake-configure path.) If a future release renames the package or
bumps its version away from `1.0.0`, the build fails fast — those are the lines
to re-check.
- **Engine-path resolution needs `SNAP`/`SNAP_BUILD`.** Because the `o3de` CLI is
installed non-editably, its package lives in the per-user venv, so O3DE's
`manifest.get_this_engine_path()` (which walks up from the package `__file__`)
would resolve into the venv and fail to find `engine.json`. O3DE has a built-in
override for immutable installs: when `SNAP` and `SNAP_BUILD` are set it returns
`$SNAP/$SNAP_BUILD`. The wrapper sets them to the engine root. (Confirmed these
vars are read *only* there — no other snap-specific behaviour is triggered.)
- **The Project Manager's own user folder is read-only (cosmetic).** With no
project open, O3DE puts its engine-level *user* folder (asset cache, metrics,
`UserSettings.xml`) under `<engine>/.../user`, i.e. read-only `/app`. You'll see
`Couldn't find a valid asset cache folder…`, `cannot write to temp file …
UserSettings.xml`, and a metrics warning at startup. These are **non-fatal**
the Project Manager runs fine; it just doesn't persist its *own* window/UI
settings between runs. The documented override
(`/O3DE/Runtime/FilePaths/SourceProjectUserPath`) does **not** help: O3DE
recomputes that path from the engine root during bootstrap and overwrites a
`--regset`. Real project data (projects you create, their caches) lives under
your home directory and is fully writable, so actual work is unaffected.
- **Launcher icon.** Shipped as `org.o3de.O3DE.png` (the `.deb` itself has only
in-editor asset icons). It's kept *inside* the app so the running window/taskbar
shows it, but removed from the *exported* set because `flatpak build-export`
validates exported icons in a bwrap sandbox that fails in an unprivileged
container. Consequence: the host menu launcher icon is generic. A privileged
runner would let us export it properly.
- **GPU / drivers:** the renderer needs working GPU access. The manifest grants
`--device=dri`/`--device=all`; on some setups you may also want the matching
GPU driver extension from Flathub.
- **Sandbox filesystem:** `--filesystem=home` lets the Project Manager create and
open projects under your home directory. Tighten or widen to taste.
- **Hosting via raw branch URLs** works because Flatpak fetches individual files
(`summary`, `config`, `objects/…`). Gitea serves these from the `pages` branch.
If you later put a real static web host in front of it, just change `Url=` in
the `.flatpakrepo`.
---
*Not affiliated with or endorsed by the Open 3D Foundation. O3DE is licensed under
Apache-2.0 / MIT.*