fix(webtop,webtop_kde,claude_desktop): give Selkies XDG_RUNTIME_DIR and the right websocket port (#2921)

* fix(webtop,webtop_kde,claude_desktop): give Selkies XDG_RUNTIME_DIR and the right WS port

Selkies panics with `RuntimeDirNotSet` right after its data websocket server
comes up, and binds that server on 8081 while nginx proxies 8082.

Upstream relies on s6-rc ordering: init-selkies-config publishes
XDG_RUNTIME_DIR and CUSTOM_WS_PORT into the s6 envdir, and svc-selkies is
started afterwards. ha_entrypoint.sh replaces s6-overlay and launches every
s6-rc.d run script in parallel with no dependency graph, so a longrun can
snapshot the envdir (with-contenv reads it once, at exec) before the oneshot
has written to it. Port 8081 in the report is the proof: that is selkies' own
default, not the 8082 init-selkies-config writes near the end of its run.

Only the webtop images carry PIXELFLUX_WAYLAND=true, which is why the missing
runtime dir reaches a Wayland socket bind there and not on claude_desktop.

20-folders.sh now exports both variables inside each run script, where no
start ordering can lose them, and 90-ingress.sh derives the nginx CWS
substitution from the same value.

Also correct the base image's $HOME/.XDG override where that write happens
rather than appending a correction to init-selkies-config: the oneshot
tolerance block appends `exit 0`, so on every boot after the first the
appended correction sat past it and never ran.

81-microsoft_edge.sh (webtop only), addressing the open review comments on
PR #2920:
- apt-get/dpkg failures no longer abort cont-init; each is guarded, warns and
  exits 0, and apt acquisition is bounded so a stalled mirror cannot hang
  start-up
- quote ${EDGE_VERSION+x}
- gate the wrapper swap on /helpers/microsoft-edge-stable still existing, so a
  second run cannot move the installed wrapper aside with nothing to replace it

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(claude_desktop): inject the run-script exports after the data_location rewrite

Two findings from an independent review of the previous commit.

The `s|$DEFAULT_LOCATION|$LOCATION|g` pass over the s6-rc.d run scripts is a
blind textual substitution, and it ran after the export injection. A
data_location *under* the image default -- /config/data_kde/foo on an image
whose default is /config/data_kde -- therefore rewrote the freshly injected
`export HOME=/config/data_kde/foo` into `.../foo/foo`. Injecting after the
rewrite instead of before removes the double substitution.

Quote the injected values so a location containing whitespace cannot produce a
broken run script. XDG_CACHE_HOME stays unquoted: the loop greps for it as its
idempotence marker.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(webtop,webtop_kde,claude_desktop): address review feedback on the Selkies env fix

Refresh the injected run-script exports on every boot (codex, coderabbit).
They were guarded by the XDG_CACHE_HOME sentinel, so they were written once and
then survived in the writable layer. Raising PUID left every service exporting a
/run/user/<old-uid> the remapped abc user cannot use, and clearing a custom
CUSTOM_WS_PORT left Selkies on the old port while 90-ingress.sh moved nginx back
to 8082 -- with the envdir written at the same boot disagreeing with both. The
exports now sit in a marked managed block that is stripped and rewritten each
boot, mirroring how the ~/.bashrc block in the same script already works. The
sweep also removes the bare exports earlier versions wrote, so an upgraded
container cannot end up with two sets. No upstream run script in these images
sets any of the five, so it only ever removes our own.

Validate CUSTOM_WS_PORT once, where it enters (coderabbit). It is interpolated
into generated shell and into a sed replacement, so a non-numeric or
out-of-range value could corrupt a run script or the nginx config. 90-ingress.sh
repeats the check rather than trusting the envdir, so a malformed value cannot
reach the nginx config if 20-folders.sh did not get that far.

Download Edge to an mktemp path instead of a fixed /tmp/edge.deb (coderabbit).
This runs as root against a world-writable tmpfs, where a predictable name can
be pre-created as a symlink to redirect the download or swap what is installed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Alexandre
2026-07-28 17:05:07 +02:00
committed by GitHub
parent bfe91cbeac
commit dc1044e453
9 changed files with 131 additions and 37 deletions

View File

@@ -1,3 +1,7 @@
## 1.36.4 (28-07-2026)
- Fix Selkies dying with a Rust `RuntimeDirNotSet` unwrap panic just after `Data WebSocket Server listening on port 8081`, and the data websocket then being proxied to the wrong port. Upstream relies on s6-rc ordering: `init-selkies-config` publishes `XDG_RUNTIME_DIR` and `CUSTOM_WS_PORT` into the s6 envdir and `svc-selkies` starts afterwards. The add-on entrypoint replaces s6-overlay and starts every `s6-rc.d` run script in parallel with no dependency graph, so Selkies can snapshot the envdir before that oneshot has written to it -- which is why it bound port 8081 (its own default) instead of the 8082 nginx proxies to, and why its Wayland compositor found no runtime directory to bind a socket in. `20-folders.sh` now exports both variables inside each run script, where no start ordering can lose them, and corrects the base image's `$HOME/.XDG` override where that write happens instead of appending a correction after the `exit 0` that the oneshot-tolerance block adds -- which meant the correction never ran on any boot after the first.
## 1.36.3 (28-07-2026)
- Make the Selkies startup scripts add-on agnostic so `webtop` and `webtop_kde` can share them by symlink instead of carrying their own drifted copies. `20-folders.sh` now derives its default data location from the home directory the Dockerfile baked into the `abc` user (`getent passwd abc`) rather than hardcoding `/data/data`, and the `permission_mode: bypass` root guard is skipped on add-ons that do not declare that option. `80-configuration.sh` falls back to `pip` when the image does not ship `uv`. No behaviour change for Claude Desktop: `getent passwd abc` returns `/data/data`, which is exactly the value that was hardcoded before.

View File

@@ -122,5 +122,5 @@ slug: claude_desktop
tmpfs: true
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "1.36.3"
version: "1.36.4"
video: true

View File

@@ -92,16 +92,35 @@ XDG_RUNTIME_DIR="/run/user/$PUID"
mkdir -p "$XDG_RUNTIME_DIR"
chmod 700 "$XDG_RUNTIME_DIR"
for file in /etc/s6-overlay/s6-rc.d/*/run; do
if [ "$(sed -n '1{/bash/p};q' "$file")" ] && ! grep -q '^export XDG_CACHE_HOME=/tmp/cache$' "$file"; then
sed -i "1a export HOME=$LOCATION" "$file"
sed -i "1a export FM_HOME=$LOCATION" "$file"
sed -i "1a export XDG_CACHE_HOME=/tmp/cache" "$file"
fi
done
# Must agree with the CWS substitution in 90-ingress.sh: nginx proxies the Selkies data
# websocket to this port, and Selkies only listens on it if CUSTOM_WS_PORT reaches its process.
# Validated here, once, because the value goes on to be interpolated into generated shell and
# into a sed replacement in 90-ingress.sh, both of which take the normalised value back out of
# the envdir written below.
SELKIES_WS_PORT="${CUSTOM_WS_PORT:-8082}"
if ! [[ "$SELKIES_WS_PORT" =~ ^[0-9]+$ ]] || [ "$SELKIES_WS_PORT" -lt 1 ] || [ "$SELKIES_WS_PORT" -gt 65535 ]; then
bashio::log.warning "CUSTOM_WS_PORT '${CUSTOM_WS_PORT:-}' is not a valid port number; using 8082"
SELKIES_WS_PORT=8082
fi
# Upstream relies on s6-rc ordering: init-selkies-config publishes XDG_RUNTIME_DIR and
# CUSTOM_WS_PORT into the s6 envdir, and svc-selkies is started afterwards. The add-on
# entrypoint replaces s6-overlay and launches every s6-rc.d run script in parallel, with no
# dependency graph, so a longrun can snapshot the envdir (with-contenv reads it once, at exec)
# before the oneshot has written to it. Selkies is where that shows: it comes up with
# CUSTOM_WS_PORT unset and binds its data websocket on the 8081 default while nginx proxies
# 8082, and on the PIXELFLUX_WAYLAND images it reaches the compositor with no XDG_RUNTIME_DIR
# and panics with `RuntimeDirNotSet` binding the Wayland socket.
#
# Exporting both inside the run scripts puts them in each process's own environment, where no
# start ordering can lose them, and keeps every service agreeing on one runtime dir -- svc-de
# otherwise waits forever on a Wayland socket under a directory Selkies never used.
# Rewrite the home path baked into the image to the user-chosen one. No-op when data_location
# is left at its default.
# is left at its default. Runs before the exports below are injected, not after: this is a
# blind textual substitution, so a location *under* the image default (data_location
# /config/data_kde/foo against a /config/data_kde image) would otherwise rewrite the freshly
# injected "export HOME=/config/data_kde/foo" into ".../foo/foo".
if [ "$LOCATION" != "$DEFAULT_LOCATION" ]; then
for folders in /defaults /etc/cont-init.d /etc/services.d /etc/s6-overlay/s6-rc.d; do
if [ -d "$folders" ]; then
@@ -110,12 +129,38 @@ if [ "$LOCATION" != "$DEFAULT_LOCATION" ]; then
done
fi
# Re-derived on every boot rather than injected once behind a marker, for the same reason the
# ~/.bashrc block below is: the run scripts live in the writable layer and survive a restart, so
# a write-once injection pins whatever PUID and CUSTOM_WS_PORT were in force the first time.
# Raising PUID would leave every service exporting a /run/user/<old-uid> the remapped abc user
# cannot use, and clearing a custom CUSTOM_WS_PORT would leave Selkies on the old port while
# 90-ingress.sh moved nginx back to 8082. Strip whatever a previous boot left -- the marked
# block, or the bare exports earlier versions wrote -- then write the current values. No
# upstream run script in these images sets any of these five, so the bare-line sweep only ever
# removes our own.
ENV_BLOCK_BEGIN="# --- BEGIN ADDON ENV (managed) ---"
ENV_BLOCK_END="# --- END ADDON ENV (managed) ---"
for file in /etc/s6-overlay/s6-rc.d/*/run; do
[ -n "$(sed -n '1{/bash/p};q' "$file")" ] || continue
sed -i "/^${ENV_BLOCK_BEGIN}\$/,/^${ENV_BLOCK_END}\$/d" "$file"
sed -i -E '/^export (HOME|FM_HOME|XDG_CACHE_HOME|XDG_RUNTIME_DIR|CUSTOM_WS_PORT)=/d' "$file"
# Each "1a" lands at line 2 and pushes the previous one down, so this reads bottom-up.
sed -i "1a $ENV_BLOCK_END" "$file"
sed -i "1a export HOME=\"$LOCATION\"" "$file"
sed -i "1a export FM_HOME=\"$LOCATION\"" "$file"
sed -i "1a export XDG_CACHE_HOME=\"/tmp/cache\"" "$file"
sed -i "1a export XDG_RUNTIME_DIR=\"$XDG_RUNTIME_DIR\"" "$file"
sed -i "1a export CUSTOM_WS_PORT=\"$SELKIES_WS_PORT\"" "$file"
sed -i "1a $ENV_BLOCK_BEGIN" "$file"
done
sed -i "s|^\(abc:[^:]*:[^:]*:[^:]*:[^:]*:\)[^:]*|\1$LOCATION|" /etc/passwd
printf "%s" "$LOCATION" > "$S6_ENVDIR/HOME"
printf "%s" "$LOCATION" > "$S6_ENVDIR/FM_HOME"
printf "%s" "/tmp/cache" > "$S6_ENVDIR/XDG_CACHE_HOME"
printf "%s" "$XDG_RUNTIME_DIR" > "$S6_ENVDIR/XDG_RUNTIME_DIR"
printf "%s" "$SELKIES_WS_PORT" > "$S6_ENVDIR/CUSTOM_WS_PORT"
# Re-derived on every boot rather than gated on a "does it already say $LOCATION" grep: that
# guard only ever recognized the CURRENT $LOCATION, so a user who changed data_location and
# later changed it back left two stale HOME/FM_HOME exports in ~/.bashrc, with the last one
@@ -159,13 +204,16 @@ bashio::log.info "Setting ownership to $PUID:$PGID"
chown -R "${PUID}:${PGID}" "$LOCATION" /tmp/cache "$XDG_RUNTIME_DIR" /data
chmod -R 700 "$LOCATION"
# The base init-selkies-config script overrides XDG_RUNTIME_DIR to $HOME/.XDG, which lands
# on persistent storage and conflicts with the tmpfs runtime dir set above. Re-assert the
# tmpfs value at the end of that oneshot so the app and desktop agree on one valid dir.
# The base init-selkies-config script overrides XDG_RUNTIME_DIR to $HOME/.XDG, which lands on
# persistent storage and conflicts with the tmpfs runtime dir set above. Correct that write
# where it happens rather than re-asserting the value at the end of the oneshot: the tolerance
# block below appends `exit 0`, so on every boot after the first an appended correction sits
# past it and never runs.
SELKIES_CONFIG_RUN="/etc/s6-overlay/s6-rc.d/init-selkies-config/run"
if [ -f "$SELKIES_CONFIG_RUN" ]; then
# Drop the trailing correction earlier versions appended, now applied at the source.
sed -i '/^# XDG_RUNTIME_DIR override reconciled$/,+1d' "$SELKIES_CONFIG_RUN"
printf '\n# XDG_RUNTIME_DIR override reconciled\nprintf "%%s" "%s" > /run/s6/container_environment/XDG_RUNTIME_DIR\n' "$XDG_RUNTIME_DIR" >> "$SELKIES_CONFIG_RUN"
sed -i "s|^.*> */run/s6/container_environment/XDG_RUNTIME_DIR *\$|printf '%s' '$XDG_RUNTIME_DIR' > /run/s6/container_environment/XDG_RUNTIME_DIR|" "$SELKIES_CONFIG_RUN"
fi
# The Selkies desktop init oneshots do best-effort device/permission setup (mknod

View File

@@ -24,7 +24,15 @@ sed -i '/listen \[::\]/d' "${NGINX_CONFIG}"
# Adapt ports and upstream paths for Home Assistant ingress
sed -i "s|3000|$(bashio::addon.ingress_port)|g" "${NGINX_CONFIG}"
sed -i "s|SUBFOLDER|/|g" "${NGINX_CONFIG}"
sed -i "s|CWS|8082|g" "${NGINX_CONFIG}"
# Same value 20-folders.sh exports to the Selkies services; both must move together or nginx
# proxies the data websocket to a port nothing listens on. That script also normalises it into
# the s6 envdir, which this one picks up through with-contenv; the check is repeated so a
# malformed value cannot reach the nginx config if 20-folders.sh did not get that far.
CWS="${CUSTOM_WS_PORT:-8082}"
if ! [[ "$CWS" =~ ^[0-9]+$ ]] || [ "$CWS" -lt 1 ] || [ "$CWS" -gt 65535 ]; then
CWS=8082
fi
sed -i "s|CWS|${CWS}|g" "${NGINX_CONFIG}"
sed -i "s|REPLACE_HOME|${HOME:-/root}|g" "${NGINX_CONFIG}"
sed -i "s|REPLACE_DOWNLOADS_PATH|${HOME:-/config}|g" "${NGINX_CONFIG}"
sed -i '/proxy_buffering/a proxy_set_header Accept-Encoding "";' "${NGINX_CONFIG}"

View File

@@ -1,3 +1,9 @@
## 4.16-r0-ls95-7 (28-07-2026)
- Fix Selkies dying with a Rust `RuntimeDirNotSet` unwrap panic just after `Data WebSocket Server listening on port 8081`, and the data websocket then being proxied to the wrong port. Upstream relies on s6-rc ordering: `init-selkies-config` publishes `XDG_RUNTIME_DIR` and `CUSTOM_WS_PORT` into the s6 envdir and `svc-selkies` starts afterwards. The add-on entrypoint replaces s6-overlay and starts every `s6-rc.d` run script in parallel with no dependency graph, so Selkies can snapshot the envdir before that oneshot has written to it -- which is why it bound port 8081 (its own default) instead of the 8082 nginx proxies to, and why its Wayland compositor found no runtime directory to bind a socket in. `20-folders.sh` now exports both variables inside each run script, where no start ordering can lose them, and corrects the base image's `$HOME/.XDG` override where that write happens instead of appending a correction after the `exit 0` that the oneshot-tolerance block adds -- which meant the correction never ran on any boot after the first.
- Microsoft Edge install: `apt-get` and `dpkg` failures no longer abort container startup -- a transient mirror failure or a bad download now logs a warning and leaves the desktop running without Edge, and apt acquisition is bounded so a stalled mirror cannot hang start-up. The post-install wrapper swap is now gated on the helper still being present, so a second run cannot move the installed wrapper aside with nothing left to replace it.
## 4.16-r0-ls95-6 (28-07-2026)
- Share the Selkies startup scripts with the `claude_desktop` add-on by symlink (`20-folders.sh`, `21-gpu_permissions.sh`, `80-configuration.sh`, `90-ingress.sh` and the nginx includes), so the fixes made there now apply here too. This brings in: GPU render-node permissions granted before the graphical services start (fixes `libEGL warning: failed to open /dev/dri/card0: Permission denied` and the resulting "waiting for stream" hang); the s6 envdir and `XDG_RUNTIME_DIR` created up front; the cache redirected to tmpfs; `/tmp/.X11-unix` pre-created so Xorg can bind its socket as a non-root user; the `init-video` and `init-selkies-config` oneshots made non-fatal so a partially permitted device setup no longer crash-loops the add-on; and an ingress config that keeps the correct (non-SSL) nginx server block. The Microsoft Edge install moves to its own webtop-only `81-microsoft_edge.sh`, which also picks up the ownership fixup that previously ran in `20-folders.sh` before Edge was installed and so never matched anything.

View File

@@ -138,5 +138,5 @@ slug: webtop-kde
tmpfs: true
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: 4.16-r0-ls95-6
version: 4.16-r0-ls95-7
video: true

View File

@@ -1,3 +1,9 @@
## 4.16-r0-ls93.2 (28-07-2026)
- Fix Selkies dying with a Rust `RuntimeDirNotSet` unwrap panic just after `Data WebSocket Server listening on port 8081`, and the data websocket then being proxied to the wrong port. Upstream relies on s6-rc ordering: `init-selkies-config` publishes `XDG_RUNTIME_DIR` and `CUSTOM_WS_PORT` into the s6 envdir and `svc-selkies` starts afterwards. The add-on entrypoint replaces s6-overlay and starts every `s6-rc.d` run script in parallel with no dependency graph, so Selkies can snapshot the envdir before that oneshot has written to it -- which is why it bound port 8081 (its own default) instead of the 8082 nginx proxies to, and why its Wayland compositor found no runtime directory to bind a socket in. `20-folders.sh` now exports both variables inside each run script, where no start ordering can lose them, and corrects the base image's `$HOME/.XDG` override where that write happens instead of appending a correction after the `exit 0` that the oneshot-tolerance block adds -- which meant the correction never ran on any boot after the first.
- Microsoft Edge install: `apt-get` and `dpkg` failures no longer abort container startup -- a transient mirror failure or a bad download now logs a warning and leaves the desktop running without Edge, and apt acquisition is bounded so a stalled mirror cannot hang start-up. The post-install wrapper swap is now gated on the helper still being present, so a second run cannot move the installed wrapper aside with nothing left to replace it.
## 4.16-r0-ls93.1 (28-07-2026)
- Share the Selkies startup scripts with the `claude_desktop` add-on by symlink (`20-folders.sh`, `21-gpu_permissions.sh`, `80-configuration.sh`, `90-ingress.sh` and the nginx includes), so the fixes made there now apply here too. This brings in: GPU render-node permissions granted before the graphical services start (fixes `libEGL warning: failed to open /dev/dri/card0: Permission denied` and the resulting "waiting for stream" hang); the s6 envdir and `XDG_RUNTIME_DIR` created up front; the cache redirected to tmpfs; `/tmp/.X11-unix` pre-created so Xorg can bind its socket as a non-root user; the `init-video` and `init-selkies-config` oneshots made non-fatal so a partially permitted device setup no longer crash-loops the add-on; and an ingress config that keeps the correct (non-SSL) nginx server block. The Microsoft Edge install moves to its own webtop-only `81-microsoft_edge.sh`, which also picks up the ownership fixup that previously ran in `20-folders.sh` before Edge was installed and so never matched anything.

View File

@@ -143,5 +143,5 @@ slug: webtop
tmpfs: true
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "4.16-r0-ls93.1"
version: "4.16-r0-ls93.2"
video: true

View File

@@ -11,41 +11,63 @@ if ! bashio::config.true 'install_ms_edge'; then
exit 0
fi
bashio::log.info "Adding microsoft edge"
apt-get update
apt-get install --no-install-recommends -y ca-certificates
# Every step below is bounded and non-fatal. cont-init.d blocks the whole add-on, so an
# unreachable or stalled packages.microsoft.com -- or a Debian mirror having a bad day -- must
# not hang or kill startup: the desktop is useful without Edge, an add-on stuck before Selkies
# starts is not. `set -e` would turn any apt or dpkg hiccup into exactly that, so each command
# is guarded and every failure path warns and exits 0.
EDGE_DEB=""
edge_giveup() {
bashio::log.warning "$1; skipping the Microsoft Edge install"
if [ -n "$EDGE_DEB" ]; then
rm -f "$EDGE_DEB"
fi
exit 0
}
bashio::log.info "Adding microsoft edge"
# -o Acquire::*Timeout bounds the mirror handshake/transfer the same way --max-time bounds curl.
APT_TIMEOUTS=(-o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 -o Acquire::Retries=1)
apt-get "${APT_TIMEOUTS[@]}" update || edge_giveup "apt-get update failed"
apt-get "${APT_TIMEOUTS[@]}" install --no-install-recommends -y ca-certificates \
|| edge_giveup "Installing ca-certificates failed"
# Both requests are bounded and non-fatal. cont-init.d blocks the whole add-on, so an
# unreachable or stalled packages.microsoft.com must not hang or kill startup: the desktop is
# useful without Edge, an add-on stuck before Selkies starts is not.
EDGE_REPO="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable"
if [ -z ${EDGE_VERSION+x} ]; then
if [ -z "${EDGE_VERSION+x}" ]; then
EDGE_VERSION=$(curl -sL --fail --connect-timeout 15 --max-time 120 "$EDGE_REPO/" \
| awk -F'(<a href="microsoft-edge-stable_|_amd64.deb\")' '/href=/ {print $2}' | sort --version-sort | tail -1 || true)
fi
if [ -z "$EDGE_VERSION" ]; then
bashio::log.error "Could not determine the latest Microsoft Edge version; skipping the Edge install"
exit 0
edge_giveup "Could not determine the latest Microsoft Edge version"
fi
if ! curl -o /tmp/edge.deb -L --fail --connect-timeout 15 --max-time 600 \
"$EDGE_REPO/microsoft-edge-stable_${EDGE_VERSION}_amd64.deb"; then
bashio::log.error "Downloading Microsoft Edge ${EDGE_VERSION} failed; skipping the Edge install"
exit 0
fi
# mktemp rather than a fixed /tmp/edge.deb: this runs as root, and a predictable name in a
# world-writable tmpfs is something another process could pre-create as a symlink to redirect
# the download or swap what gets installed.
EDGE_DEB="$(mktemp -t microsoft-edge.XXXXXXXXXX.deb)" || edge_giveup "Could not create a temporary file"
dpkg -I /tmp/edge.deb
apt-get install --no-install-recommends -y /tmp/edge.deb
curl -o "$EDGE_DEB" -L --fail --connect-timeout 15 --max-time 600 \
"$EDGE_REPO/microsoft-edge-stable_${EDGE_VERSION}_amd64.deb" \
|| edge_giveup "Downloading Microsoft Edge ${EDGE_VERSION} failed"
dpkg -I "$EDGE_DEB" || edge_giveup "The downloaded Microsoft Edge package is not a valid .deb"
apt-get "${APT_TIMEOUTS[@]}" install --no-install-recommends -y "$EDGE_DEB" \
|| edge_giveup "Installing Microsoft Edge ${EDGE_VERSION} failed"
rm -f "$EDGE_DEB"
bashio::log.info "Applying edge docker tweaks"
if [ -f /usr/bin/microsoft-edge-stable ]; then
mv /usr/bin/microsoft-edge-stable /usr/bin/microsoft-edge-real
elif [ -f /usr/bin/microsoft-edge ]; then
mv /usr/bin/microsoft-edge /usr/bin/microsoft-edge-real
fi
# Gated on the helper still being in /helpers, which is where the image ships it and where it
# stops being once installed. Without the guard a second run would move the wrapper already
# sitting in /usr/bin aside as "-real" with nothing left to take its place, and Edge would
# stop launching.
if [ -f /helpers/microsoft-edge-stable ]; then
if [ -f /usr/bin/microsoft-edge-stable ]; then
mv /usr/bin/microsoft-edge-stable /usr/bin/microsoft-edge-real
elif [ -f /usr/bin/microsoft-edge ]; then
mv /usr/bin/microsoft-edge /usr/bin/microsoft-edge-real
fi
mv /helpers/microsoft-edge-stable /usr/bin/
fi