refactor: systemd units in services/, read with service_file like patches/

This commit is contained in:
theupriser committed 2026-09-27 15:58:41 +02:00
1 parent de70fad000
commit 0883e7a501
20 files changed
+136 -103

No files matched your search

+11 -6
View File
@@ -31,13 +31,15 @@ version="v$(grep -oP '^VERSION=\K.*' "$entry"), $(git describe --always --dirty
sed -e '1{/^#!/d}' -e '/^# Sourced by steamify.sh/d' "lib/$lib.sh" sed -e '1{/^#!/d}' -e '/^# Sourced by steamify.sh/d' "lib/$lib.sh"
echo echo
done done
# patches/ embedded: replaces lib/common.sh's patch_file, which reads # patches/ and services/ embedded: replace lib/common.sh's patch_file
# them from the checkout. # and service_raw, which read them from the checkout.
echo "# ----- patches/ -----" embed() {
echo "patch_file() {" local fn="$1" dir="$2" f
echo "# ----- $dir/ -----"
echo "$fn() {"
echo ' case "$1" in' echo ' case "$1" in'
for f in patches/*; do for f in "$dir"/*; do
[[ "$f" == patches/README.md ]] && continue [[ "$f" == "$dir/README.md" ]] && continue
grep -q '^STEAMIFY_PATCH_EOF$' "$f" && { echo "bundle: $f contains the heredoc delimiter" >&2; exit 1; } grep -q '^STEAMIFY_PATCH_EOF$' "$f" && { echo "bundle: $f contains the heredoc delimiter" >&2; exit 1; }
echo " $(basename "$f")) cat << 'STEAMIFY_PATCH_EOF'" echo " $(basename "$f")) cat << 'STEAMIFY_PATCH_EOF'"
cat "$f" cat "$f"
@@ -48,6 +50,9 @@ version="v$(grep -oP '^VERSION=\K.*' "$entry"), $(git describe --always --dirty
echo ' esac' echo ' esac'
echo "}" echo "}"
echo echo
}
embed patch_file patches
embed service_raw services
echo "# ----- steamify.sh -----" echo "# ----- steamify.sh -----"
echo "main() {" echo "main() {"
# Everything after the source loop is the entry point's body. # Everything after the source loop is the entry point's body.
+2 -2
View File
@@ -6,7 +6,7 @@
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash # curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/download/latest/steamify.sh | bash # curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/download/latest/steamify.sh | bash
# Each release also has the app (v2): steamify-app.tar.gz (ui/, steamify.sh, # Each release also has the app (v2): steamify-app.tar.gz (ui/, steamify.sh,
# lib/, patches/, the icon) and its launcher, run with: # lib/, patches/, services/, the icon) and its launcher, run with:
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash # curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash
# A push to feat/v2-ui (the app, not released yet) updates the pre-release # A push to feat/v2-ui (the app, not released yet) updates the pre-release
# v2-ui-preview instead, whose launcher gets that build: # v2-ui-preview instead, whose launcher gets that build:
@@ -36,7 +36,7 @@ jobs:
.github/tools/bundle.sh dist/steamify.sh .github/tools/bundle.sh dist/steamify.sh
# The app: sources, not the single-file build (its backend mode runs # The app: sources, not the single-file build (its backend mode runs
# steamify.sh from its own folder). # steamify.sh from its own folder).
tar -czf dist/steamify-app.tar.gz steamify.sh lib patches ui assets/steam-gaming-settings.svg LICENSE.md tar -czf dist/steamify-app.tar.gz steamify.sh lib patches services ui assets/steam-gaming-settings.svg LICENSE.md
cp ui/steamify-app.sh dist/steamify-app.sh cp ui/steamify-app.sh dist/steamify-app.sh
- name: Check - name: Check
+6
View File
@@ -19,6 +19,12 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
apply, never inline in the shell code. Read them with `patch_file apply, never inline in the shell code. Read them with `patch_file
<name>` (`lib/common.sh`, from the checkout); the bundle embeds every file <name>` (`lib/common.sh`, from the checkout); the bundle embeds every file
in `patches/` and overrides `patch_file`. Add new ones to its README. in `patches/` and overrides `patch_file`. Add new ones to its README.
- `services/` - the systemd units the scripts install (`.service`,
`.timer`, `.path`), never inline in the shell code. Read them with
`service_file <name> [KEY=value...]` (`lib/common.sh`), which fills in
`@KEY@` placeholders; the bundle embeds every file in `services/` too
(`service_raw`). Add new ones to its README. Small drop-ins for other
packages' units stay inline.
- `ui/` - the app: `steamify-ui` (PySide6; runs `steamify.sh --backend`, - `ui/` - the app: `steamify-ui` (PySide6; runs `steamify.sh --backend`,
`lib/backend.sh`) and `ui/qml/`: `Main.qml` (window, header, which screen `lib/backend.sh`) and `ui/qml/`: `Main.qml` (window, header, which screen
shows), `AppState.qml` (all state and logic, input actions), one shows), `AppState.qml` (all state and logic, input actions), one
+2 -1
View File
@@ -7,7 +7,8 @@ one per merged pull request.
## 2.5.0 - 2026-09-27 ## 2.5.0 - 2026-09-27
- **docs: README screenshot at 2.5.0 with Update notifications; AGENTS.md: retake it when rows change** - **refactor: systemd units in services/, read with service_file like patches/**
- `8a2601d` **docs: README screenshot at 2.5.0 with Update notifications; AGENTS.md: retake it when rows change**
- `572417b` **fix: Open Steamify from the notification starts the app (own scope), the same way it was last used (app or terminal)** - `572417b` **fix: Open Steamify from the notification starts the app (own scope), the same way it was last used (app or terminal)**
- `cf73229` **feat: Update notifications: a notification and tray icon when there's a new Steamify** - `cf73229` **feat: Update notifications: a notification and tray icon when there's a new Steamify**
+2 -1
View File
@@ -380,8 +380,9 @@ immediately, which can turn into a loop - see
| `lib/fremont-poweroff.sh` | Steam Machine support: the power-off fix (DKMS module from `patches/`) | | `lib/fremont-poweroff.sh` | Steam Machine support: the power-off fix (DKMS module from `patches/`) |
| `lib/update-notifier.sh` | Update notifications: the notifier from `patches/` and its user timer | | `lib/update-notifier.sh` | Update notifications: the notifier from `patches/` and its user timer |
| `lib/vram-booster.sh` | VRAM booster (`dmemcg-booster`, `plasma-foreground-booster`) | | `lib/vram-booster.sh` | VRAM booster (`dmemcg-booster`, `plasma-foreground-booster`) |
| `services/` | The systemd units the scripts install (`service_file`, `@KEY@` placeholders); see its README |
| `patches/` | Module sources and patches the scripts build or apply (`patch_file`); see its README | | `patches/` | Module sources and patches the scripts build or apply (`patch_file`); see its README |
| `.github/tools/bundle.sh` | Builds the single-file version (`dist/steamify.sh`), with `patches/` embedded | | `.github/tools/bundle.sh` | Builds the single-file version (`dist/steamify.sh`), with `patches/` and `services/` embedded |
| `.github/workflows/bundle.yml` | Builds and checks it on every push; publishes it on `main` | | `.github/workflows/bundle.yml` | Builds and checks it on every push; publishes it on `main` |
The single-file version is generated: on every push to `main`, GitHub The single-file version is generated: on every push to `main`, GitHub
+1 -13
View File
@@ -23,19 +23,7 @@ boot_enable() {
# the session into the base config; run it right away rather than rely # the session into the base config; run it right away rather than rely
# on its path unit, which could still be pending when the login manager # on its path unit, which could still be pending when the login manager
# starts. # starts.
sudo tee "$BOOT_UNIT" > /dev/null << EOF service_file "$BOOT_UNIT_NAME" SESSION_SYNC="$SESSION_SYNC" | sudo tee "$BOOT_UNIT" > /dev/null
[Unit]
Description=Steamify CachyOS: boot into the desktop instead of gaming mode
Before=display-manager.service sddm.service plasmalogin.service
[Service]
Type=oneshot
ExecStart=/usr/lib/steamos/steam-set-session plasma.desktop
ExecStart=/bin/sh -c '[ -x $SESSION_SYNC ] && exec $SESSION_SYNC || true'
[Install]
WantedBy=graphical.target
EOF
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable "$BOOT_UNIT_NAME" || { err "Enabling $BOOT_UNIT_NAME failed."; return 1; } sudo systemctl enable "$BOOT_UNIT_NAME" || { err "Enabling $BOOT_UNIT_NAME failed."; return 1; }
ok "Boots into the desktop from the next boot on (Return to Gaming Mode still works)." ok "Boots into the desktop from the next boot on (Return to Gaming Mode still works)."
+11
View File
@@ -9,6 +9,17 @@ ok() { echo -e "${c_green}[OK]${c_reset} $*"; }
# patch_file <name>: a file from patches/ (module sources, patches). The # patch_file <name>: a file from patches/ (module sources, patches). The
# single-file build replaces this with one that has them embedded. # single-file build replaces this with one that has them embedded.
patch_file() { cat "$SCRIPT_DIR/patches/$1"; } patch_file() { cat "$SCRIPT_DIR/patches/$1"; }
# service_file <name> [KEY=value...]: a systemd unit from services/, with
# each @KEY@ replaced by its value. The single-file build replaces
# service_raw like patch_file.
service_raw() { cat "$SCRIPT_DIR/services/$1"; }
service_file() {
local name="$1" kv
local -a subst=(-e '')
shift
for kv in "$@"; do subst+=(-e "s|@${kv%%=*}@|${kv#*=}|g"); done
service_raw "$name" | sed "${subst[@]}"
}
warn() { echo -e "${c_yellow}[WARN]${c_reset} $*"; } warn() { echo -e "${c_yellow}[WARN]${c_reset} $*"; }
err() { echo -e "${c_red}[ERROR] $*${c_reset}" >&2; } err() { echo -e "${c_red}[ERROR] $*${c_reset}" >&2; }
ask_yn() { ask_yn() {
+2 -21
View File
@@ -211,28 +211,9 @@ EOF
# 7. systemd path watcher + service # 7. systemd path watcher + service
info "Installing systemd path watcher so session switches take effect immediately" info "Installing systemd path watcher so session switches take effect immediately"
sudo tee /etc/systemd/system/sync-steamos-session.path > /dev/null << 'EOF' service_file sync-steamos-session.path | sudo tee /etc/systemd/system/sync-steamos-session.path > /dev/null
[Unit]
Description=Watch for steamos session changes
[Path] service_file sync-steamos-session.service | sudo tee /etc/systemd/system/sync-steamos-session.service > /dev/null
PathModified=/etc/plasmalogin.conf.d/zz-steamos-autologin.conf
[Install]
WantedBy=multi-user.target
EOF
sudo tee /etc/systemd/system/sync-steamos-session.service > /dev/null << 'EOF'
[Unit]
Description=Sync steamos session selection into plasmalogin.conf
# Session switches can come in bursts (switch + autologin reset); never
# let systemd's start rate limit silently stop the bridge.
StartLimitIntervalSec=0
[Service]
Type=oneshot
ExecStart=/usr/local/bin/sync-steamos-session.sh
EOF
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable --now sync-steamos-session.path sudo systemctl enable --now sync-steamos-session.path
+1 -17
View File
@@ -16,23 +16,7 @@ steam_enable() {
systemd_user_dir="$HOME/.config/systemd/user" systemd_user_dir="$HOME/.config/systemd/user"
mkdir -p "$systemd_user_dir" mkdir -p "$systemd_user_dir"
cat << 'EOF' > "$systemd_user_dir/steam-desktop-autostart.service" service_file steam-desktop-autostart.service > "$systemd_user_dir/steam-desktop-autostart.service"
[Unit]
Description=Steam Background Autostart for Virtual Keyboard
After=graphical-session.target
PartOf=graphical-session.target
[Service]
Type=simple
# Only in the Plasma desktop session; gamescope-session starts its own Steam.
ExecCondition=/bin/sh -c '[ "$XDG_CURRENT_DESKTOP" = KDE ]'
ExecStart=/usr/bin/steam -silent
Restart=on-failure
RestartSec=5
[Install]
WantedBy=graphical-session.target
EOF
# Clean out old .desktop shortcut so they don't fight # Clean out old .desktop shortcut so they don't fight
rm -f "$HOME/.config/autostart/steam.desktop" rm -f "$HOME/.config/autostart/steam.desktop"
+1 -13
View File
@@ -312,19 +312,7 @@ echo "Installing missing kernel headers: ${missing[*]}"
exec pacman -S --needed --noconfirm "${missing[@]}" exec pacman -S --needed --noconfirm "${missing[@]}"
EOF EOF
sudo chmod 755 "$HEADERS_SCRIPT" sudo chmod 755 "$HEADERS_SCRIPT"
sudo tee "$HEADERS_UNIT" > /dev/null << EOF service_file ensure-kernel-headers.service HEADERS_SCRIPT="$HEADERS_SCRIPT" | sudo tee "$HEADERS_UNIT" > /dev/null
[Unit]
Description=Install missing kernel headers so DKMS builds leds-valve for every kernel
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=$HEADERS_SCRIPT
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable ensure-kernel-headers.service sudo systemctl enable ensure-kernel-headers.service
} }
+2 -28
View File
@@ -39,34 +39,8 @@ notify_enable() {
patch_file steamify-notifier.py | install_executable "$NOTIFY_SCRIPT" 755 || patch_file steamify-notifier.py | install_executable "$NOTIFY_SCRIPT" 755 ||
{ err "Couldn't write $NOTIFY_SCRIPT."; return 1; } { err "Couldn't write $NOTIFY_SCRIPT."; return 1; }
mkdir -p "$NOTIFY_UNITS" mkdir -p "$NOTIFY_UNITS"
# PartOf the graphical session: the tray icon goes with a logout. The service_file "$NOTIFY_NAME.service" NOTIFY_SCRIPT="$NOTIFY_SCRIPT" > "$NOTIFY_UNITS/$NOTIFY_NAME.service"
# short wait at login lets the tray come up first (in the script: an service_file "$NOTIFY_NAME.timer" > "$NOTIFY_UNITS/$NOTIFY_NAME.timer"
# ExecStartPre sleep runs into the start timeout).
cat > "$NOTIFY_UNITS/$NOTIFY_NAME.service" << EOF
[Unit]
Description=Steamify: check for a new release
After=plasma-workspace.target
PartOf=graphical-session.target
[Service]
Type=simple
ExecStart=$NOTIFY_SCRIPT --delay 60
[Install]
WantedBy=plasma-workspace.target
EOF
cat > "$NOTIFY_UNITS/$NOTIFY_NAME.timer" << EOF
[Unit]
Description=Steamify: check for a new release daily
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl --user daemon-reload systemctl --user daemon-reload
systemctl --user enable "$NOTIFY_NAME.service" >/dev/null 2>&1 && systemctl --user enable "$NOTIFY_NAME.service" >/dev/null 2>&1 &&
systemctl --user enable --now "$NOTIFY_NAME.timer" >/dev/null 2>&1 || systemctl --user enable --now "$NOTIFY_NAME.timer" >/dev/null 2>&1 ||
+16
View File
@@ -0,0 +1,16 @@
# Services
The systemd units Steamify installs, kept out of the shell code so they can
be read and reviewed on their own. `lib/*.sh` reads them with `service_file
<name> [KEY=value...]`, which fills in `@KEY@` placeholders (paths the
scripts define); the single-file build (`.github/tools/bundle.sh`) embeds
them. Small drop-ins that only add a line or two to someone else's unit stay
in the shell code.
| File | Used by | What |
|---|---|---|
| `steamify-boot-desktop.service` | `lib/boot-session.sh` (Boot into: desktop) | Sets the Plasma session before the login manager starts |
| `sync-steamos-session.path`, `.service` | `lib/login-manager.sh` (SteamOS conversion, plasma-login-manager) | The sync bridge: copies Steam's session choice into `plasmalogin.conf` |
| `steam-desktop-autostart.service` | `lib/steam-desktop.sh` (SteamOS conversion) | Steam in the background on the Plasma desktop (user unit) |
| `ensure-kernel-headers.service` | `lib/steam-machine.sh` (Steam Machine support) | Installs missing kernel headers at boot, so DKMS builds for every kernel |
| `steamify-update-check.service`, `.timer` | `lib/update-notifier.sh` (Update notifications) | The daily and login check for a new release (user units) |
+11
View File
@@ -0,0 +1,11 @@
[Unit]
Description=Install missing kernel headers so DKMS builds leds-valve for every kernel
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=@HEADERS_SCRIPT@
[Install]
WantedBy=multi-user.target
+15
View File
@@ -0,0 +1,15 @@
[Unit]
Description=Steam Background Autostart for Virtual Keyboard
After=graphical-session.target
PartOf=graphical-session.target
[Service]
Type=simple
# Only in the Plasma desktop session; gamescope-session starts its own Steam.
ExecCondition=/bin/sh -c '[ "$XDG_CURRENT_DESKTOP" = KDE ]'
ExecStart=/usr/bin/steam -silent
Restart=on-failure
RestartSec=5
[Install]
WantedBy=graphical-session.target
+11
View File
@@ -0,0 +1,11 @@
[Unit]
Description=Steamify CachyOS: boot into the desktop instead of gaming mode
Before=display-manager.service sddm.service plasmalogin.service
[Service]
Type=oneshot
ExecStart=/usr/lib/steamos/steam-set-session plasma.desktop
ExecStart=/bin/sh -c '[ -x @SESSION_SYNC@ ] && exec @SESSION_SYNC@ || true'
[Install]
WantedBy=graphical.target
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Steamify: check for a new release
After=plasma-workspace.target
# The tray icon goes with a logout.
PartOf=graphical-session.target
[Service]
Type=simple
# The short wait at login lets the tray come up first (in the script: an
# ExecStartPre sleep runs into the start timeout).
ExecStart=@NOTIFY_SCRIPT@ --delay 60
[Install]
WantedBy=plasma-workspace.target
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Steamify: check for a new release daily
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
+8
View File
@@ -0,0 +1,8 @@
[Unit]
Description=Watch for steamos session changes
[Path]
PathModified=/etc/plasmalogin.conf.d/zz-steamos-autologin.conf
[Install]
WantedBy=multi-user.target
+9
View File
@@ -0,0 +1,9 @@
[Unit]
Description=Sync steamos session selection into plasmalogin.conf
# Session switches can come in bursts (switch + autologin reset); never
# let systemd's start rate limit silently stop the bridge.
StartLimitIntervalSec=0
[Service]
Type=oneshot
ExecStart=/usr/local/bin/sync-steamos-session.sh
+1 -1
View File
@@ -2,7 +2,7 @@
# Steamify CachyOS - get and start the app (v2), for `curl | bash`: # Steamify CachyOS - get and start the app (v2), for `curl | bash`:
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash # curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash
# Downloads the newest release's app package (steamify-app.tar.gz: the app, # Downloads the newest release's app package (steamify-app.tar.gz: the app,
# steamify.sh, lib/ and patches/) to ~/.local/share/steamify/app, installs PySide6 if # steamify.sh, lib/, patches/ and services/) to ~/.local/share/steamify/app, installs PySide6 if
# it's missing, adds a launcher entry, and starts the app. # it's missing, adds a launcher entry, and starts the app.
# STEAMIFY_RELEASE=<tag> takes that release instead of the newest (the # STEAMIFY_RELEASE=<tag> takes that release instead of the newest (the
# v2-ui-preview build's copy of this script sets it). STEAMIFY_BRANCH=<branch> # v2-ui-preview build's copy of this script sets it). STEAMIFY_BRANCH=<branch>