Fix o3de CLI install on read-only /app via prebuilt sdist
The real first-launch installer is python/get_python.sh, not LYPython.cmake. It does `pip install -e scripts/o3de`, whose egg_info write hits read-only /app and fails ([Errno 30]). get_python.sh has a built-in immutable-install hook: if scripts/o3de/dist/o3de-1.0.0.tar.gz exists it installs that (non-editable) instead. Pre-build that tarball at Flatpak build time (python3 setup.py sdist) so the runtime CLI lands in the writable ~/.o3de venv. Verified end-to-end: tarball install -> `import o3de` resolves from site-packages. Also patch every LYPython.cmake copy (not just the first) for the separate project-build CMake-configure path, and add python3/python3-setuptools to CI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ jobs:
|
|||||||
apt-get update
|
apt-get update
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
ca-certificates curl git jq xz-utils zstd binutils tar \
|
ca-certificates curl git jq xz-utils zstd binutils tar \
|
||||||
|
python3 python3-setuptools \
|
||||||
flatpak
|
flatpak
|
||||||
|
|
||||||
# Done as a plain git clone instead of actions/checkout@v4: the bare
|
# Done as a plain git clone instead of actions/checkout@v4: the bare
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ the upstream version has no matching `vX.Y.Z` tag yet, so reruns are cheap no-op
|
|||||||
## Building locally
|
## Building locally
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# flatpak + cmake (cmake is used by O3DE's get_python.sh) + curl
|
# flatpak + curl + python3/setuptools (to pre-build the o3de sdist)
|
||||||
sudo apt install flatpak cmake # or your distro's equivalent
|
sudo apt install flatpak python3-setuptools # or your distro's equivalent
|
||||||
./scripts/build.sh
|
./scripts/build.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -131,12 +131,19 @@ These were confirmed by inspecting the v26.05 package (`opt/O3DE/26.05/…`):
|
|||||||
runtime and builds a venv under `~/.o3de` (writable), pip-installing its deps
|
runtime and builds a venv under `~/.o3de` (writable), pip-installing its deps
|
||||||
there. The build does **not** bundle Python — the venv is keyed to the user's
|
there. The build does **not** bundle Python — the venv is keyed to the user's
|
||||||
home path, so it can only be created at runtime.
|
home path, so it can only be created at runtime.
|
||||||
- **The editable-install patch.** O3DE installs its own `o3de` CLI with
|
- **The o3de CLI install (read-only `/app`).** On first launch O3DE installs its
|
||||||
`pip install -e`, which writes an `egg-info` next to the source under read-only
|
own `o3de` CLI from `scripts/o3de` into the per-user `~/.o3de` venv. By default
|
||||||
`/app` and fails. The build patches `cmake/LYPython.cmake` to drop the `-e`, so
|
`python/get_python.sh` does an *editable* install (`pip install -e`), which
|
||||||
it's a normal install (built in a temp dir, landing in the writable `~/.o3de`
|
writes an `egg-info` next to the source under read-only `/app` and fails.
|
||||||
venv). If a future O3DE release changes that pip command, the build prints a
|
`get_python.sh` has a built-in immutable-install hook (the Snap path): if a
|
||||||
warning and Python setup will fail at runtime — that's the line to re-check.
|
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.
|
||||||
- **Other runtime writes into the install tree may still fail.** Anything else
|
- **Other runtime writes into the install tree may still fail.** Anything else
|
||||||
O3DE tries to generate *inside* `/opt/O3DE/...` at runtime (e.g. certain per-gem
|
O3DE tries to generate *inside* `/opt/O3DE/...` at runtime (e.g. certain per-gem
|
||||||
Python deps, or the engine-level asset cache) hits the read-only `/app`. Project
|
Python deps, or the engine-level asset cache) hits the read-only `/app`. Project
|
||||||
|
|||||||
+18
-7
@@ -37,17 +37,28 @@ modules:
|
|||||||
# is kept for robustness in case a future release adds desktop glue there.)
|
# is kept for robustness in case a future release adds desktop glue there.)
|
||||||
- 'if [ -d data/opt ]; then mkdir -p "${FLATPAK_DEST}/opt"; cp -a data/opt/. "${FLATPAK_DEST}/opt/"; fi'
|
- 'if [ -d data/opt ]; then mkdir -p "${FLATPAK_DEST}/opt"; cp -a data/opt/. "${FLATPAK_DEST}/opt/"; fi'
|
||||||
- 'if [ -d data/usr ]; then cp -a data/usr/. "${FLATPAK_DEST}/"; fi'
|
- 'if [ -d data/usr ]; then cp -a data/usr/. "${FLATPAK_DEST}/"; fi'
|
||||||
# On first launch O3DE pip-installs its 'o3de' CLI editable ('pip install
|
# On first launch O3DE installs its 'o3de' CLI from scripts/o3de. By default
|
||||||
# -e'), which writes an egg-info into read-only /app and fails. Force a
|
# python/get_python.sh does an *editable* install ('pip install -e'), which
|
||||||
# normal install (built in a temp dir, lands in the writable ~/.o3de venv).
|
# writes an egg-info into 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. Build the tarball now, while /app is still writable.
|
||||||
- |
|
- |
|
||||||
set -e
|
set -e
|
||||||
LYPYTHON=$(find "${FLATPAK_DEST}/opt/O3DE" -path '*/cmake/LYPython.cmake' | head -n1)
|
O3DE_PKG=$(find "${FLATPAK_DEST}/opt/O3DE" -maxdepth 3 -type d -path '*/scripts/o3de' | head -n1)
|
||||||
if [ -n "$LYPYTHON" ] && grep -qF -- '-m pip install -e ' "$LYPYTHON"; then
|
if [ -n "$O3DE_PKG" ] && [ -f "$O3DE_PKG/setup.py" ]; then
|
||||||
sed -i 's/-m pip install -e /-m pip install /g' "$LYPYTHON"
|
( cd "$O3DE_PKG" && python3 setup.py sdist )
|
||||||
|
test -f "$O3DE_PKG/dist/o3de-1.0.0.tar.gz"
|
||||||
else
|
else
|
||||||
echo "::warning:: 'pip install -e' not found in LYPython.cmake"
|
echo "scripts/o3de/setup.py not found; O3DE layout may have changed" >&2
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
# Belt-and-suspenders: LYPython.cmake also installs o3de (editable) during a
|
||||||
|
# project-build CMake configure. Patch every copy to non-editable.
|
||||||
|
find "${FLATPAK_DEST}/opt/O3DE" -path '*/cmake/LYPython.cmake' | while read -r LYPYTHON; do
|
||||||
|
grep -qF -- '-m pip install -e ' "$LYPYTHON" && \
|
||||||
|
sed -i 's/-m pip install -e /-m pip install /g' "$LYPYTHON" || true
|
||||||
|
done
|
||||||
# Launcher + AppStream + desktop entry under the Flatpak app-id.
|
# Launcher + AppStream + desktop entry under the Flatpak app-id.
|
||||||
- install -Dm755 o3de-wrapper.sh "${FLATPAK_DEST}/bin/o3de-wrapper.sh"
|
- install -Dm755 o3de-wrapper.sh "${FLATPAK_DEST}/bin/o3de-wrapper.sh"
|
||||||
- install -Dm644 org.o3de.O3DE.desktop "${FLATPAK_DEST}/share/applications/org.o3de.O3DE.desktop"
|
- install -Dm644 org.o3de.O3DE.desktop "${FLATPAK_DEST}/share/applications/org.o3de.O3DE.desktop"
|
||||||
|
|||||||
+30
-12
@@ -29,20 +29,38 @@ tar -C data -xf data.tar.*
|
|||||||
mkdir -p "$DEST/opt"
|
mkdir -p "$DEST/opt"
|
||||||
cp -a data/opt/. "$DEST/opt/"
|
cp -a data/opt/. "$DEST/opt/"
|
||||||
|
|
||||||
echo ">> patching the editable pip install (read-only /app workaround)"
|
echo ">> pre-building the o3de sdist (read-only /app workaround)"
|
||||||
# On first launch O3DE sets up a per-user Python venv in ~/.o3de and pip-installs
|
# On first launch O3DE builds a per-user Python venv in ~/.o3de and installs its
|
||||||
# its 'o3de' CLI with 'pip install -e' (editable). Editable mode writes an
|
# 'o3de' CLI from <engine>/scripts/o3de. By default python/get_python.sh does an
|
||||||
# egg-info next to the source under /app, which is read-only in a Flatpak, so it
|
# *editable* install ('pip install -e'), which writes an egg-info next to the
|
||||||
# fails. Force a normal (non-editable) install instead: pip builds in a temp dir
|
# source under read-only /app and fails. get_python.sh has a built-in escape hatch
|
||||||
# and installs into the writable ~/.o3de venv. (Nothing Python-related needs to
|
# for immutable installs (the Snap path): if a prebuilt sdist exists at
|
||||||
# be baked into the image; it all lives per-user under ~/.o3de.)
|
# scripts/o3de/dist/o3de-1.0.0.tar.gz it installs THAT (non-editable) instead. So
|
||||||
LYPYTHON=$(find "$DEST/opt/O3DE" -path '*/cmake/LYPython.cmake' | head -n1)
|
# we build that tarball here, while the tree is still writable. At runtime the
|
||||||
if [ -n "$LYPYTHON" ] && grep -qF -- '-m pip install -e ' "$LYPYTHON"; then
|
# tarball is copied into the writable ~/.o3de venv - nothing is written to /app.
|
||||||
|
O3DE_PKG=$(find "$DEST/opt/O3DE" -maxdepth 3 -type d -path '*/scripts/o3de' | head -n1)
|
||||||
|
if [ -n "$O3DE_PKG" ] && [ -f "$O3DE_PKG/setup.py" ]; then
|
||||||
|
( cd "$O3DE_PKG" && python3 setup.py sdist )
|
||||||
|
if [ -f "$O3DE_PKG/dist/o3de-1.0.0.tar.gz" ]; then
|
||||||
|
echo " built: $O3DE_PKG/dist/o3de-1.0.0.tar.gz"
|
||||||
|
else
|
||||||
|
echo " ERROR: sdist did not produce o3de-1.0.0.tar.gz" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ERROR: scripts/o3de/setup.py not found; O3DE layout may have changed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Belt-and-suspenders for the project-build path: LYPython.cmake also installs the
|
||||||
|
# o3de package (editable) during a CMake configure when you build a project. Patch
|
||||||
|
# every copy to a non-editable install so that write doesn't hit read-only /app.
|
||||||
|
find "$DEST/opt/O3DE" -path '*/cmake/LYPython.cmake' | while read -r LYPYTHON; do
|
||||||
|
if grep -qF -- '-m pip install -e ' "$LYPYTHON"; then
|
||||||
sed -i 's/-m pip install -e /-m pip install /g' "$LYPYTHON"
|
sed -i 's/-m pip install -e /-m pip install /g' "$LYPYTHON"
|
||||||
echo " patched: $LYPYTHON"
|
echo " patched: $LYPYTHON"
|
||||||
else
|
fi
|
||||||
echo " WARNING: 'pip install -e' not found in LYPython.cmake; O3DE layout may have changed" >&2
|
done
|
||||||
fi
|
|
||||||
|
|
||||||
echo ">> installing launcher + metadata"
|
echo ">> installing launcher + metadata"
|
||||||
install -Dm755 o3de-wrapper.sh "$DEST/bin/o3de-wrapper.sh"
|
install -Dm755 o3de-wrapper.sh "$DEST/bin/o3de-wrapper.sh"
|
||||||
|
|||||||
Reference in New Issue
Block a user