Skip to content

Commit 421e3ff

Browse files
committed
fix: replace sysext with installation in /opt
1 parent 8b1e273 commit 421e3ff

3 files changed

Lines changed: 98 additions & 87 deletions

File tree

.github/workflows/release.yaml

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ jobs:
5252
# or dropped entirely.
5353
patchelf --remove-needed libunwind.so.1 ./target/release/moonshine
5454
55+
- name: Bake version into install script
56+
run: |
57+
sed -i 's/VERSION="__VERSION__"/VERSION="${{ steps.tag.outputs.tag }}"/' ./dist/moonshine-install.sh
58+
5559
# Portable tarball with an FHS-ish layout (bin/, lib/, share/).
5660
- name: Package portable tarball
5761
run: |
@@ -62,6 +66,7 @@ jobs:
6266
cp ./dist/start-moonshine.sh ./dist/60-moonshine.rules ./dist/50-moonshine-inhibit-sleep.rules \
6367
./dist/moonshine-modules.conf ./dist/moonshine-sysusers.conf \
6468
./dist/moonshine@.service ./dist/VkLayer_moonshine_wsi.json "$STAGE/share/moonshine/"
69+
cp ./dist/moonshine-install.sh "$STAGE/bin/"
6570
cp README.md LICENSE "$STAGE/"
6671
tar --zstd -cf "./moonshine-${{ steps.tag.outputs.tag }}-linux-amd64.tar.zst" \
6772
-C "$RUNNER_TEMP" moonshine
@@ -80,37 +85,6 @@ jobs:
8085
nfpm package --config nfpm.yaml --packager "$fmt" --target .
8186
done
8287
83-
- name: Build sysext squashfs image
84-
run: |
85-
sudo apt-get install -y squashfs-tools
86-
S="$RUNNER_TEMP/sysext"
87-
mkdir -p "$S/usr/bin" \
88-
"$S/usr/lib/moonshine/vulkan-layers" \
89-
"$S/usr/lib/udev/rules.d" \
90-
"$S/usr/lib/modules-load.d" \
91-
"$S/usr/lib/systemd/system" \
92-
"$S/usr/lib/sysusers.d" \
93-
"$S/usr/lib/extension-release.d" \
94-
"$S/usr/share/vulkan/implicit_layer.d" \
95-
"$S/usr/share/polkit-1/rules.d" \
96-
"$S/usr/share/licenses/moonshine"
97-
cp ./target/release/moonshine "$S/usr/bin/"
98-
cp ./target/release/libmoonshine_wsi.so "$S/usr/lib/moonshine/vulkan-layers/"
99-
cp ./dist/moonshine-install.sh "$S/usr/bin/"
100-
cp ./dist/60-moonshine.rules "$S/usr/lib/udev/rules.d/"
101-
cp ./dist/moonshine-modules.conf "$S/usr/lib/modules-load.d/"
102-
cp ./dist/moonshine-sysusers.conf "$S/usr/lib/sysusers.d/"
103-
cp ./dist/VkLayer_moonshine_wsi.json "$S/usr/share/vulkan/implicit_layer.d/"
104-
cp ./dist/50-moonshine-inhibit-sleep.rules "$S/usr/share/polkit-1/rules.d/"
105-
cp ./LICENSE "$S/usr/share/licenses/moonshine/"
106-
echo "ID=_any" > "$S/usr/lib/extension-release.d/extension-release.moonshine"
107-
mksquashfs "$S" "moonshine-${{ steps.tag.outputs.tag }}-x86_64.raw" \
108-
-all-root -noappend -comp zstd
109-
110-
- name: Bake version into install script
111-
run: |
112-
sed -i 's/VERSION="__VERSION__"/VERSION="${{ steps.tag.outputs.tag }}"/' ./dist/moonshine-install.sh
113-
11488
- name: Release
11589
uses: softprops/action-gh-release@v2
11690
if: steps.tag.outputs.tag != ''
@@ -120,5 +94,4 @@ jobs:
12094
./moonshine-${{ steps.tag.outputs.tag }}-linux-amd64.tar.zst
12195
./moonshine_*.deb
12296
./moonshine-*.rpm
123-
./moonshine-${{ steps.tag.outputs.tag }}-x86_64.raw
12497
./dist/moonshine-install.sh

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Your keyboard, mouse, and controller inputs are sent back to the host so you can
1616

1717
## Requirements
1818

19-
1. **Linux only**. Available as `.deb`, `.rpm`, Nix, AUR, and systemd-sysext for Atomic distros (Bazzite, Silverblue, SteamOS). Tested on Arch Linux, but reported to work on other distributions too.
19+
1. **Linux only**. Available as `.deb`, `.rpm`, Nix, AUR, and an install script for Atomic distros (Bazzite, Silverblue, SteamOS). Tested on Arch Linux, but reported to work on other distributions too.
2020
1. **systemd**. Required for launching and managing application processes. Almost all modern Linux distributions include it by default.
2121
1. **A GPU with Vulkan video encoding**. NVIDIA RTX, AMD RDNA2+, or Intel Arc.
2222
1. **Moonlight v6.0.0 or higher**. Compatibility with older versions or unofficial ports is not guaranteed.
@@ -65,6 +65,17 @@ nix profile install github:hgaiser/moonshine
6565

6666
After installing, follow the [Enable the service](#enable-the-service) steps below (the NixOS module handles this for you when enabled).
6767

68+
### Bazzite / Silverblue / Atomic distros
69+
70+
For immutable distros where `/usr` is read-only, use the installer script:
71+
72+
```sh
73+
curl -fsSL https://github.com/hgaiser/moonshine/releases/latest/download/moonshine-install.sh | bash
74+
```
75+
76+
This deploys moonshine to `/opt/moonshine/` and configuration drop-ins to `/etc/`,
77+
avoiding SELinux conflicts with overlay filesystems.
78+
6879
### Enable the service
6980

7081
These steps apply to every installation method above (the service is a systemd unit).

dist/moonshine-install.sh

Lines changed: 81 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ prompt_text() {
6868
# --- help ---
6969

7070
print_help() {
71-
echo "moonshine-install.sh — install moonshine via systemd-sysext"
72-
echo ""
73-
echo " curl -fsSL https://github.com/hgaiser/moonshine/releases/latest/download/moonshine-install.sh | bash"
71+
echo "moonshine-install.sh — install moonshine"
7472
echo ""
7573
echo "Options:"
7674
echo " --uninstall Remove moonshine completely"
@@ -103,14 +101,6 @@ if [[ "$(uname -m)" != "x86_64" ]]; then
103101
die "moonshine only supports x86_64"
104102
fi
105103

106-
if ! command -v systemd-sysext &>/dev/null; then
107-
die "systemd-sysext not found (systemd >= 248 required)"
108-
fi
109-
110-
if systemd-detect-virt --container --quiet 2>/dev/null; then
111-
die "cannot run inside a container (sysext uses overlayfs)"
112-
fi
113-
114104
if ! command -v sudo &>/dev/null; then
115105
die "sudo not found"
116106
fi
@@ -155,39 +145,22 @@ if [[ "$UNINSTALL" == "true" ]]; then
155145
sudo systemctl disable "moonshine@${USER}" || true
156146
fi
157147

158-
if systemd-sysext list 2>/dev/null | grep -q moonshine; then
159-
step "Unmerging sysext..."
160-
sudo systemd-sysext unmerge moonshine.raw || true
161-
fi
162-
163-
if [[ -f /var/lib/extensions/moonshine.raw ]]; then
164-
step "Removing extension image..."
165-
sudo rm -f /var/lib/extensions/moonshine.raw
166-
fi
167-
168-
if [[ -f /etc/systemd/system/moonshine@.service ]]; then
169-
step "Removing copied service unit..."
170-
sudo rm -f /etc/systemd/system/moonshine@.service
171-
sudo systemctl daemon-reload || true
172-
fi
173-
148+
step "Removing files..."
149+
sudo rm -rf /opt/moonshine
150+
sudo rm -f /etc/systemd/system/moonshine@.service
151+
sudo rm -f /etc/udev/rules.d/60-moonshine.rules
152+
sudo rm -f /etc/modules-load.d/moonshine.conf
153+
sudo rm -f /etc/sysusers.d/moonshine.conf
154+
sudo rm -f /etc/vulkan/implicit_layer.d/VkLayer_moonshine_wsi.json
155+
sudo rm -f /etc/polkit-1/rules.d/50-moonshine-inhibit-sleep.rules
156+
sudo rm -f /etc/profile.d/moonshine.sh
157+
sudo systemctl daemon-reload || true
174158
sudo udevadm control --reload || true
175159

176160
ok "moonshine uninstalled"
177161
exit 0
178162
fi
179163

180-
# --- check if sysext is already enabled ---
181-
182-
if ! systemctl is-enabled systemd-sysext.service &>/dev/null; then
183-
die "systemd-sysext.service is not enabled.
184-
Moonshine requires sysext to be active at boot to merge the extension.
185-
Enable it first (this makes /usr read-only, breaking package managers
186-
like pacman/apt/dnf while the sysext is active — understand the risk):
187-
sudo systemctl enable --now systemd-sysext.service
188-
Then run this installer again."
189-
fi
190-
191164
# --- download ---
192165

193166
VERSION="__VERSION__"
@@ -202,13 +175,19 @@ fi
202175

203176
info "Installing moonshine ${VERSION}"
204177

205-
URL="https://github.com/hgaiser/moonshine/releases/download/${VERSION}/moonshine-${VERSION}-x86_64.raw"
178+
URL="https://github.com/hgaiser/moonshine/releases/download/${VERSION}/moonshine-${VERSION}-linux-amd64.tar.zst"
206179
step "Downloading ${DIM}${URL}${RESET}"
207180
TMPFILE="$(mktemp)"
208181
curl -fsSL --retry 3 -o "$TMPFILE" "$URL"
209182

210183
SIZE=$(du -h "$TMPFILE" | cut -f1)
211184
step "Downloaded (${SIZE})"
185+
186+
step "Extracting..."
187+
TMPDIR="$(mktemp -d)"
188+
tar --zstd -xf "$TMPFILE" -C "$TMPDIR"
189+
S="$TMPDIR/moonshine"
190+
212191
echo ""
213192

214193
# --- prompts ---
@@ -251,25 +230,73 @@ if [[ -z "$HEALTHCHECK" ]]; then
251230
fi
252231

253232
# --- build and run privileged commands ---
254-
# start-moonshine.sh and moonshine@.service are not in the sysext — they're
255-
# deployed directly to /etc/systemd/system/ so they persist on the real
256-
# filesystem regardless of the sysext overlay. This ensures the service unit
257-
# is available at boot before systemd-sysext merges extensions, and avoids
258-
# permission issues with overlayfs on distros like Bazzite.
233+
234+
MOONSHINE_HOME="/opt/moonshine"
259235

260236
CMDS=(
237+
# Stop existing service
261238
"systemctl stop 'moonshine@${USER}' 2>/dev/null || true"
262-
"mkdir -p /var/lib/extensions"
263-
"mv '${TMPFILE}' /var/lib/extensions/moonshine.raw"
264-
"systemd-sysext refresh"
265-
"curl -fsSL --retry 3 -o /etc/systemd/system/start-moonshine.sh https://raw.githubusercontent.com/hgaiser/moonshine/${VERSION}/dist/start-moonshine.sh"
266-
"chmod +x /etc/systemd/system/start-moonshine.sh"
267-
"curl -fsSL --retry 3 https://raw.githubusercontent.com/hgaiser/moonshine/${VERSION}/dist/moonshine@.service | sed 's|/usr/bin/start-moonshine.sh|/etc/systemd/system/start-moonshine.sh|' > /etc/systemd/system/moonshine@.service"
239+
240+
# Create directories
241+
"mkdir -p '${MOONSHINE_HOME}/bin'"
242+
"mkdir -p /etc/udev/rules.d"
243+
"mkdir -p /etc/modules-load.d"
244+
"mkdir -p /etc/sysusers.d"
245+
"mkdir -p /etc/vulkan/implicit_layer.d"
246+
"mkdir -p /etc/polkit-1/rules.d"
247+
# Deploy moonshine binary
248+
"cp '${S}/bin/moonshine' '${MOONSHINE_HOME}/bin/moonshine'"
249+
"chmod 755 '${MOONSHINE_HOME}/bin/moonshine'"
250+
251+
# Deploy Vulkan WSI layer
252+
"mkdir -p '${MOONSHINE_HOME}/lib'"
253+
"cp '${S}/lib/moonshine/vulkan-layers/libmoonshine_wsi.so' '${MOONSHINE_HOME}/lib/libmoonshine_wsi.so'"
254+
"chmod 755 '${MOONSHINE_HOME}/lib/libmoonshine_wsi.so'"
255+
256+
# Deploy the install script itself for future upgrades/uninstall
257+
"cp '${S}/bin/moonshine-install.sh' '${MOONSHINE_HOME}/bin/moonshine-install.sh'"
258+
"chmod 755 '${MOONSHINE_HOME}/bin/moonshine-install.sh'"
259+
260+
# Deploy start script with path patched
261+
"sed 's|/usr/bin/moonshine|${MOONSHINE_HOME}/bin/moonshine|g' '${S}/share/moonshine/start-moonshine.sh' > '${MOONSHINE_HOME}/start-moonshine.sh'"
262+
"chmod 755 '${MOONSHINE_HOME}/start-moonshine.sh'"
263+
264+
# Deploy service unit with path patched
265+
"sed 's|/usr/bin/start-moonshine.sh|${MOONSHINE_HOME}/start-moonshine.sh|' '${S}/share/moonshine/moonshine@.service' > /etc/systemd/system/moonshine@.service"
266+
267+
# Deploy udev rules
268+
"cp '${S}/share/moonshine/60-moonshine.rules' /etc/udev/rules.d/60-moonshine.rules"
269+
270+
# Deploy modules-load config
271+
"cp '${S}/share/moonshine/moonshine-modules.conf' /etc/modules-load.d/moonshine.conf"
272+
273+
# Deploy sysusers config
274+
"cp '${S}/share/moonshine/moonshine-sysusers.conf' /etc/sysusers.d/moonshine.conf"
275+
276+
# Deploy Vulkan layer manifest with library_path patched
277+
"sed 's|/usr/lib/moonshine/vulkan-layers/libmoonshine_wsi.so|${MOONSHINE_HOME}/lib/libmoonshine_wsi.so|' '${S}/share/moonshine/VkLayer_moonshine_wsi.json' > /etc/vulkan/implicit_layer.d/VkLayer_moonshine_wsi.json"
278+
279+
# Deploy polkit sleep-inhibit rules
280+
"cp '${S}/share/moonshine/50-moonshine-inhibit-sleep.rules' /etc/polkit-1/rules.d/50-moonshine-inhibit-sleep.rules"
281+
282+
# Create profile.d for PATH
283+
"echo 'export PATH=\"\${PATH}:${MOONSHINE_HOME}/bin\"' > /etc/profile.d/moonshine.sh"
284+
285+
# Reload systemd
286+
"systemctl daemon-reload"
287+
288+
# Apply sysusers (creates moonshine group)
268289
"systemd-sysusers || true"
290+
291+
# Reload udev rules
269292
"udevadm control --reload || true"
270293
"udevadm trigger || true"
294+
295+
# Load kernel modules
271296
"modprobe uinput || true"
272297
"modprobe uhid || true"
298+
299+
# Reload polkit
273300
"systemctl reload-or-restart polkit.service || true"
274301
)
275302

@@ -280,11 +307,10 @@ fi
280307
if [[ "$HEALTHCHECK" == "true" ]]; then
281308
CMDS+=("echo ''")
282309
CMDS+=("echo ':: Running health check'")
283-
CMDS+=("systemd-run --quiet --wait --pipe -p 'User=${USER}' -p 'SupplementaryGroups=input' -p 'SupplementaryGroups=moonshine' -p 'DeviceAllow=/dev/uinput rw' -p 'DeviceAllow=/dev/uhid rw' -p 'DeviceAllow=char-drm rw' -p 'DeviceAllow=char-nvidia rw' -p 'DeviceAllow=char-nvidia-uvm rw' /etc/systemd/system/start-moonshine.sh healthcheck || true")
310+
CMDS+=("systemd-run --quiet --wait --pipe -p 'User=${USER}' -p 'SupplementaryGroups=input' -p 'SupplementaryGroups=moonshine' -p 'DeviceAllow=/dev/uinput rw' -p 'DeviceAllow=/dev/uhid rw' -p 'DeviceAllow=char-drm rw' -p 'DeviceAllow=char-nvidia rw' -p 'DeviceAllow=char-nvidia-uvm rw' '${MOONSHINE_HOME}/start-moonshine.sh' healthcheck || true")
284311
fi
285312

286313
if [[ "$ENABLE_ON_BOOT" == "true" ]] || [[ "$START_NOW" == "true" ]]; then
287-
CMDS+=("systemctl daemon-reload")
288314
if [[ "$ENABLE_ON_BOOT" == "true" ]] && [[ "$START_NOW" == "true" ]]; then
289315
CMDS+=("systemctl enable --now 'moonshine@${USER}'")
290316
elif [[ "$ENABLE_ON_BOOT" == "true" ]]; then
@@ -304,6 +330,9 @@ echo ""
304330

305331
sudo bash -c "$(printf '%s\n' "${CMDS[@]}")"
306332

333+
# Cleanup temp files
334+
rm -rf "$TMPDIR" "$TMPFILE"
335+
307336
ok "moonshine ${VERSION} installed"
308337

309338
if [[ "$ENABLE_ON_BOOT" != "true" ]] && [[ "$START_NOW" != "true" ]]; then
@@ -320,7 +349,5 @@ echo ""
320349
echo -e " ${DIM}status${RESET} ${BOLD}systemctl status moonshine@${USER}${RESET}"
321350
echo -e " ${DIM}config${RESET} ${BOLD}/home/${USER}/.config/moonshine/config.toml${RESET}"
322351
echo ""
323-
step "This installer is at /usr/bin/moonshine-install.sh for future upgrades or uninstall"
324-
echo ""
325352

326353
ok "Done"

0 commit comments

Comments
 (0)